def _generate_tooltip_lines(): if not _devices_info: yield '<b>%s</b>: ' % NAME + _("no receiver") return yield '<b>%s</b>' % NAME yield '' for _ignore, number, name, status in _devices_info: if number is None: # receiver continue p = status.to_string() if p: # does it have any properties to print? yield '<b>%s</b>' % name if status: yield '\t%s' % p else: yield '\t%s <small>(' % p + _("offline") + ')</small>' else: if status: yield '<b>%s</b> <small>(' % name + _("no status") + ')</small>' else: yield '<b>%s</b> <small>(' % name + _("offline") + ')</small>' yield ''
def _create_buttons_box(): bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) bb.set_layout(Gtk.ButtonBoxStyle.END) bb._details = _new_button(None, 'dialog-information', _SMALL_BUTTON_ICON_SIZE, tooltip=_("Show Technical Details"), toggle=True, clicked=_update_details) bb.add(bb._details) bb.set_child_secondary(bb._details, True) bb.set_child_non_homogeneous(bb._details, True) def _pair_new_device(trigger): assert _find_selected_device_id() is not None receiver = _find_selected_device() assert receiver is not None assert bool(receiver) assert receiver.kind is None _action.pair(_window, receiver) bb._pair = _new_button(_("Pair new device"), 'list-add', clicked=_pair_new_device) bb.add(bb._pair) def _unpair_current_device(trigger): assert _find_selected_device_id() is not None device = _find_selected_device() assert device is not None assert bool(device) assert device.kind is not None _action.unpair(_window, device) bb._unpair = _new_button(_("Unpair"), 'edit-delete', clicked=_unpair_current_device) bb.add(bb._unpair) return bb
def _create_device_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) def _status_line(label_text): b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) b.set_size_request(10, 28) b._label = Gtk.Label(label_text) b._label.set_alignment(0, 0.5) b._label.set_size_request(170, 10) b.pack_start(b._label, False, False, 0) b._icon = Gtk.Image() b.pack_start(b._icon, False, False, 0) b._text = Gtk.Label() b._text.set_alignment(0, 0.5) b.pack_start(b._text, True, True, 0) return b p._battery = _status_line(_("Battery")) p.pack_start(p._battery, False, False, 0) p._secure = _status_line(_("Wireless Link")) p._secure._icon.set_from_icon_name('dialog-warning', _INFO_ICON_SIZE) p.pack_start(p._secure, False, False, 0) p._lux = _status_line(_("Lighting")) p.pack_start(p._lux, False, False, 0) p._config = _config_panel.create() p.pack_end(p._config, False, False, 4) return p
def create(receiver): assert receiver is not None assert receiver.kind is None assistant = Gtk.Assistant() assistant.set_title(_("%(receiver_name)s: pair new device") % {"receiver_name": receiver.name}) assistant.set_icon_name("list-add") assistant.set_size_request(400, 240) assistant.set_resizable(False) assistant.set_role("pair-device") page_intro = _create_page( assistant, Gtk.AssistantPageType.PROGRESS, _("Turn on the device you want to pair."), "preferences-desktop-peripherals", _("If the device is already turned on,\nturn if off and on again."), ) spinner = Gtk.Spinner() spinner.set_visible(True) page_intro.pack_end(spinner, True, True, 24) assistant.connect("prepare", _prepare, receiver) assistant.connect("cancel", _finish, receiver) assistant.connect("close", _finish, receiver) return assistant
def _create_sbox(s): sbox = Gtk.HBox(homogeneous=False, spacing=6) sbox.pack_start(Gtk.Label(s.label), False, False, 0) spinner = Gtk.Spinner() spinner.set_tooltip_text(_("Working") + '...') failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR) failed.set_tooltip_text(_("Read/write operation failed.")) if s.kind == _SETTING_KIND.toggle: control = _create_toggle_control(s) sbox.pack_end(control, False, False, 0) elif s.kind == _SETTING_KIND.choice: control = _create_choice_control(s) sbox.pack_end(control, False, False, 0) elif s.kind == _SETTING_KIND.range: control = _create_slider_control(s) sbox.pack_end(control, True, True, 0) else: raise NotImplemented control.set_sensitive(False) # the first read will enable it sbox.pack_end(spinner, False, False, 0) sbox.pack_end(failed, False, False, 0) if s.description: sbox.set_tooltip_text(s.description) sbox.show_all() spinner.start() # the first read will stop it failed.set_visible(False) return sbox
def show(dev, reason=None, icon=None): """Show a notification with title and text.""" if available and Notify.is_initted(): summary = dev.name # if a notification with same name is already visible, reuse it to avoid spamming n = _notifications.get(summary) if n is None: n = _notifications[summary] = Notify.Notification() if reason: message = reason elif dev.status is None: message = _("unpaired") elif bool(dev.status): message = dev.status.to_string() or _("connected") else: message = _("offline") # we need to use the filename here because the notifications daemon # is an external application that does not know about our icon sets icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None \ else _icons.icon_file(icon) n.update(summary, message, icon_file) urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL n.set_urgency(urgency) try: # if _log.isEnabledFor(_DEBUG): # _log.debug("showing %s", n) n.show() except Exception: _log.exception("showing %s", n)
def _create(): about = Gtk.AboutDialog() about.set_program_name(NAME) about.set_version(__version__) about.set_comments(_("Shows status of devices connected\nthrough wireless Logitech receivers.")) about.set_logo_icon_name(NAME.lower()) about.set_copyright('© 2012-2013 Daniel Pavel') about.set_license_type(Gtk.License.GPL_2_0) about.set_authors(('Daniel Pavel http://github.com/pwr',)) try: about.add_credit_section(_("GUI design"), ('Julien Gascard', 'Daniel Pavel')) about.add_credit_section(_("Testing"), ( 'Douglas Wagner', 'Julien Gascard', 'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html', )) about.add_credit_section(_("Logitech documentation"), ( 'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower', 'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28', )) except TypeError: # gtk3 < ~3.6.4 has incorrect gi bindings import logging logging.exception("failed to fully create the about dialog") except: # the Gtk3 version may be too old, and the function does not exist import logging logging.exception("failed to fully create the about dialog") about.set_translator_credits('\n'.join(( 'gogo (croatian)', 'Papoteur, David Geiger, Damien Lallement (français)', 'Michele Olivo (italiano)', 'Adrian Piotrowicz (polski)', 'Drovetto, JrBenito (Portuguese-BR)', 'Daniel Pavel (română)', 'Daniel Zippert, Emelie Snecker (svensk)', 'Dimitriy Ryazantcev (Russian)', ))) about.set_website('http://pwr.github.io/Solaar/') about.set_website_label(NAME) about.connect('response', lambda x, y: x.hide()) def _hide(dialog, event): dialog.hide() return True about.connect('delete-event', _hide) return about
def create_widgets(self): self.widgets = {} self.label_b = Gtk.Label(label=_('Button'), halign=Gtk.Align.START, valign=Gtk.Align.END, hexpand=True, vexpand=True) self.label_c = Gtk.Label(label=_('Count'), halign=Gtk.Align.START, valign=Gtk.Align.END, hexpand=True, vexpand=True) self.field_b = CompletionEntry(self.BUTTONS) self.field_c = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1) for field in [self.field_b, self.field_c]: field.set_halign(Gtk.Align.CENTER) field.set_valign(Gtk.Align.START) field.set_vexpand(True) self.field_b.connect('changed', self._on_update) self.field_c.connect('changed', self._on_update) self.widgets[self.label_b] = (0, 0, 1, 1) self.widgets[self.label_c] = (1, 0, 1, 1) self.widgets[self.field_b] = (0, 1, 1, 1) self.widgets[self.field_c] = (1, 1, 1, 1)
def update_device(device, item, selected_device_id, need_popup, full=False): was_online = _model.get_value(item, _COLUMN.ACTIVE) is_online = bool(device.online) _model.set_value(item, _COLUMN.ACTIVE, is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) battery_voltage = device.status.get(_K.BATTERY_VOLTAGE) if battery_level is None: _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE) _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE) else: if battery_voltage is not None: status_text = '%(battery_voltage)dmV' % {'battery_voltage': battery_voltage} elif isinstance(battery_level, _NamedInt): status_text = _(str(battery_level)) else: status_text = '%(battery_percent)d%%' % {'battery_percent': battery_level} _model.set_value(item, _COLUMN.STATUS_TEXT, status_text) charging = device.status.get(_K.BATTERY_CHARGING) icon_name = _icons.battery(battery_level, charging) _model.set_value(item, _COLUMN.STATUS_ICON, icon_name) if selected_device_id is None or need_popup: select(device.receiver.path if device.receiver else device.path, device.number) elif selected_device_id == (device.receiver.path if device.receiver else device.path, device.number): full_update = full or was_online != is_online _update_info_panel(device, full=full_update)
def create_widgets(self): self.widgets = {} self.fields = [] self.del_btns = [] self.add_btn = Gtk.Button(_('Add key'), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True, vexpand=True) self.add_btn.connect('clicked', self._clicked_add) self.widgets[self.add_btn] = (1, 0, 1, 1)
def main(): _require('pyudev', 'python3-pyudev') # handle ^C in console import signal signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, _handlesigint) args = _parse_arguments() if not args: return if args.action: # if any argument, run comandline and exit return _cli.run(args.action, args.hidraw_path) gi = _require('gi', 'python3-gi or python3-gobject') _require('gi.repository.Gtk', 'gir1.2-gtk-3.0', gi, 'Gtk', '3.0') udev_file = '42-logitech-unify-permissions.rules' if not os.path.isfile('/etc/udev/rules.d/' + udev_file) and not os.path.isfile( '/lib/udev/rules.d/' + udev_file): print(_i18n._('ERROR: '), end='') print(_i18n._('Solaar depends on a udev file that is not present'), end='.\n') print( _i18n._( 'For more information see the Solaar installation directions\n' 'at https://pwr-solaar.github.io/Solaar/installation')) try: import solaar.ui as ui import solaar.listener as listener listener.setup_scanner(ui.status_changed, ui.error_dialog) import solaar.upower as _upower if args.restart_on_wake_up: _upower.watch(listener.start_all, listener.stop_all) else: _upower.watch(lambda: listener.ping_all(True)) # main UI event loop ui.run_loop(listener.start_all, listener.stop_all, args.window != 'only', args.window != 'hide') except Exception: import sys from traceback import format_exc sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
def _create_sbox(s, device): sbox = Gtk.HBox(homogeneous=False, spacing=6) sbox.setting = s sbox.kind = s.kind if s.description: sbox.set_tooltip_text(s.description) lbl = Gtk.Label(s.label) lbl.set_alignment(0.0, 0.5) label = Gtk.EventBox() label.add(lbl) spinner = Gtk.Spinner() spinner.set_tooltip_text(_('Working') + '...') sbox._spinner = spinner failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR) failed.set_tooltip_text(_('Read/write operation failed.')) sbox._failed = failed change_icon = Gtk.Image.new_from_icon_name('changes-prevent', Gtk.IconSize.LARGE_TOOLBAR) sbox._change_icon = change_icon _change_icon(False, change_icon) change = Gtk.Button() change.set_relief(Gtk.ReliefStyle.NONE) change.add(change_icon) change.set_sensitive(True) change.connect('clicked', _change_click, sbox) if s.kind == _SETTING_KIND.toggle: control = ToggleControl(sbox) elif s.kind == _SETTING_KIND.range: control = SliderControl(sbox) elif s.kind == _SETTING_KIND.choice: control = _create_choice_control(sbox) elif s.kind == _SETTING_KIND.map_choice: control = MapChoiceControl(sbox) elif s.kind == _SETTING_KIND.multiple_toggle: control = MultipleToggleControl(sbox, change) elif s.kind == _SETTING_KIND.multiple_range: control = MultipleRangeControl(sbox, change) else: raise Exception('NotImplemented') control.set_sensitive(False) # the first read will enable it control.layout(sbox, label, change, spinner, failed) sbox._control = control sbox.show_all() spinner.start() # the first read will stop it failed.set_visible(False) return sbox
def _check_encrypted(dev): if assistant.is_drawable(): if device.status.get(_K.LINK_ENCRYPTED) is False: hbox.pack_start(Gtk.Image.new_from_icon_name('security-low', Gtk.IconSize.MENU), False, False, 0) hbox.pack_start(Gtk.Label(_('The wireless link is not encrypted') + '!'), False, False, 0) hbox.show_all() else: return True
def _check_encrypted(dev): if assistant.is_drawable(): if device.status.get(_K.LINK_ENCRYPTED) == False: hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0) hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + "!"), False, False, 0) hbox.show_all() else: return True
def _event_button_released(self, v, e): if e.button == 3: # right click m, it = v.get_selection().get_selected() wrapped = m[it][0] c = wrapped.component parent_it = m.iter_parent(it) parent_c = m[parent_it][0].component if wrapped.level > 0 else None menu = Gtk.Menu() can_wrap = wrapped.editable and wrapped.component is not None and wrapped.level >= 2 can_delete = wrapped.editable and not isinstance(parent_c, _DIV.Not) and c is not None and wrapped.level >= 1 can_insert = wrapped.editable and not isinstance(parent_c, _DIV.Not) and wrapped.level >= 2 can_insert_only_rule = wrapped.editable and wrapped.level == 1 can_flatten = wrapped.editable and not isinstance(parent_c, _DIV.Not) and isinstance( c, (_DIV.Rule, _DIV.And, _DIV.Or) ) and wrapped.level >= 2 and len(c.components) can_copy = wrapped.level >= 1 can_insert_root = wrapped.editable and wrapped.level == 0 for item in self.__get_insert_menus(m, it, c, can_insert, can_insert_only_rule, can_insert_root): menu.append(item) if can_flatten: menu.append(self._menu_flatten(m, it)) if can_wrap: menu.append(self._menu_wrap(m, it)) menu.append(self._menu_negate(m, it)) if menu.get_children(): menu.append(Gtk.SeparatorMenuItem(visible=True)) if can_delete: menu.append(self._menu_cut(m, it)) if can_copy and c is not None: menu.append(self._menu_copy(m, it)) if can_insert and _rule_component_clipboard is not None: p = self._menu_paste(m, it) menu.append(p) if c is None: # just a placeholder p.set_label(_('Paste here')) else: p.set_label(_('Paste above')) p2 = self._menu_paste(m, it, below=True) p2.set_label(_('Paste below')) menu.append(p2) elif can_insert_only_rule and isinstance(_rule_component_clipboard, _DIV.Rule): p = self._menu_paste(m, it) menu.append(p) if c is None: p.set_label(_('Paste rule here')) else: p.set_label(_('Paste rule above')) p2 = self._menu_paste(m, it, below=True) p2.set_label(_('Paste rule below')) menu.append(p2) elif can_insert_root and isinstance(_rule_component_clipboard, _DIV.Rule): p = self._menu_paste(m, m.iter_nth_child(it, 0)) p.set_label(_('Paste rule')) menu.append(p) if menu.get_children() and can_delete: menu.append(Gtk.SeparatorMenuItem(visible=True)) if can_delete: menu.append(self._menu_delete(m, it)) if menu.get_children(): menu.popup_at_pointer(e)
def _update_receiver_panel(receiver, panel, buttons, full=False): assert receiver devices_count = len(receiver) paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % { 'count': devices_count } if(receiver.max_devices > 0): paired_text += '\n\n<small>%s</small>' % ngettext('Up to %(max_count)s device can be paired to this receiver.', 'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices) % { 'max_count': receiver.max_devices } elif devices_count > 0: paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.') pairings = receiver.remaining_pairings(False) if ( pairings is not None and pairings >= 0 ) : paired_text += '\n<small>%s</small>' % _('This receiver has %d pairing(s) remaining.') % pairings panel._count.set_markup(paired_text) is_pairing = receiver.status.lock_open if is_pairing: panel._scanning.set_visible(True) if not panel._spinner.get_visible(): panel._spinner.start() panel._spinner.set_visible(True) else: panel._scanning.set_visible(False) if panel._spinner.get_visible(): panel._spinner.stop() panel._spinner.set_visible(False) panel.set_visible(True) # b._insecure.set_visible(False) buttons._unpair.set_visible(False) if ( receiver.may_unpair or receiver.re_pairs ) and not is_pairing and \ ( receiver.remaining_pairings() is None or receiver.remaining_pairings() != 0 ): if not receiver.re_pairs and devices_count >= receiver.max_devices: paired_devices = tuple(n for n in range(1, receiver.max_devices+1) if n in receiver) buttons._pair.set_sensitive(len(paired_devices) < receiver.max_devices) else: buttons._pair.set_sensitive(True) else: buttons._pair.set_sensitive(False) buttons._pair.set_visible(True)
def _create_menu(quit_handler): menu = Gtk.Menu() # per-device menu entries will be generated as-needed no_receiver = Gtk.MenuItem.new_with_label(_('No Logitech device found')) no_receiver.set_sensitive(False) menu.append(no_receiver) menu.append(Gtk.SeparatorMenuItem.new()) from .action import make menu.append(make('help-about', _('About %s') % NAME, _show_about_window, stock_id='help-about').create_menu_item()) menu.append(make('application-exit', _('Quit %s') % NAME, quit_handler, stock_id='application-exit').create_menu_item()) del make menu.show_all() return menu
def _create_menu(quit_handler): menu = Gtk.Menu() # per-device menu entries will be generated as-needed no_receiver = Gtk.MenuItem.new_with_label(_("No Logitech receiver found")) no_receiver.set_sensitive(False) menu.append(no_receiver) menu.append(Gtk.SeparatorMenuItem.new()) from .action import about, make menu.append(about.create_menu_item()) menu.append(make('application-exit', _("Quit"), quit_handler, stock_id=Gtk.STOCK_QUIT).create_menu_item()) del about, make menu.show_all() return menu
def _generate_description_lines(): if not _devices_info: yield _('no receiver') return for _ignore, number, name, status in _devices_info: if number is None: # receiver continue p = status.to_string() if p: # does it have any properties to print? yield '<b>%s</b>' % name if status: yield '\t%s' % p else: yield '\t%s <small>(' % p + _('offline') + ')</small>' else: if status: yield '<b>%s</b> <small>(' % name + _('no status') + ')</small>' else: yield '<b>%s</b> <small>(' % name + _('offline') + ')</small>'
def _error_dialog(reason, object): _log.error("error: %s %s", reason, object) if reason == 'permissions': title = _("Permissions error") text = _("Found a Logitech Receiver (%s), but did not have permission to open it.") % object + \ '\n\n' + \ _("If you've just installed Solaar, try removing the receiver and plugging it back in.") elif reason == 'unpair': title = _("Unpairing failed") text = _("Failed to unpair %s from %s.") % (object.name, object.receiver.name) + \ '\n\n' + \ _("The receiver returned an error, with no further details.") else: raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object) assert title assert text m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text) m.set_title(title) m.run() m.destroy()
def _error_dialog(reason, object): _log.error('error: %s %s', reason, object) if reason == 'permissions': title = _('Permissions error') text = (_( 'Found a Logitech Receiver (%s), but did not have permission to open it.' ) % object + '\n\n' + _( "If you've just installed Solaar, try removing the receiver and plugging it back in." )) elif reason == 'unpair': title = _('Unpairing failed') text = (_('Failed to unpair %{device} from %{receiver}.').format( device=object.name, receiver=object.receiver.name) + '\n\n' + _('The receiver returned an error, with no further details.')) else: raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object) assert title assert text m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text) m.set_title(title) m.run() m.destroy()
def _show_passcode(assistant, receiver, passkey): if _log.isEnabledFor(_DEBUG): _log.debug('%s show passkey: %s', receiver, passkey) name = receiver.status.device_name authentication = receiver.status.device_authentication intro_text = _('%(receiver_name)s: pair new device') % { 'receiver_name': receiver.name } page_text = _('Enter passcode on %(name)s.') % {'name': name} page_text += '\n' if authentication & 0x01: page_text += _('Type %(passcode)s and then press the enter key.') % { 'passcode': receiver.status.device_passkey } else: passcode = ', '.join([ _('right') if bit == '1' else _('left') for bit in f'{int(receiver.status.device_passkey):010b}' ]) page_text += _( 'Press %(code)s\nand then press left and right buttons simultaneously.' ) % { 'code': passcode } page = _create_page(assistant, Gtk.AssistantPageType.PROGRESS, intro_text, 'preferences-desktop-peripherals', page_text) assistant.set_page_complete(page, True) assistant.next_page()
def __get_insert_menus(self, m, it, c, can_insert, can_insert_only_rule, can_insert_root): items = [] if can_insert: ins = self._menu_insert(m, it) items.append(ins) if c is None: # just a placeholder ins.set_label(_('Insert here')) else: ins.set_label(_('Insert above')) ins2 = self._menu_insert(m, it, below=True) ins2.set_label(_('Insert below')) items.append(ins2) elif can_insert_only_rule: ins = self._menu_create_rule(m, it) items.append(ins) if c is None: ins.set_label(_('Insert new rule here')) else: ins.set_label(_('Insert new rule above')) ins2 = self._menu_create_rule(m, it, below=True) ins2.set_label(_('Insert new rule below')) items.append(ins2) elif can_insert_root: ins = self._menu_create_rule(m, m.iter_nth_child(it, 0)) items.append(ins) return items
def show(dev, reason=None, icon=None, progress=None): """Show a notification with title and text. Optionally displays the `progress` integer value in [0, 100] as a progress bar.""" if available and Notify.is_initted(): summary = dev.name # if a notification with same name is already visible, reuse it to avoid spamming n = _notifications.get(summary) if n is None: n = _notifications[summary] = Notify.Notification() if reason: message = reason elif dev.status is None: message = _('unpaired') elif bool(dev.status): message = dev.status.to_string() or _('connected') else: message = _('offline') # we need to use the filename here because the notifications daemon # is an external application that does not know about our icon sets icon_file = _icons.device_icon_file( dev.name, dev.kind) if icon is None else _icons.icon_file(icon) n.update(summary, message, icon_file) urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL n.set_urgency(urgency) n.set_hint('desktop-entry', GLib.Variant('s', NAME.lower())) if progress: n.set_hint('value', GLib.Variant('i', progress)) try: # if _log.isEnabledFor(_DEBUG): # _log.debug("showing %s", n) n.show() except Exception: _log.exception('showing %s', n)
def __init__(self, sbox, delegate=None): super().__init__(homogeneous=False, spacing=6) self.init(sbox, delegate) self.keyBox = Gtk.ComboBoxText() for entry in sbox.setting.choices: self.keyBox.append(str(int(entry)), _(str(entry))) self.keyBox.set_active(0) key_choice = int(self.keyBox.get_active_id()) self.value_choices = self.sbox.setting.choices[key_choice] self.valueBox = _create_choice_control(sbox.setting, choices=self.value_choices, delegate=self) self.pack_start(self.keyBox, False, False, 0) self.pack_end(self.valueBox, False, False, 0) self.keyBox.connect('changed', self.map_value_notify_key)
def unpair(window, device): assert device assert device.kind is not None qdialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, _('Unpair') + ' ' + device.name + ' ?') qdialog.set_icon_name('remove') qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) qdialog.add_button(_('Unpair'), Gtk.ResponseType.ACCEPT) choice = qdialog.run() qdialog.destroy() if choice == Gtk.ResponseType.ACCEPT: receiver = device.receiver assert receiver device_number = device.number try: del receiver[device_number] except Exception: # _log.exception("unpairing %s", device) error_dialog('unpair', device)
def unpair(window, device): assert device assert device.kind is not None qdialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, _("Unpair") + ' ' + device.name + ' ?') qdialog.set_icon_name('remove') qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) qdialog.add_button(_("Unpair"), Gtk.ResponseType.ACCEPT) choice = qdialog.run() qdialog.destroy() if choice == Gtk.ResponseType.ACCEPT: receiver = device.receiver assert receiver device_number = device.number try: del receiver[device_number] except: # _log.exception("unpairing %s", device) error_dialog('unpair', device)
def _generate_tooltip_lines(): if not _devices_info: yield '<b>%s</b>: ' % NAME + _("no receiver") return for _ignore, number, name, status in _devices_info: if number is None: # receiver continue p = status.to_string() if p: # does it have any properties to print? yield '<b>%s</b>' % name if status: yield '\t%s' % p else: yield '\t%s <small>(' % p + _("offline") + ')</small>' else: if status: yield '<b>%s</b> <small>(' % name + _("no status") + ')</small>' else: yield '<b>%s</b> <small>(' % name + _("offline") + ')</small>' yield ''
def _create_device_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) def _status_line(label_text): b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) b.set_size_request(10, 28) b._label = Gtk.Label(label_text) b._label.set_alignment(0, 0.5) b._label.set_size_request(170, 10) b.pack_start(b._label, False, False, 0) b._icon = Gtk.Image() b.pack_start(b._icon, False, False, 0) b._text = Gtk.Label() b._text.set_alignment(0, 0.5) b.pack_start(b._text, True, True, 0) return b p._battery = _status_line(_('Battery')) p.pack_start(p._battery, False, False, 0) p._secure = _status_line(_('Wireless Link')) p._secure._icon.set_from_icon_name('dialog-warning', _INFO_ICON_SIZE) p.pack_start(p._secure, False, False, 0) p._lux = _status_line(_('Lighting')) p.pack_start(p._lux, False, False, 0) p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False, 0) # spacer p._config = _config_panel.create() p.pack_end(p._config, True, True, 4) return p
def create(receiver): assert receiver is not None assert receiver.kind is None global address, kind, authentication, name, passcode address = name = kind = authentication = passcode = None assistant = Gtk.Assistant() assistant.set_title( _('%(receiver_name)s: pair new device') % {'receiver_name': receiver.name}) assistant.set_icon_name('list-add') assistant.set_size_request(400, 240) assistant.set_resizable(False) assistant.set_role('pair-device') if receiver.receiver_kind == 'bolt': page_text = _( 'Press a pairing button or key until the pairing light flashes quickly.' ) page_text += '\n' page_text += _( 'You may have to first turn the device off and on again.') else: page_text = _('Turn on the device you want to pair.') page_text += '\n' page_text += _( 'If the device is already turned on, turn it off and on again.') if receiver.remaining_pairings() and receiver.remaining_pairings() >= 0: page_text += ngettext( '\n\nThis receiver has %d pairing remaining.', '\n\nThis receiver has %d pairings remaining.', receiver.remaining_pairings()) % receiver.remaining_pairings() page_text += _('\nCancelling at this point will not use up a pairing.') intro_text = _('%(receiver_name)s: pair new device') % { 'receiver_name': receiver.name } page_intro = _create_page(assistant, Gtk.AssistantPageType.PROGRESS, intro_text, 'preferences-desktop-peripherals', page_text) spinner = Gtk.Spinner() spinner.set_visible(True) page_intro.pack_end(spinner, True, True, 24) assistant.connect('prepare', _prepare, receiver) assistant.connect('cancel', _finish, receiver) assistant.connect('close', _finish, receiver) return assistant
def _update_receiver_panel(receiver, panel, buttons, full=False): assert receiver devices_count = len(receiver) paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % { 'count': devices_count } if(receiver.max_devices > 0): paired_text += '\n\n<small>%s</small>' % ngettext('Up to %(max_count)s device can be paired to this receiver.', 'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices) % { 'max_count': receiver.max_devices } elif(devices_count > 0): paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.') panel._count.set_markup(paired_text) is_pairing = receiver.status.lock_open if is_pairing: panel._scanning.set_visible(True) if not panel._spinner.get_visible(): panel._spinner.start() panel._spinner.set_visible(True) else: panel._scanning.set_visible(False) if panel._spinner.get_visible(): panel._spinner.stop() panel._spinner.set_visible(False) panel.set_visible(True) # b._insecure.set_visible(False) buttons._unpair.set_visible(False) may_pair = receiver.may_unpair and not is_pairing if may_pair and devices_count >= receiver.max_devices: online_devices = tuple(n for n in range(1, receiver.max_devices) if n in receiver and receiver[n].online) may_pair &= len(online_devices) < receiver.max_devices buttons._pair.set_sensitive(may_pair) buttons._pair.set_visible(True)
def _pairing_succeeded(assistant, receiver, device): assert device if _log.isEnabledFor(_DEBUG): _log.debug("%s success: %s", receiver, device) page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY) header = Gtk.Label(_("Found a new device:")) header.set_alignment(0.5, 0) page.pack_start(header, False, False, 0) device_icon = Gtk.Image() icon_set = _icons.device_icon_set(device.name, device.kind) device_icon.set_from_icon_set(icon_set, Gtk.IconSize.LARGE) device_icon.set_alignment(0.5, 1) page.pack_start(device_icon, True, True, 0) device_label = Gtk.Label() device_label.set_markup('<b>%s</b>' % device.name) device_label.set_alignment(0.5, 0) page.pack_start(device_label, True, True, 0) hbox = Gtk.HBox(False, 8) hbox.pack_start(Gtk.Label(' '), False, False, 0) hbox.set_property('expand', False) hbox.set_property('halign', Gtk.Align.CENTER) page.pack_start(hbox, False, False, 0) def _check_encrypted(dev): if assistant.is_drawable(): if device.status.get(_K.LINK_ENCRYPTED) == False: hbox.pack_start( Gtk.Image.new_from_icon_name('security-low', Gtk.IconSize.MENU), False, False, 0) hbox.pack_start( Gtk.Label(_("The wireless link is not encrypted") + '!'), False, False, 0) hbox.show_all() else: return True GLib.timeout_add(_STATUS_CHECK, _check_encrypted, device) page.show_all() assistant.next_page() assistant.commit()
def _create_window_layout(): assert _tree is not None assert _details is not None assert _info is not None assert _empty is not None assert _tree.get_selection().get_mode() == Gtk.SelectionMode.SINGLE _tree.get_selection().connect('changed', _device_selected) tree_scroll = Gtk.ScrolledWindow() tree_scroll.add(_tree) tree_scroll.set_min_content_width(_tree.get_size_request()[0]) tree_scroll.set_shadow_type(Gtk.ShadowType.IN) tree_panel = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) tree_panel.set_homogeneous(False) tree_panel.pack_start(tree_scroll, True, True, 0) tree_panel.pack_start(_details, False, False, 0) panel = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 16) panel.pack_start(tree_panel, True, True, 0) panel.pack_start(_info, True, True, 0) panel.pack_start(_empty, True, True, 0) about_button = _new_button(_("About") + ' ' + NAME, 'help-about', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window) bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START) bottom_buttons_box.add(about_button) # solaar_version = Gtk.Label() # solaar_version.set_markup('<small>' + NAME + ' v' + VERSION + '</small>') # bottom_buttons_box.add(solaar_version) # bottom_buttons_box.set_child_secondary(solaar_version, True) vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8) vbox.set_border_width(8) vbox.pack_start(panel, True, True, 0) vbox.pack_end(bottom_buttons_box, False, False, 0) vbox.show_all() _details.set_visible(False) _info.set_visible(False) return vbox
def __init__(self): window = Gtk.Window() window.set_title(_('Solaar Rule Editor')) window.connect('delete-event', self._closing) vbox = Gtk.VBox() self.top_panel, self.view = self._create_top_panel() for col in self._create_view_columns(): self.view.append_column(col) vbox.pack_start(self.top_panel, True, True, 0) self.dirty = False # if dirty, there are pending changes to be saved self.type_ui = {} self.update_ui = {} self.bottom_panel = self._create_bottom_panel() self.ui = defaultdict(lambda: UnsupportedRuleComponentUI(self.bottom_panel)) self.ui.update({ # one instance per type rc_class: rc_ui_class(self.bottom_panel, on_update=self.on_update) for rc_class, rc_ui_class in COMPONENT_UI.items() }) bottom_box = Gtk.ScrolledWindow() bottom_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER) bottom_box.add(self.bottom_panel) vbox.pack_start(bottom_box, True, True, 0) self.model = self._create_model() self.view.set_model(self.model) self.view.expand_all() window.add(vbox) geometry = Gdk.Geometry() geometry.min_width = 800 geometry.min_height = 800 window.set_geometry_hints(None, geometry, Gdk.WindowHints.MIN_SIZE) window.set_position(Gtk.WindowPosition.CENTER) window.show_all() window.connect('delete-event', lambda w, e: w.hide_on_delete() or True) style = window.get_style_context() style.add_class('solaar') self.window = window self._editing_component = None
def has_stopped(self): r, self.receiver = self.receiver, None assert r is not None if _log.isEnabledFor(_INFO): _log.info("%s: notifications listener has stopped", r) # because udev is not notifying us about device removal, # make sure to clean up in _all_listeners _all_listeners.pop(r.path, None) r.status = _("The receiver was unplugged.") if r: try: r.close() except: _log.exception("closing receiver %s" % r.path) self.status_changed_callback(r) #, _status.ALERT.NOTIFICATION)
def _create_window_layout(): assert _tree is not None assert _details is not None assert _info is not None assert _empty is not None assert _tree.get_selection().get_mode() == Gtk.SelectionMode.SINGLE _tree.get_selection().connect('changed', _device_selected) tree_scroll = Gtk.ScrolledWindow() tree_scroll.add(_tree) tree_scroll.set_min_content_width(_tree.get_size_request()[0]) tree_scroll.set_shadow_type(Gtk.ShadowType.IN) tree_panel = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) tree_panel.set_homogeneous(False) tree_panel.pack_start(tree_scroll, True, True, 0) tree_panel.pack_start(_details, False, False, 0) panel = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 16) panel.pack_start(tree_panel, False, False, 0) panel.pack_start(_info, True, True, 0) panel.pack_start(_empty, True, True, 0) about_button = _new_button(_("About") + ' ' + NAME, 'help-about', icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window) bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START) bottom_buttons_box.add(about_button) # solaar_version = Gtk.Label() # solaar_version.set_markup('<small>' + NAME + ' v' + VERSION + '</small>') # bottom_buttons_box.add(solaar_version) # bottom_buttons_box.set_child_secondary(solaar_version, True) vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8) vbox.set_border_width(8) vbox.pack_start(panel, True, True, 0) vbox.pack_end(bottom_buttons_box, False, False, 0) vbox.show_all() _details.set_visible(False) _info.set_visible(False) return vbox
def _create_receiver_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) p._count = Gtk.Label() p._count.set_padding(24, 0) p._count.set_alignment(0, 0.5) p.pack_start(p._count, True, True, 0) p._scanning = Gtk.Label(_("Scanning") + '...') p._spinner = Gtk.Spinner() bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) bp.pack_start(Gtk.Label(' '), True, True, 0) bp.pack_start(p._scanning, False, False, 0) bp.pack_end(p._spinner, False, False, 0) p.pack_end(bp, False, False, 0) return p
def _pairing_succeeded(assistant, receiver, device): assert device if _log.isEnabledFor(_DEBUG): _log.debug("%s success: %s", receiver, device) page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY) header = Gtk.Label(_("Found a new device:")) header.set_alignment(0.5, 0) page.pack_start(header, False, False, 0) device_icon = Gtk.Image() icon_set = _icons.device_icon_set(device.name, device.kind) device_icon.set_from_icon_set(icon_set, Gtk.IconSize.LARGE) device_icon.set_alignment(0.5, 1) page.pack_start(device_icon, True, True, 0) device_label = Gtk.Label() device_label.set_markup("<b>%s</b>" % device.name) device_label.set_alignment(0.5, 0) page.pack_start(device_label, True, True, 0) hbox = Gtk.HBox(False, 8) hbox.pack_start(Gtk.Label(" "), False, False, 0) hbox.set_property("expand", False) hbox.set_property("halign", Gtk.Align.CENTER) page.pack_start(hbox, False, False, 0) def _check_encrypted(dev): if assistant.is_drawable(): if device.status.get(_K.LINK_ENCRYPTED) == False: hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0) hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + "!"), False, False, 0) hbox.show_all() else: return True GLib.timeout_add(_STATUS_CHECK, _check_encrypted, device) page.show_all() assistant.next_page() assistant.commit()
def run_loop(startup_hook, shutdown_hook, use_tray, show_window): assert use_tray or show_window, 'need either tray or visible window' # from gi.repository.Gio import ApplicationFlags as _ApplicationFlags APP_ID = 'io.github.pwr_solaar.solaar' application = Gtk.Application.new( APP_ID, Gio.ApplicationFlags.HANDLES_COMMAND_LINE) application.connect( 'startup', lambda app, startup_hook: _startup( app, startup_hook, use_tray, show_window), startup_hook) application.connect('command-line', _command_line) application.connect('activate', _activate) application.connect('shutdown', _shutdown, shutdown_hook) application.register() if application.get_is_remote(): print( _('Another Solaar process is already running so just expose its window' )) application.run()
def _pairing_failed(assistant, receiver, error): if _log.isEnabledFor(_DEBUG): _log.debug('%s fail: %s', receiver, error) assistant.commit() header = _('Pairing failed') + ': ' + _(str(error)) + '.' if 'timeout' in str(error): text = _('Make sure your device is within range, and has a decent battery charge.') elif str(error) == 'device not supported': text = _('A new device was detected, but it is not compatible with this receiver.') elif 'many' in str(error): text = _('More paired devices than receiver can support.') else: text = _('No further details are available about the error.') _create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, 'dialog-error', text) assistant.next_page() assistant.commit()
def _pairing_failed(assistant, receiver, error): if _log.isEnabledFor(_DEBUG): _log.debug("%s fail: %s", receiver, error) assistant.commit() header = _("Pairing failed") + ": " + _(str(error)) + "." if "timeout" in str(error): text = _("Make sure your device is within range, and has a decent battery charge.") elif str(error) == "device not supported": text = _("A new device was detected, but it is not compatible with this receiver.") elif "many" in str(error): text = _("The receiver only supports %d paired device(s).") else: text = _("No further details are available about the error.") _create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, "dialog-error", text) assistant.next_page() assistant.commit()
def _error_dialog(reason, object): _log.error("error: %s %s", reason, object) if reason == 'permissions': title = _("Permissions error") text = _("Found a Logitech Receiver (%s), but did not have permission to open it.") % object + \ '\n\n' + \ _("If you've just installed Solaar, try removing the receiver and plugging it back in.") elif reason == 'unpair': title = _("Unpairing failed") text = _("Failed to unpair %{device} from %{receiver}.").format(device=object.name, receiver=object.receiver.name) + \ '\n\n' + \ _("The receiver returned an error, with no further details.") else: raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object) assert title assert text m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text) m.set_title(title) m.run() m.destroy()
# # # # def _toggle_notifications(action): # if action.get_active(): # notify.init('Solaar') # else: # notify.uninit() # action.set_sensitive(notify.available) # toggle_notifications = make_toggle('notifications', 'Notifications', _toggle_notifications) from .about import show_window as _show_about_window from solaar import NAME about = make('help-about', _("About") + ' ' + NAME, _show_about_window, stock_id=Gtk.STOCK_ABOUT) # # # from . import pair_window def pair(window, receiver): assert receiver assert receiver.kind is None pair_dialog = pair_window.create(receiver) pair_dialog.set_transient_for(window) pair_dialog.set_destroy_with_parent(True) pair_dialog.set_modal(True) pair_dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
def _create_empty_panel(): p = Gtk.Label() p.set_markup('<small>' + _("Select a device") + '</small>') p.set_sensitive(False) return p
# _SMALL_BUTTON_ICON_SIZE = Gtk.IconSize.MENU _NORMAL_BUTTON_ICON_SIZE = Gtk.IconSize.BUTTON _TREE_ICON_SIZE = Gtk.IconSize.BUTTON _INFO_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR _DEVICE_ICON_SIZE = Gtk.IconSize.DND # tree model columns _COLUMN = _NamedInts(PATH=0, NUMBER=1, ACTIVE=2, NAME=3, ICON=4, STATUS_TEXT=5, STATUS_ICON=6, DEVICE=7) _COLUMN_TYPES = (str, int, bool, str, str, str, str, TYPE_PYOBJECT) _TREE_SEPATATOR = (None, 0, False, None, None, None, None, None) assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES) assert len(_COLUMN_TYPES) == len(_COLUMN) _TOOLTIP_LINK_SECURE = _("The wireless link between this device and its receiver is encrypted.") _TOOLTIP_LINK_INSECURE = _("The wireless link between this device and its receiver is not encrypted.\n" "\n" "For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n" "\n" "It is, however, a major security issue for text-input devices (keyboards, numpads),\n" "because typed text can be sniffed inconspicuously by 3rd parties within range.") _UNIFYING_RECEIVER_TEXT = ( _("No device paired") + '.\n\n<small>' + _("Up to %d devices can be paired to this receiver") + '.</small>', '%d ' + _("paired devices") + '\n\n<small>' + _("Up to %d devices can be paired to this receiver") + '.</small>', ) _NANO_RECEIVER_TEXT = ( _("No device paired") + '.\n\n<small> </small>', ' \n\n<small>' + _("Only one device can be paired to this receiver") + '.</small>', )
def _update_device_panel(device, panel, buttons, full=False): assert device is_online = bool(device.online) panel.set_sensitive(is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) if battery_level is None: icon_name = _icons.battery() panel._battery._icon.set_sensitive(False) panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE) panel._battery._text.set_sensitive(True) panel._battery._text.set_markup('<small>%s</small>' % _("unknown")) else: charging = device.status.get(_K.BATTERY_CHARGING) icon_name = _icons.battery(battery_level, charging) panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE) panel._battery._icon.set_sensitive(True) if isinstance(battery_level, _NamedInt): text = _(str(battery_level)) else: text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level } if is_online: if charging: text += ' <small>(%s)</small>' % _("charging") else: text += ' <small>(%s)</small>' % _("last known") panel._battery._text.set_sensitive(is_online) panel._battery._text.set_markup(text) if is_online: not_secure = device.status.get(_K.LINK_ENCRYPTED) == False if not_secure: panel._secure._text.set_text(_("not encrypted")) panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE) panel._secure.set_tooltip_text(_("The wireless link between this device and its receiver is not encrypted.\n" "\n" "For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n" "\n" "It is, however, a major security issue for text-input devices (keyboards, numpads),\n" "because typed text can be sniffed inconspicuously by 3rd parties within range.")) else: panel._secure._text.set_text(_("encrypted")) panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE) panel._secure.set_tooltip_text(_("The wireless link between this device and its receiver is encrypted.")) panel._secure._icon.set_visible(True) else: panel._secure._text.set_markup('<small>%s</small>' % _("offline")) panel._secure._icon.set_visible(False) panel._secure.set_tooltip_text('') if is_online: light_level = device.status.get(_K.LIGHT_LEVEL) if light_level is None: panel._lux.set_visible(False) else: panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE) panel._lux._text.set_text(_("%(light_level)d lux") % { 'light_level': light_level }) panel._lux.set_visible(True) else: panel._lux.set_visible(False) buttons._pair.set_visible(False) buttons._unpair.set_sensitive(device.receiver.may_unpair) buttons._unpair.set_visible(True) panel.set_visible(True) if full: _config_panel.update(device, is_online)
def update(device, need_popup=False): if _window is None: return assert device is not None if need_popup: popup() selected_device_id = _find_selected_device_id() if device.kind is None: # receiver is_alive = bool(device) item = _receiver_row(device.path, device if is_alive else None) assert item if is_alive and item: was_pairing = bool(_model.get_value(item, _COLUMN.STATUS_ICON)) is_pairing = bool(device.status.lock_open) _model.set_value( item, _COLUMN.STATUS_ICON, 'network-wireless' if is_pairing else _CAN_SET_ROW_NONE) if selected_device_id == (device.path, 0): full_update = need_popup or was_pairing != is_pairing _update_info_panel(device, full=full_update) elif item: if _TREE_SEPATATOR: separator = _model.iter_next(item) _model.remove(separator) _model.remove(item) else: # peripheral is_paired = bool(device) assert device.receiver assert device.number is not None and device.number > 0, "invalid device number" + str( device.number) item = _device_row(device.receiver.path, device.number, device if is_paired else None) if is_paired and item: was_online = _model.get_value(item, _COLUMN.ACTIVE) is_online = bool(device.online) _model.set_value(item, _COLUMN.ACTIVE, is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) if battery_level is None: _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE) _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE) else: if isinstance(battery_level, _NamedInt): status_text = _("%(battery_level)s") % { 'battery_level': _(str(battery_level)) } else: status_text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level } _model.set_value(item, _COLUMN.STATUS_TEXT, status_text) charging = device.status.get(_K.BATTERY_CHARGING) icon_name = _icons.battery(battery_level, charging) _model.set_value(item, _COLUMN.STATUS_ICON, icon_name) if selected_device_id is None or need_popup: select(device.receiver.path, device.number) elif selected_device_id == (device.receiver.path, device.number): full_update = need_popup or was_online != is_online _update_info_panel(device, full=full_update) elif item: _model.remove(item) _config_panel.clean(device) # make sure all rows are visible _tree.expand_all()
def _details_items(device, read_all=False): # If read_all is False, only return stuff that is ~100% already # cached, and involves no HID++ calls. if device.kind is None: yield (_("Path"), device.path) # 046d is the Logitech vendor id yield (_("USB id"), '046d:' + device.product_id) if read_all: yield (_("Serial"), device.serial) else: yield (_("Serial"), '...') else: # yield ('Codename', device.codename) yield (_("Index"), device.number) yield (_("Wireless PID"), device.wpid) hid_version = device.protocol yield (_("Protocol"), 'HID++ %1.1f' % hid_version if hid_version else 'unknown') if read_all and device.polling_rate: yield (_("Polling rate"), '%d ms (%dHz)' % (device.polling_rate, 1000 // device.polling_rate)) if read_all or not device.online: yield (_("Serial"), device.serial) else: yield (_("Serial"), '...') if read_all: for fw in list(device.firmware): yield (' ' + _(str(fw.kind)), (fw.name + ' ' + fw.version).strip()) elif device.kind is None or device.online: yield (' %s' % _("Firmware"), '...') flag_bits = device.status.get(_K.NOTIFICATION_FLAGS) if flag_bits is not None: flag_names = ('(%s)' % _("none"),) if flag_bits == 0 else _hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits) yield (_("Notifications"), ('\n%15s' % ' ').join(flag_names))
def update(device, need_popup=False): if _window is None: return assert device is not None if need_popup: popup() selected_device_id = _find_selected_device_id() if device.kind is None: # receiver is_alive = bool(device) item = _receiver_row(device.path, device if is_alive else None) assert item if is_alive and item: was_pairing = bool(_model.get_value(item, _COLUMN.STATUS_ICON)) is_pairing = bool(device.status.lock_open) _model.set_value(item, _COLUMN.STATUS_ICON, 'network-wireless' if is_pairing else _CAN_SET_ROW_NONE) if selected_device_id == (device.path, 0): full_update = need_popup or was_pairing != is_pairing _update_info_panel(device, full=full_update) elif item: if _TREE_SEPATATOR: separator = _model.iter_next(item) _model.remove(separator) _model.remove(item) else: # peripheral is_paired = bool(device) assert device.receiver assert device.number is not None and device.number > 0, "invalid device number" + str(device.number) item = _device_row(device.receiver.path, device.number, device if is_paired else None) if is_paired and item: was_online = _model.get_value(item, _COLUMN.ACTIVE) is_online = bool(device.online) _model.set_value(item, _COLUMN.ACTIVE, is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) if battery_level is None: _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE) _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE) else: if isinstance(battery_level, _NamedInt): status_text = _("%(battery_level)s") % { 'battery_level': _(str(battery_level)) } else: status_text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level } _model.set_value(item, _COLUMN.STATUS_TEXT, status_text) charging = device.status.get(_K.BATTERY_CHARGING) icon_name = _icons.battery(battery_level, charging) _model.set_value(item, _COLUMN.STATUS_ICON, icon_name) if selected_device_id is None or need_popup: select(device.receiver.path, device.number) elif selected_device_id == (device.receiver.path, device.number): full_update = need_popup or was_online != is_online _update_info_panel(device, full=full_update) elif item: _model.remove(item) _config_panel.clean(device) # make sure all rows are visible _tree.expand_all()
def _update_device_panel(device, panel, buttons, full=False): assert device is_online = bool(device.online) panel.set_sensitive(is_online) battery_level = device.status.get(_K.BATTERY_LEVEL) if battery_level is None: icon_name = _icons.battery() panel._battery._icon.set_sensitive(False) panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE) panel._battery._text.set_sensitive(True) panel._battery._text.set_markup('<small>%s</small>' % _("unknown")) else: charging = device.status.get(_K.BATTERY_CHARGING) icon_name = _icons.battery(battery_level, charging) panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE) panel._battery._icon.set_sensitive(True) if isinstance(battery_level, _NamedInt): text = _(str(battery_level)) else: text = '%d%%' % battery_level if is_online: if charging: text += ' <small>(%s)</small>' % _("charging") else: text += ' <small>(%s)</small>' % _("last known") panel._battery._text.set_sensitive(is_online) panel._battery._text.set_markup(text) if is_online: not_secure = device.status.get(_K.LINK_ENCRYPTED) == False if not_secure: panel._secure._text.set_text(_("not encrypted")) panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE) panel._secure.set_tooltip_text(_TOOLTIP_LINK_INSECURE) else: panel._secure._text.set_text(_("encrypted")) panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE) panel._secure.set_tooltip_text(_TOOLTIP_LINK_SECURE) panel._secure._icon.set_visible(True) else: panel._secure._text.set_markup('<small>%s</small>' % _("offline")) panel._secure._icon.set_visible(False) panel._secure.set_tooltip_text('') if is_online: light_level = device.status.get(_K.LIGHT_LEVEL) if light_level is None: panel._lux.set_visible(False) else: panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE) panel._lux._text.set_text('%d %s' % (light_level, _("lux"))) panel._lux.set_visible(True) else: panel._lux.set_visible(False) buttons._pair.set_visible(False) buttons._unpair.set_sensitive(device.receiver.may_unpair) buttons._unpair.set_visible(True) panel.set_visible(True) if full: _config_panel.update(device, is_online)