Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
3"""
4import json
5import logging
6import os
7import sys
8import time
9import warnings
12LOG_FORMAT = '[%(asctime)s] %(threadName)-10s [%(levelname)-7s] %(message)s'
15class Pi3Bar(object):
16 """
17 Main thread. Manages all plugin instances.
18 Collects output of them every second and writes it as json to stdout.
20 Example:
22 .. code-block:: python
24 #! /usr/bin/env python
25 from pi3bar.app import Pi3Bar
26 from pi3bar.plugins import *
29 Pi3Bar(
30 # <- 4 spaces!
32 # ... plugins
34 log_path='~/.pi3bar.log', # change the log file path if you want
36 ).run() # run pi3bar when executing this file
37 """
38 def __init__(self, *register_plugins, **kwargs):
39 log_level = kwargs.pop('log_level', logging.WARNING)
40 try:
41 main_file = sys.modules['__main__'].__file__
42 except AttributeError:
43 default = os.path.join(os.path.expanduser('~'), '.pi3bar.log')
44 else:
45 default = os.path.join(os.path.dirname(main_file), 'status.log')
46 log_path = kwargs.pop('log_path', default)
48 handler = logging.FileHandler(log_path)
49 formatter = logging.Formatter(LOG_FORMAT)
50 handler.setFormatter(formatter)
52 warnings.simplefilter('always', DeprecationWarning)
53 logging.captureWarnings(True)
54 self.logger = logging.getLogger()
55 self.logger.addHandler(handler)
56 self.logger.setLevel(log_level)
58 self.plugins = []
60 from pi3bar.io import I3BarEventThread
61 self.event_thread = I3BarEventThread(self.on_plugin_click, self.logger)
62 self._register_plugins(register_plugins)
64 def _register_plugins(self, plugins):
65 for plugin in plugins:
66 if plugin in self.plugins:
67 self.logger.error('Plugin %s already registered!' % plugin)
68 continue
70 if plugin.instance is None:
71 instance_count = sum([1 for p in self.plugins if p.name == plugin.name])
72 plugin.instance = str(instance_count)
74 plugin.logger = self.logger
75 self.plugins.append(plugin)
76 plugin.info('Registered')
78 def collect_output(self):
79 """
80 Collect output of each plugin.
81 Return json array string.
82 """
83 output_list = []
84 plugins = self.plugins[:]
86 if not plugins:
87 from pi3bar.plugins.utils import NoPlugins, Version
88 # show version
89 plugins.append(Version())
90 # and info that plugins is empty
91 plugins.append(NoPlugins())
93 for plugin in plugins:
94 output = plugin.get_output()
95 try:
96 s = json.dumps(output)
97 except TypeError:
98 self.logger.error('TypeError in plugin %s:' % plugin, exc_info=1)
99 s = json.dumps({
100 'name': plugin.name,
101 'full_text': 'ERROR',
102 'short_text': 'E',
103 'background': '#ff0000'
104 })
105 output_list.append(s)
106 self.logger.debug('collect_output() [%s] %s' % (plugin, output))
108 return ',[%s]' % ','.join(output_list)
110 def collect_write(self):
111 """
112 Write output of all plugins to stdout.
113 """
114 from pi3bar.io import write_sys
115 write_sys(self.collect_output())
117 def on_plugin_click(self, name, instance, button, x, y):
118 """
119 Find registered plugin with given ``name`` and ``instance`` and call
120 its :meth:`pi3bar.plugins.base.Plugin.on_click` method with ``button``,
121 ``x`` and ``y``.
122 """
123 for plugin in self.plugins:
124 if plugin.name == name and plugin.instance == instance:
125 try:
126 plugin.on_click(button, x, y)
127 except:
128 # log instead of raise errors during plugin.on_click
129 self.logger.error('Exception in %s.on_click:' % plugin, exc_info=1)
130 return
131 self.logger.error('Click by unregistered plugin "%s" instance "%s"' % (name, instance))
133 def run(self):
134 """
135 Run the main loop.
136 """
137 from pi3bar.io import write_sys
139 write_sys('{ "version": 1, "click_events": true }') # header
140 write_sys('[') # start infinite json array
142 # show version
143 from pi3bar.plugins.utils import Version
144 write_sys('[%s]' % json.dumps(Version().get_output())) # first item does not start with a comma!
145 time.sleep(1)
147 self.event_thread.start()
149 for plugin in self.plugins:
150 plugin.run()
152 while self.event_thread.CONTINUE:
153 # run while event_thread is running.
154 # It only closes gracefully when i3bar is closed.
155 self.collect_write()
156 time.sleep(1)