diff options
| -rw-r--r-- | CHANGELOG | 17 | ||||
| -rw-r--r-- | README.rst | 5 | ||||
| -rw-r--r--[-rwxr-xr-x] | examples/empty_class.py | 0 | ||||
| -rw-r--r-- | examples/i3bar_click_events.py | 127 | ||||
| -rw-r--r-- | examples/whoami.py | 26 | ||||
| -rwxr-xr-x | py3status/__init__.py | 41 | ||||
| -rwxr-xr-x | setup.py | 3 |
7 files changed, 215 insertions, 4 deletions
@@ -1,3 +1,20 @@ +version 1.1 (2013-12-15) +* new generic click event handler using the special module file named i3bar_click_events.py which will be forwarded any orphan click event for action. this allows you to take action on clicks made on your i3status modules ! +* add the filename as the module_name property to the Module class +* drop useless modes on examples +* new example whoami displaying the currently logged in user +* be more concrete in the i3status.conf example +* add IRC information, join us on #py3status on freenode to share your ideas or ask for help +* redirect stdout and stderr to null to suppress modules outputs, this prevents i3bar from frezzing when a user module prints something to stdout or stderr wrt issue #20 +* use base stdout for the output on i3bar +* fix first event opening line wrt issue #19 thx to @lathan +* fix delay on py3status start waiting for i3status, this caused a useless first refresh delay of py3status of i3status interval seconds, thx to @Edholm on issue #18 +* fix i3status time adjustment when format does not contain the necessary items to get an exact datetime wrt issue #18 +* fix respect user's locale for time transformation, thx to @Edholm on issue #18 +* new example module displaying Yahoo Weather forcast +* Merge pull request #15 from alethiophile/master +* Float update-time option, by Tom Hunt + version 1.0 (2013-08-30) * total rewrite (yes, again) * support for i3bar click_events, they're dispatched to user-written py3status classes based on their name/instance @@ -18,11 +18,16 @@ See the wiki for up to date documentation: Learn how to write your own modules: https://github.com/ultrabug/py3status/wiki/Write-your-own-modules +Get help or share your ideas on IRC: + channel **#py3status** on **FreeNode** + Requirements ============ You **must** set the `output_format` to `i3bar` in the general section of your i3status.conf: :: general { + colors = true + interval = 5 output_format = "i3bar" } diff --git a/examples/empty_class.py b/examples/empty_class.py index 077269c..077269c 100755..100644 --- a/examples/empty_class.py +++ b/examples/empty_class.py diff --git a/examples/i3bar_click_events.py b/examples/i3bar_click_events.py new file mode 100644 index 0000000..792965c --- /dev/null +++ b/examples/i3bar_click_events.py @@ -0,0 +1,127 @@ +from subprocess import Popen +from time import time + + +class Py3status: + """ + This module allows you to take actions based on click events made on + the i3status modules. For example, thanks to this module you could + launch the wicd GUI when clicking on the ethernet or wireless module + of your i3status output ! + + IMPORTANT: + This module file name is reserved and should NOT be changed if you + want py3status to handle your i3status modules click events ! + + The behavior described above will only work if this file is named + 'i3bar_click_events.py' ! + """ + def __init__(self): + """ + This is where you setup your actions based on your i3status config. + + Configuration: + -------------- + self.actions = { + "<module name and instance>": { + <button number>: [<function to run>, <arg1>, <arg2>], + } + } + + Variables: + ---------- + <button number> is an integer from 1 to 3: + 1 : left click + 2 : middle click + 3 : right click + + <function to run> is a python function written in this module. + The 'external_command' function is provided for convenience if + you want to call an external program from this module. + You can of course write your own functions and have them executed + on a click event at will with possible arguments <arg1>, <arg2>... + + <module name and instance> is a string made from the module + attribute 'name' and 'instance' using a space as separator: + For i3status modules, it's simply the name of the module as it + appears in the 'order' instruction of your i3status.conf. + Example: + in i3status.conf -> order += "wireless wlan0" + self.actions key -> "wireless wlan0" + + Usage example: + -------------- + - Open the wicd-gtk GUI when we LEFT click on the ethernet + module of i3status. + - Open emelfm2 to /home when we LEFT click on + the /home instance of disk_info + - Open emelfm2 to / when we LEFT click on + the / instance of disk_info + + The related i3status.conf looks like: + order += "disk /home" + order += "disk /" + order += "ethernet eth0" + + The resulting self.actions should be: + self.actions = { + "ethernet eth0": { + 1: [external_command, 'wicd-gtk', '-n'], + }, + "disk_info /home": { + 1: [external_command, 'emelfm2', '-1', '~'], + }, + "disk_info /": { + 1: [external_command, 'emelfm2', '-1', '/'], + }, + } + """ + # CONFIGURE ME PLEASE, LOVE YOU BIG TIME ! + self.actions = { + } + + def on_click(self, i3status_output_json, i3status_config, event): + """ + If an action is configured for the given i3status module 'name' and + 'instance' (if present), then we'll execute the given function with + its arguments (if present). + + Usually you SHOULD NOT modify this part of the code. + """ + button = event['button'] + key_name = '{} {}'.format( + event['name'], + event.get('instance', '') + ).strip() + if key_name in self.actions and button in self.actions[key_name]: + # get the function to run + func = self.actions[key_name][button][0] + assert hasattr(func, '__call__'), \ + 'first element of the action list must be a function' + + # run the function with the possibly given arguments + func(*self.actions[key_name][button][1:]) + + def i3bar_click_events(self, i3status_output_json, i3status_config): + """ + Cached empty output, this module doesn't show anything. + """ + response = {'full_text': '', 'name': 'i3bar_click_events'} + response['cached_until'] = time() + 3600 + return (-1, response) + + +def external_command(*cmd): + """ + This convenience function lets you call an external program at will. + + NOTE: + The stdout and stderr MUST be suppressed as shown here to avoid any + output from being caught by the i3bar (this would freeze it). + See issue #20 for more info. + """ + Popen( + cmd, + stdout=open('/dev/null', 'w'), + stderr=open('/dev/null', 'w') + ) diff --git a/examples/whoami.py b/examples/whoami.py new file mode 100644 index 0000000..f930991 --- /dev/null +++ b/examples/whoami.py @@ -0,0 +1,26 @@ +from getpass import getuser +from time import time + + +class Py3status: + """ + Simply output the currently logged in user in i3bar. + + Inspired by i3 FAQ: + https://faq.i3wm.org/question/1618/add-user-name-to-status-bar/ + """ + def whoami(self, i3status_output_json, i3status_config): + """ + We use the getpass module to get the current user. + """ + # the current user doesnt change so much, cache it good + CACHE_TIMEOUT = 600 + + # here you can change the format of the output + # default is just to show the username + username = '{}'.format(getuser()) + + # set, cache and return the output + response = {'full_text': username, 'name': 'whoami'} + response['cached_until'] = time() + CACHE_TIMEOUT + return (0, response) diff --git a/py3status/__init__.py b/py3status/__init__.py index bd8072a..cb24d39 100755 --- a/py3status/__init__.py +++ b/py3status/__init__.py @@ -34,8 +34,8 @@ def print_line(line): """ Print given line to stdout (i3bar). """ - sys.stdout.write('{}\n'.format(line)) - sys.stdout.flush() + sys.__stdout__.write('{}\n'.format(line)) + sys.__stdout__.flush() class IOPoller: @@ -252,6 +252,22 @@ class Events(Thread): if self.config['debug']: syslog(LOG_INFO, 'dispatching default event {}'.format(event)) + def i3bar_click_events_module(self): + """ + Detect the presence of the special i3bar_click_events.py module. + + When py3status detects a module named 'i3bar_click_events.py', + it will dispatch i3status click events to this module so you can catch + them and trigger any function call based on the event. + """ + for module in self.modules: + if not module.click_events: + continue + if module.module_name == 'i3bar_click_events.py': + return module + else: + return False + def run(self): """ Wait for an i3bar JSON event, then find the right module to dispatch @@ -276,6 +292,7 @@ class Events(Thread): # setup default action on button 2 press default_event = False + dispatched = False if 'button' in event and event['button'] == 2: default_event = True @@ -291,10 +308,23 @@ class Events(Thread): if 'instance' in event: if event['instance'] == obj['instance']: self.dispatch(module, obj, event) + dispatched = True break else: self.dispatch(module, obj, event) + dispatched = True break + + # fall back to i3bar_click_events.py module if present + if not dispatched: + module = self.i3bar_click_events_module() + if module: + if self.config['debug']: + syslog( + LOG_INFO, + 'dispatching event to i3bar_click_events' + ) + self.dispatch(module, obj, event) except Exception: err = sys.exc_info()[1] syslog(LOG_WARNING, 'event failed ({})'.format(err)) @@ -320,6 +350,7 @@ class Module(Thread): self.lock = lock self.methods = {} self.module_class = None + self.module_name = f_name # self.load_methods(include_path, f_name) @@ -389,7 +420,7 @@ class Module(Thread): syslog( LOG_INFO, 'module {} click_events={} has_kill={} methods={}'.format( - f_name, + self.module_name, self.click_events, self.has_kill, self.methods.keys() @@ -601,6 +632,10 @@ class Py3statusWrapper(): if self.config['debug']: syslog(LOG_INFO, 'events thread started') + # suppress modules' ouput wrt issue #20 + sys.stdout = open('/dev/null', 'w') + sys.stderr = open('/dev/null', 'w') + # load and spawn modules threads for include_path, f_name in self.list_modules(): try: @@ -5,6 +5,7 @@ py3status import os from setuptools import find_packages, setup + # Utility function to read the README file. # Used for the long_description. It's nice, because now 1) we have a top level # README file and 2) it's easier to type in the README file than to put a raw @@ -14,7 +15,7 @@ def read(fname): setup( name='py3status', - version='1.0', + version='1.1', author='Ultrabug', author_email='ultrabug@ultrabug.net', description='py3status is an extensible i3status wrapper written in python', |
