This commit is contained in:
Daniel Pavel 2012-10-05 13:26:52 +03:00
parent cb3a42c04e
commit ecf3539ba2
7 changed files with 123 additions and 58 deletions

View File

@ -29,15 +29,15 @@ def _status_updated(watcher, icon, window):
# pass # pass
def run(): def run(config):
GObject.threads_init() GObject.threads_init()
ui.notify.start(APP_TITLE) ui.notify.init(APP_TITLE, config.notifications)
watcher = WatcherThread(ui.notify.show) watcher = WatcherThread(ui.notify.show)
watcher.start() watcher.start()
window = ui.window.create(APP_TITLE, watcher.devices[0]) window = ui.window.create(APP_TITLE, watcher.devices[0], not config.start_hidden, config.close_to_tray)
menu_actions = [('Scan all devices', watcher.full_scan), menu_actions = [('Scan all devices', watcher.full_scan),
# ('Pair new device', _pair_new_device, watcher), # ('Pair new device', _pair_new_device, watcher),
@ -53,4 +53,4 @@ def run():
Gtk.main() Gtk.main()
watcher.stop() watcher.stop()
ui.notify.stop() ui.notify.set_active(False)

View File

@ -2,35 +2,62 @@
# Optional desktop notifications. # Optional desktop notifications.
# #
import logging
try: try:
import notify2 as _notify import notify2 as _notify
available = True # assumed to be working since the import succeeded
available = True _active = False # not yet active
_app_title = None
_notifications = {} _notifications = {}
def start(app_title): def init(app_title, active=True):
"""Init the notifications system.""" """Init the notifications system."""
_notify.init(app_title) global _app_title, _active
return True _app_title = app_title
return set_active(active)
def stop(): def set_active(active=True):
"""Stop the notifications system.""" global available, _active
for n in list(_notifications.values()): if available:
try: if active:
n.close() if not _active:
except Exception: try:
# DBUS _notify.init(_app_title)
pass _active = True
_notifications.clear() except:
_notify.uninit() logging.exception("initializing desktop notifications")
available = False
else:
if _active:
for n in list(_notifications.values()):
try:
n.close()
except Exception:
logging.exception("closing open notification %s", n)
# DBUS
pass
_notifications.clear()
try:
_notify.uninit()
except:
logging.exception("stopping desktop notifications")
available = False
_active = False
return _active
def show(status_code, title, text, icon=None): def active():
return _active
def show(status_code, title, text='', icon=None):
"""Show a notification with title and text.""" """Show a notification with title and text."""
if not available: if not available or not _active:
return return
if title in _notifications: if title in _notifications:
@ -45,17 +72,20 @@ try:
icon = icon or title icon = icon or title
notification.update(title, text, title) notification.update(title, text, title)
try: try:
notification.show() if text:
notification.show()
else:
notification.close()
except Exception: except Exception:
# DBUS logging.exception("showing notification %s", notification)
pass
except ImportError: except ImportError:
import logging
logging.exception("ouch") logging.exception("ouch")
logging.warn("python-notify2 not found, desktop notifications are disabled") logging.warn("python-notify2 not found, desktop notifications are disabled")
available = False available = False
def start(app_title): pass active = False
def stop(): pass def init(app_title, active=True): return False
def active(): return False
def set_active(active=True): return False
def show(status_code, title, text, icon=None): pass def show(status_code, title, text, icon=None): pass

View File

@ -21,7 +21,10 @@ def update(window, devices):
first = controls[0].get_child() first = controls[0].get_child()
icon, label = first.get_children() icon, label = first.get_children()
rstatus = devices[0] rstatus = devices[0]
label.set_markup('<big><b>%s</b></big>\n%s' % (rstatus.name, rstatus.text)) if rstatus.text:
label.set_markup('<big><b>%s</b></big>\n%s' % (rstatus.name, rstatus.text))
else:
label.set_markup('<big><b>%s</b></big>' % rstatus.name)
for index in range(1, 1 + _MAX_DEVICES): for index in range(1, 1 + _MAX_DEVICES):
devstatus = devices.get(index) devstatus = devices.get(index)
@ -120,7 +123,7 @@ def _device_box(title):
return frame return frame
def create(title, rstatus): def create(title, rstatus, show=True, close_to_tray=False):
vbox = Gtk.VBox(homogeneous=False, spacing=4) vbox = Gtk.VBox(homogeneous=False, spacing=4)
vbox.set_border_width(4) vbox.set_border_width(4)
@ -130,6 +133,8 @@ def create(title, rstatus):
vbox.set_visible(True) vbox.set_visible(True)
window = Gtk.Window() window = Gtk.Window()
window.add(vbox)
window.set_title(title) window.set_title(title)
window.set_icon_name(title) window.set_icon_name(title)
window.set_keep_above(True) window.set_keep_above(True)
@ -137,16 +142,21 @@ def create(title, rstatus):
# window.set_skip_pager_hint(True) # window.set_skip_pager_hint(True)
window.set_deletable(False) window.set_deletable(False)
window.set_resizable(False) window.set_resizable(False)
window.set_position(Gtk.WindowPosition.MOUSE) window.set_position(Gtk.WindowPosition.MOUSE)
window.set_type_hint(Gdk.WindowTypeHint.UTILITY) window.set_type_hint(Gdk.WindowTypeHint.UTILITY)
window.set_wmclass(title, 'status-window') window.set_wmclass(title, 'status-window')
window.set_role('status-window') window.set_role('status-window')
window.connect('window-state-event', _state_event) if close_to_tray:
window.connect('delete-event', lambda w, e: toggle(None, window) or True) window.connect('window-state-event', _state_event)
window.connect('delete-event', lambda w, e: toggle(None, window) or True)
else:
window.connect('delete-event', Gtk.main_quit)
window.add(vbox) if show:
window.present() window.present()
return window return window

View File

@ -17,15 +17,15 @@ _THREAD_SLEEP = 5 # seconds
_UNIFYING_RECEIVER = 'Unifying Receiver' _UNIFYING_RECEIVER = 'Unifying Receiver'
_NO_DEVICES = 'No devices attached.' _NO_DEVICES = 'No devices attached.'
_SCANNING = 'Initializing...' _INITIALIZING = 'Initializing...'
_SCANNING = 'Scanning...'
_NO_RECEIVER = 'not found' _NO_RECEIVER = 'not found'
_FOUND_RECEIVER = 'found'
class _DevStatus(api.AttachedDeviceInfo): class _DevStatus(api.AttachedDeviceInfo):
timestamp = time.time() timestamp = time.time()
code = devices.STATUS.UNKNOWN code = devices.STATUS.UNKNOWN
text = '' text = _INITIALIZING
refresh = None refresh = None
@ -48,20 +48,23 @@ class WatcherThread(threading.Thread):
def run(self): def run(self):
self.active = True self.active = True
self._notify(0, _UNIFYING_RECEIVER, _SCANNING)
while self.active: while self.active:
if self.listener is None: if self.listener is None:
self._device_status_changed(self.rstatus, (devices.STATUS.UNKNOWN, _INITIALIZING))
self._update_status_text()
receiver = api.open() receiver = api.open()
if receiver: if receiver:
self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _FOUND_RECEIVER)) self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _SCANNING))
self._update_status_text()
for devinfo in api.list_devices(receiver): for devinfo in api.list_devices(receiver):
self._new_device(devinfo) self._new_device(devinfo)
if len(self.devices) > 1:
if len(self.devices) == 1: self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, ''))
else:
self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _NO_DEVICES)) self._device_status_changed(self.rstatus, (devices.STATUS.CONNECTED, _NO_DEVICES))
self._update_status_text() self._update_status_text()
self.listener = EventsListener(receiver, self._events_callback) self.listener = EventsListener(receiver, self._events_callback)
@ -163,20 +166,30 @@ class WatcherThread(threading.Thread):
devstatus.timestamp = time.time() devstatus.timestamp = time.time()
if type(status) == int: if type(status) == int:
devstatus.code = status status_code = status
if devstatus.code in devices.STATUS_NAME: if status_code in devices.STATUS_NAME:
devstatus.text = devices.STATUS_NAME[devstatus.code] status_text = devices.STATUS_NAME[status_code]
else: else:
devstatus.code = status[0] status_code = status[0]
if isinstance(status[1], str): if isinstance(status[1], str):
devstatus.text = status[1] status_text = status[1]
elif isinstance(status[1], dict): elif isinstance(status[1], dict):
status_text = ''
for key, value in status[1].items(): for key, value in status[1].items():
setattr(devstatus, key, value) if key == 'text':
status_text = value
else:
setattr(devstatus, key, value)
else:
status_code = devices.STATUS.UNKNOWN
status_text = ''
if old_status_code != devstatus.code: if not (status_code == 0 and old_status_code > 0):
logging.debug("%s: device '%s' status changed %s => %s: %s", time.asctime(), devstatus.name, old_status_code, devstatus.code, devstatus.text) devstatus.code = status_code
if devstatus.code // 256 != old_status_code // 256: devstatus.text = status_text
logging.debug("%s: device '%s' status update %s => %s: %s", time.asctime(), devstatus.name, old_status_code, status_code, status_text)
if status_code == 0 or old_status_code != status_code:
self._notify(devstatus.code, devstatus.name, devstatus.text) self._notify(devstatus.code, devstatus.name, devstatus.text)
return True return True

View File

@ -1,5 +1,8 @@
"""Generic Human Interface Device API. """Generic Human Interface Device API."""
"""
__author__ = "Daniel Pavel"
__license__ = "GPL"
__version__ = "0.3"
# #
# In case a future pure-Python implementation is feasible. # In case a future pure-Python implementation is feasible.

View File

@ -1 +1,5 @@
# pass #
__author__ = "Daniel Pavel"
__license__ = "GPL"
__version__ = "0.4"

View File

@ -6,18 +6,23 @@ __version__ = '0.4'
# #
# #
import logging
if __name__ == '__main__': if __name__ == '__main__':
import argparse import argparse
arg_parser = argparse.ArgumentParser() arg_parser = argparse.ArgumentParser(prog='Solaar')
arg_parser.add_argument('-v', '--verbose', action='count', default=0, arg_parser.add_argument('-v', '--verbose', action='count', default=0,
help='increase the logger verbosity') help='increase the logger verbosity')
arg_parser.add_argument('-N', '--disable-notifications', action='store_false', dest='notifications',
help='disable desktop notifications')
arg_parser.add_argument('-H', '--start-hidden', action='store_true', dest='start_hidden',
help='hide the application window on start')
arg_parser.add_argument('-t', '--close-to-tray', action='store_true',
help='closing the application window hides it')
arg_parser.add_argument('-V', '--version', action='version', version='%(prog)s ' + __version__)
args = arg_parser.parse_args() args = arg_parser.parse_args()
import logging
log_level = logging.root.level - 10 * args.verbose log_level = logging.root.level - 10 * args.verbose
logging.basicConfig(level=log_level if log_level > 0 else 1) logging.basicConfig(level=log_level if log_level > 0 else 1)
import app import app
app.run() app.run(args)