def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar( "org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list)
def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = {"UserListChanged": self.build_user_list} # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.delete_user_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK) self.objects.delete_user_dialog.get_widget_for_response( Gtk.ResponseType.OK).get_style_context().add_class( "destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.groups_dialog.add_buttons(_("_Close"), Gtk.ResponseType.CLOSE) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Enabled", self.group_enabled_toggle, active=1)) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Group description", Gtk.CellRendererText(), text=2)) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed)
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list)
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.locale1.set-keyboard") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Keyboard = Keyboard() self.building_list = False self.default = None self.default_variant = None self.default_model = None # Make the layout_view treeview working... layout_renderer = Gtk.CellRendererText() self.layout_column = Gtk.TreeViewColumn("Layout", layout_renderer, text=1) self.objects.layouts.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.layout_view.append_column(self.layout_column) # Do the same for the variant_view... variant_renderer = Gtk.CellRendererText() self.variant_column = Gtk.TreeViewColumn("Variant", variant_renderer, text=1) self.objects.variants.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.variant_view.append_column(self.variant_column) # And something similar for the model combobox... model_renderer = Gtk.CellRendererText() self.objects.model_combo.pack_start(model_renderer, True) self.objects.model_combo.add_attribute(model_renderer, "text", 1) self.objects.models.set_sort_column_id(1, Gtk.SortType.ASCENDING) # Populate the layout list GObject.idle_add(self.build_layout_list) # Populate the model list GObject.idle_add(self.build_model_list)
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "toggled": ("show_all", "savespace_enable"), "cursor-changed": ("locale_view", ), } @quickstart.threads.thread def set_locale(self, locale, sel, itr): """ Sets the given locale. """ try: self.Locale.set(locale) self.default = itr # Create stamp self.Locale.create_stamp([".alan2-locale-changed"]) GObject.idle_add(self.RebootDialog.show) except: sel.select_iter(self.default) GObject.idle_add(self.objects.region_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) @quickstart.threads.thread def savespace_purge(self, locale): """ Purges foreign locales. """ self.Locale.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) def on_locale_view_cursor_changed(self, locale_view): """ Fired when the user changes the locale. """ # selection sel = self.objects.locale_view.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.locales.get_value(itr, 0) if selected == self.Locale.default: return if self.objects.savespace_enable.get_active(): # Display warning if self.objects.savespace_warning.run() == Gtk.ResponseType.NO: self.objects.savespace_warning.hide() sel.select_iter(self.default) return self.objects.savespace_enable.set_active(False) self.objects.savespace_warning.hide() GObject.idle_add(self.objects.region_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.set_locale(selected, sel, itr) def on_show_all_toggled(self, checkbutton): """ Fired when the 'Show all locales' checkbutton has been clicked. """ GObject.idle_add(self.build_locale_list, self.objects.show_all.get_active()) def on_savespace_enable_toggled(self, checkbutton): """ Fired when the 'Enable savespace' checkbutton has been clicked. """ locale = self.objects.locales.get_value(self.default, 0) if checkbutton.get_active(): self.Locale.savespace_enable(locale) # Purge window if self.objects.savespace_window.run() == Gtk.ResponseType.YES: # Purge!! self.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.objects.savespace_window.hide() else: self.Locale.savespace_disable() def build_locale_list(self, all=False): """ Populates the listbox with locales. """ self.objects.locales.clear() for locale, human in self.Locale.human_form(all=all).items(): if all: codepage = self.Locale.codepages[locale] else: codepage = "" itr = self.objects.locales.append((locale, human, codepage)) # Save iter if this is the default... if locale == self.Locale.default: self.default = itr if self.default: sel = self.objects.locale_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.locale_view.scroll_to_cell, sel.get_selected_rows()[1][0]) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar( "org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") self.cancellable = Gio.Cancellable() self.RebootDialog = RebootDialog(self.cancellable) self.RebootDialog.bind_property("visible", self.scene_container, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() self.cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Keyboard settings. """ events = { "changed": ("model_combo", ), "cursor-changed": ("layout_view", "variant_view"), } def setxkbmap(self): """ Invokes setxkbmap and sets the currently selected layout, variant and model. """ subprocess.call( [ "setxkbmap", self.Keyboard.default_layout, self.Keyboard.default_variant if self.Keyboard.default_variant else "", "-model" if self.Keyboard.default_model else "", self.Keyboard.default_model if self.Keyboard.default_model else "" ] ) def on_model_combo_changed(self, combobox): """ Fired when the user changes the keyboard model. """ itr = self.objects.model_combo.get_active_iter() if not itr: return selected = self.objects.models.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(model=selected) self.default_model = itr self.setxkbmap() except: self.objects.model_combo.set_active_iter(self.default_model) def on_variant_view_cursor_changed(self, treeview): """ Fired when the user changes the variant. """ if self.building_list: return # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.variants.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(variant=selected) self.default_variant = itr self.setxkbmap() except: sel.select_iter(self.default_variant) def on_layout_view_cursor_changed(self, treeview): """ Fired when the user changes the locale. """ # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.layouts.get_value(itr, 0) if selected == self.Keyboard.default_layout: return # Reset default variant self.default_variant = None try: self.Keyboard.set(layout=selected, variant='') self.default = itr self.setxkbmap() except: sel.select_iter(self.default) # Rebuild variant list GObject.idle_add(self.build_variant_list, selected) def build_variant_list(self, layout): """ Populates the variant list. """ self.building_list = True self.objects.variants.clear() models, layouts = self.Keyboard.supported() variants = layouts[layout]["variants"] for variant in variants: for item, key in variant.items(): itr = self.objects.variants.append([item, key]) # Save the default variant if item == self.Keyboard.default_variant: self.default_variant = itr # No variant reciter = self.objects.variants.prepend(['', layouts[layout]["description"]]) sel = self.objects.variant_view.get_selection() if self.default_variant: sel.select_iter(self.default) else: # No variant, use reciter sel.select_iter(reciter) GObject.idle_add(self.objects.variant_view.scroll_to_cell, sel.get_selected_rows()[1][0]) self.building_list = False def build_model_list(self): """ Populates the model list. """ pc105 = None self.objects.models.clear() models = self.Keyboard.supported()[0] for item, key in models.items(): itr = self.objects.models.append([item, key]) # Save the default model if item == self.Keyboard.default_model: self.default_model = itr elif item == "pc105": # Fallback pc105 = itr if self.default_model: self.objects.model_combo.set_active_iter(self.default_model) else: self.objects.model_combo.set_active_iter(pc105) def build_layout_list(self): """ Populates the layout list. """ self.objects.layouts.clear() models, layouts = self.Keyboard.supported() for item, key in layouts.items(): itr = self.objects.layouts.append([item, key["description"]]) # Save iter if it's the default... if item == self.Keyboard.default_layout: self.default = itr if self.default: sel = self.objects.layout_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.layout_view.scroll_to_cell, sel.get_selected_rows()[1][0]) # Also build the variant view! GObject.idle_add(self.build_variant_list, self.Keyboard.default_layout) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.locale1.set-keyboard") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Keyboard = Keyboard() self.building_list = False self.default = None self.default_variant = None self.default_model = None # Make the layout_view treeview working... layout_renderer = Gtk.CellRendererText() self.layout_column = Gtk.TreeViewColumn("Layout", layout_renderer, text=1) self.objects.layouts.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.layout_view.append_column(self.layout_column) # Do the same for the variant_view... variant_renderer = Gtk.CellRendererText() self.variant_column = Gtk.TreeViewColumn("Variant", variant_renderer, text=1) self.objects.variants.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.variant_view.append_column(self.variant_column) # And something similar for the model combobox... model_renderer = Gtk.CellRendererText() self.objects.model_combo.pack_start(model_renderer, True) self.objects.model_combo.add_attribute(model_renderer, "text", 1) self.objects.models.set_sort_column_id(1, Gtk.SortType.ASCENDING) # Populate the layout list GObject.idle_add(self.build_layout_list) # Populate the model list GObject.idle_add(self.build_model_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() return True
class Scene(quickstart.scenes.BaseScene): """ Time & Date settings. """ events = { "delete-event" : ("select_timezone_dialog",), "realize": ("select_timezone_dialog",), "button-press-event" : ("main",), "clicked" : ("time_button", "location_button", "apply_timezone"), "toggled" : ("calendar_button", "ntp_enabled"), "value-changed" : ("hours_adjustment", "minutes_adjustment", "seconds_adjustment"), "wrapped" : ("hours",), "output" : ("hours", "minutes", "seconds") } @quickstart.threads.on_idle def build_timezone_list(self): """ Builds the timezone list. """ self.objects.timezones.clear() for item, key in self.TimeZone.supported.items(): for zone in key: zone1 = "%s/%s" % (item, zone) itr = self.objects.timezones.append([zone1, zone]) # Save the iter if it's the default if zone1 == self.TimeZone.default: # save the iter! ;) self.default = itr if self.default: sel = self.objects.timezone_treeview.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.timezone_treeview.scroll_to_cell, sel.get_selected_rows()[1][0]) GObject.idle_add(self.objects.timezone_treeview.grab_focus) def on_apply_timezone_clicked(self, button): """ Fired when the apply_timezone button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.hide) # Get selected sel = self.objects.timezone_treeview.get_selection() if not sel: return model, itr = sel.get_selected() if not itr: return selected = self.objects.timezones.get_value(itr, 0) if selected == self.objects.location_button.get_label(): return # Ugly! try: self.TimeDate.SetTimezone( '(sb)', selected, True ) self.default = itr self.current_datetime = datetime.datetime.now() except: sel.select_iter(self.default) self.objects.location_button.set_label(self.objects.timezones.get_value(self.default, 0)) def on_select_timezone_dialog_realize(self, widget): """ Fired when the select_timezone_dialog is going to be shown. """ self.build_timezone_list() def on_select_timezone_dialog_delete_event(self, widget, event): """ Fired when the select_timezone_dialog is going to be destroyed. """ widget.hide() return True # do not destroy def on_main_button_press_event(self, eventbox, event): """ Fired when the user has clicked on the eventbox. We use this call to return to the 'read-only' mode on the time view. """ if self.objects.time_modify.props.visible and event.type == Gdk.EventType.BUTTON_PRESS: self.refresh_infos() self.objects.time_modify.hide() self.objects.time_button.show() def on_spinbutton_output(self, spinbutton): """ Fired when a spinbutton has been changed. We use this method to ensure to have two digits everytime. Code comes from http://stackoverflow.com/a/9998968 (thanks!) (not connected to anything, due to a current limitation of quickstart we will associate on_*widget*_output to this method later) """ spinbutton.set_text('{:02d}'.format(int(spinbutton.get_adjustment().get_value()))) return True on_hours_output = on_spinbutton_output on_minutes_output = on_spinbutton_output on_seconds_output = on_spinbutton_output def on_hours_wrapped(self, spinbutton): """ Fired when the hours spinbutton has been wrapped. """ if self.timezone12: if int(spinbutton.get_text()) == 1: # +1 self.hour_offset = 0 if self.hour_offset == 12 else 12 else: # -1 self.hour_offset = 12 if self.hour_offset == 0 else 0 # Reset time self.on_hours_adjustment_value_changed(spinbutton.get_adjustment()) def set_time(self): """ Actually sets the time. """ print("Setting time...") self.TimeDate.SetTime('(xbb)', time.mktime(self.current_datetime.timetuple()) * 1000000, False, False) def on_hours_adjustment_value_changed(self, adjustment): """ Fired when the hours adjustment has been changed. """ if self.hours_input: value = int(adjustment.get_value()) if self.timezone12: value += self.hour_offset self.current_datetime = self.current_datetime.replace( hour=value if not self.timezone12 else ( 0 if value == 24 else value ) ) if self.timezone12: self.objects.timezone12_edit.set_text(self.current_datetime.strftime("%p")) self.set_time() def on_minutes_adjustment_value_changed(self, adjustment): """ Fired when the minutes adjustment has been changed. """ if self.minutes_input: self.current_datetime = self.current_datetime.replace(minute=int(adjustment.get_value())) self.set_time() def on_seconds_adjustment_value_changed(self, adjustment): """ Fired when the seconds adjustment has been changed. """ if self.seconds_input: self.current_datetime = self.current_datetime.replace(second=int(adjustment.get_value())) self.set_time() def on_location_button_clicked(self, button): """ Fired when the location_button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.present) def on_time_button_clicked(self, button): """ Fired when the time_button has been clicked. """ self.hours_input = self.minutes_input = self.seconds_input = False button.hide() self.objects.time_modify.show() # Load the adjustments with current data time = self.current_datetime.time() # Handle 12-hour timezones if not self.timezone12: # 24 hour self.objects.hours_adjustment.set_value(time.hour) self.objects.timezone12_edit.hide() else: # 12 hour self.objects.hours_adjustment.set_value(int(time.strftime("%I"))) self.objects.timezone12_edit.set_text(self.current_datetime.strftime("%p")) self.objects.minutes_adjustment.set_value(time.minute) self.objects.seconds_adjustment.set_value(time.second) def on_calendar_button_toggled(self, button): """ Fired when the calendar_button has been toggled. """ if button.get_active(): self.calendar_popover.show_all() def on_day_selected(self, calendar): """ Fired when a day in the calendar has been selected. """ date = calendar.get_date() self.current_datetime = self.current_datetime.replace( year=date[0], month=date[1]+1, day=date[2] ) self.objects.calendar_button.set_label(self.current_datetime.date().strftime("%A %d %B %Y")) # Update time if we should if self.date_input: self.set_time() def on_ntp_enabled_toggled(self, checkbox): """ Fired when the ntp_enabled checkbox has been toggled. """ if self.unlockbar.current_state == ActionResponse.UNLOCK: print("Setting NTP to %s" % checkbox.get_active()) try: self.TimeDate.SetNTP('(bb)', checkbox.get_active(), False) except GLib.GError as error: # FIXME? Here it raises FileNotFound error, I think # is some Debian bug? pass # Set sensitivity on the manual container self.objects.manual_container.set_sensitive(not checkbox.get_active()) @quickstart.threads.on_idle def refresh_infos(self): """ Refreshes the information displayed. """ _datetime = datetime.datetime.now() self.objects.time.set_text(_datetime.time().strftime("%X")) @quickstart.threads.on_idle def update_date(self): """ Updates the date. """ date = self.current_datetime.date() self.date_input = False self.calendar.select_month(date.month-1, date.year) self.calendar.select_day(date.day) self.date_input = True @quickstart.threads.on_idle def update_time(self): """ Updates the time. """ self.current_datetime += datetime.timedelta(seconds=1) time = self.current_datetime.time() if time.hour == 00 and time.minute == 00 and time.second == 00: # Day changed self.update_date() # Update the current visible time object if self.objects.time_modify.props.visible: if time.minute == 00 and time.second == 00: self.hours_input = False self.objects.hours_adjustment.set_value(time.hour) if time.second == 00: self.minutes_input = False self.objects.minutes_adjustment.set_value(time.minute) self.seconds_input = False self.objects.seconds_adjustment.set_value(time.second) self.hours_input = self.minutes_input = self.seconds_input = True else: self.objects.time.set_text(time.strftime("%X")) def on_locked(self, unlockbar): """ Fired when the user (or policykit) has locked the special temporary privilegies. """ # Locked, disable sensitivity on everything... self.objects.container.set_sensitive(False) def on_unlocked(self, unlockbar): """ Fired when the user has got the special temporary privilegies. """ # Unlocked, re-set sensitivity self.objects.container.set_sensitive(True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable ) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn( "Timezone", Gtk.CellRendererText(), text=0 ) ) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect("closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE*80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class("no-borders") self.objects.calendar_button.get_style_context().add_class("no-borders") def on_scene_called(self): """ Called when switching to this scene. We will handle here all timeouts to ensure we syncronize the time label with the actual time. """ self.objects.main.show_all() self.objects.time_modify.hide() # We are locked self.unlockbar.emit("locked") self.current_datetime = datetime.datetime.now() self.timezone12 = (not self.current_datetime.strftime("%p") == "") adj = self.objects.hours.get_adjustment() if self.timezone12: adj.set_upper(12) adj.set_lower(1) else: adj.set_upper(23) adj.set_lower(0) if self.current_datetime.time().hour > 12: self.hour_offset = 12 self.update_date() self.update_time() # NTP ntp = bool(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'NTP')) self.objects.ntp_enabled.set_active(ntp) # Timezone self.objects.location_button.set_label(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'Timezone')) self.label_timeout = GLib.timeout_add_seconds(1, self.update_time) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ GLib.source_remove(self.label_timeout) self.unlockbar.cancel_authorization() self.bus_cancellable.cancel() return True
def prepare_scene(self): """ Fired when doing the scene setup. """ self.scene_container = self.objects.main # Connect to the handler self.handler = UpdateHandler() # Convenience variable that houses the "installing" state. # Used when checking whether to continue following the apt log, # without flooding the DBus bus. self._installing = False # Determines if we are following the log or not self.following_log = False # APT_LOG follower (FIXME: should move it elsewhere) self.follower = None # Determines if the log has been fully written to the buffer self.log_written = False # Set appropriate font size and weight for the "Distribution upgrades" label context = self.objects.distribution_upgrade_label.create_pango_context( ) desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE * 13) # Size self.objects.distribution_upgrade_label.override_font(desc) # Do the same for the "Application updates" label self.objects.application_updates_label.override_font(desc) # ...and for the "The software is up-to-date" one self.objects.application_updates_status.override_font(desc) # ..and for the error_description desc.set_size(Pango.SCALE * 10) self.objects.error_description.override_font(desc) # ...and for the "Semplice is installing the updates" one desc.set_size(Pango.SCALE * 20) self.objects.install_label.override_font(desc) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.channels.manage") self.objects.summary.pack_start(self.unlockbar, False, False, 0) # Set-up channel combobox renderer = Gtk.CellRendererText() self.objects.selected_channel.pack_start(renderer, True) self.objects.selected_channel.add_attribute(renderer, "text", 1) self.objects.selected_channel.add_attribute(renderer, "sensitive", 2) # Create update list self.update_list = UpdateList() self.objects.application_updates_scroll.add(self.update_list) #self.objects.application_updates_content.pack_start(self.update_list, True, True, 5) #self.update_list.prepend(Gtk.Label("HAAA")) #print(len(self.update_list), self.update_list.props.empty, self.update_list.props.selection_mode) self.update_list.show_all() # Open AppStream database # FIXME pending AppStream API update. See #4 #Database.open() #for item in ["glade", "pokerth", "banshee", "gnome-software"]: # self.update_list.add_item(-1, item, "XX", "upgrade", True) # FIXME self.objects.distribution_upgrade.hide() # Connect to status-toggled self.update_list.connect("status-toggled", self.on_status_toggled) # Connect to lock failed: self.handler.connect("lock-failed", self.on_lock_failed) # Connect to generic failure: self.handler.connect("generic-failure", self.on_generic_failure) # Connect to update found: self.handler.connect("update-found", self.on_update_found) # Connect to package-status-changed self.handler.connect("package-status-changed", self.on_package_status_changed) # Connect to package-fetch signals self.handler.connect("package-fetch-started", self.on_package_fetch_started) self.handler.connect("package-fetch-failed", self.on_package_fetch_failed) self.handler.connect("package-fetch-finished", self.on_package_fetch_finished) # React when checking changes self.handler.connect("notify::checking", self.on_checking_changed) # React when refreshing self.handler.connect("notify::refreshing", self.on_refreshing_changed) # React when downloading self.handler.connect("notify::downloading", self.on_downloading_changed) # React when installing self.handler.connect("notify::installing", self.on_installing_changed) # React when we know the total size to download self.handler.connect("notify::update-required-download", self.on_update_required_download_changed) # Ensure that the updates_frame is not sensitive when the settings # are locked... self.unlockbar.bind_property("lock", self.objects.settings_frame, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN) # Show the up-to-date container if the update list is empty self.update_list.bind_property( "empty", self.objects.application_updates_status, "visible", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "status-scene", self.objects.application_updates_status, "visible_child_name", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) #self.handler.bind_property( # "cache-operation", # self.objects.application_updates_checking_container, # "visible", # GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE #) #self.objects.application_updates_checking_container.bind_property( # "visible", # self.objects.software_uptodate_container, # "visible", # GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE #) self.update_list.bind_property( "empty", self.objects.application_updates_scroll, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.update_list.bind_property( "empty", self.objects.application_updates_actions, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Show download indicators when the cache is refreshing self.handler.bind_property("cache-operation", self.objects.spinner, "active", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-operation", self.objects.download_rate, "visible", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-operation", self.objects.download_eta, "visible", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-rate", self.objects.download_rate, "label", GObject.BindingFlags.DEFAULT) self.handler.bind_property("download-eta", self.objects.download_eta, "label", GObject.BindingFlags.DEFAULT) self.handler.bind_property("download-current-item", self.objects.download_rate, "tooltip_text", GObject.BindingFlags.DEFAULT) self.handler.bind_property( "cache-operation", self.objects.refresh_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "cache-operation", self.update_list, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "cache-operation", self.objects.application_updates_actions, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Remove sensitiveness on checkbuttons when downloading self.handler.bind_property( "downloading", self.update_list.package_checkbox, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Update labels in the action buttons accordingly to the current mode self.handler.bind_property("download-operation-label", self.objects.download_button, "label", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("install-operation-label", self.objects.install_button, "label", GObject.BindingFlags.SYNC_CREATE) # Update sensitiveness of the install_button when downloading self.handler.bind_property( "downloading", self.objects.install_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Update the installation_scene label self.handler.bind_property("install-scene-label", self.objects.install_label, "label", GObject.BindingFlags.SYNC_CREATE) # Show the log textview when "Show details" has been pressed self.objects.show_details_button.bind_property( "active", self.objects.details_scrolled, "visible", GObject.BindingFlags.SYNC_CREATE) # Switch into the installation mode if channels is already installing if self.handler.props.installing: self.on_installing_changed() else: # Check for updates if not self.handler.props.refreshing: self.handler.check() # Downloading? Enable the mode if self.handler.props.downloading: #self.on_downloading_changed() self.update_list.enable_downloading_mode()
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "BrightnessChanged": self.on_brightness_level_changed_external, } # Create unlockbar self.unlockbar = UnlockBar( "org.semplicelinux.vera.powermanager.modify-logind") self.objects.main.pack_start(self.unlockbar, False, False, 0) # Search for batteries for device in self.client.get_devices(): if device.props.is_present and device.props.power_supply and device.props.kind == Up.DeviceKind.BATTERY: # Found a power supply, and we'll show this in the UI. self.with_battery = device # We show only one battery, otherwise the UI will be a mess break # If there is a battery, bind properties to get live updates on the status if self.with_battery: # Show the battery frame self.objects.battery_frame.show() # Name self.objects.battery_name.set_text("%s %s" % (self.with_battery.props.vendor, self.with_battery.props.model)) # Percentage on charge_bar self.with_battery.bind_property( "percentage", self.objects.charge_bar, "value", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) # Percentage on label # Currently python-gi doesn't support bind_property_full(), which is # a shame because it's fantastic. # So we are using a connection here. percentage_callback = lambda x, y: self.objects.battery_percentage.set_text( "%s%%" % int(self.with_battery.props.percentage)) self.with_battery.connect("notify::percentage", percentage_callback) percentage_callback(None, None) # Status on label # Unforunately, same as above. status_callback = lambda x, y: self.objects.battery_status.set_text( "%s, " % BATTERY_STATE[self.with_battery.props.state]) self.with_battery.connect("notify::state", status_callback) status_callback(None, None) # Check for lid switch if os.path.exists("/proc/acpi/button/lid"): # That's a pretty dirty check self.objects.lid_switch_container.show() # Create cells for the comboboxes for combo in (self.objects.power_button_action, self.objects.lid_switch_action): cellrenderer = Gtk.CellRendererText() combo.pack_start(cellrenderer, True) combo.add_attribute(cellrenderer, "text", 1) combo.connect("changed", self.on_combobox_changed) # Disable "Buttons" frame when locked self.unlockbar.bind_property( "lock", self.objects.buttons_frame, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN)
def prepare_scene(self): """ Fired when doing the scene setup. """ self.scene_container = self.objects.main # Connect to the handler self.handler = UpdateHandler() # Convenience variable that houses the "installing" state. # Used when checking whether to continue following the apt log, # without flooding the DBus bus. self._installing = False # Determines if we are following the log or not self.following_log = False # APT_LOG follower (FIXME: should move it elsewhere) self.follower = None # Determines if the log has been fully written to the buffer self.log_written = False # Set appropriate font size and weight for the "Distribution upgrades" label context = self.objects.distribution_upgrade_label.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE*13) # Size self.objects.distribution_upgrade_label.override_font(desc) # Do the same for the "Application updates" label self.objects.application_updates_label.override_font(desc) # ...and for the "The software is up-to-date" one self.objects.application_updates_status.override_font(desc) # ..and for the error_description desc.set_size(Pango.SCALE*10) self.objects.error_description.override_font(desc) # ...and for the "Semplice is installing the updates" one desc.set_size(Pango.SCALE*20) self.objects.install_label.override_font(desc) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.channels.manage") self.objects.summary.pack_start(self.unlockbar, False, False, 0) # Set-up channel combobox renderer = Gtk.CellRendererText() self.objects.selected_channel.pack_start(renderer, True) self.objects.selected_channel.add_attribute(renderer, "text", 1) self.objects.selected_channel.add_attribute(renderer, "sensitive", 2) # Create update list self.update_list = UpdateList() self.objects.application_updates_scroll.add(self.update_list) #self.objects.application_updates_content.pack_start(self.update_list, True, True, 5) #self.update_list.prepend(Gtk.Label("HAAA")) #print(len(self.update_list), self.update_list.props.empty, self.update_list.props.selection_mode) self.update_list.show_all() # Open AppStream database # FIXME pending AppStream API update. See #4 #Database.open() #for item in ["glade", "pokerth", "banshee", "gnome-software"]: # self.update_list.add_item(-1, item, "XX", "upgrade", True) # FIXME self.objects.distribution_upgrade.hide() # Connect to status-toggled self.update_list.connect("status-toggled", self.on_status_toggled) # Connect to lock failed: self.handler.connect("lock-failed", self.on_lock_failed) # Connect to generic failure: self.handler.connect("generic-failure", self.on_generic_failure) # Connect to update found: self.handler.connect("update-found", self.on_update_found) # Connect to package-status-changed self.handler.connect("package-status-changed", self.on_package_status_changed) # Connect to package-fetch signals self.handler.connect("package-fetch-started", self.on_package_fetch_started) self.handler.connect("package-fetch-failed", self.on_package_fetch_failed) self.handler.connect("package-fetch-finished", self.on_package_fetch_finished) # React when checking changes self.handler.connect("notify::checking", self.on_checking_changed) # React when refreshing self.handler.connect("notify::refreshing", self.on_refreshing_changed) # React when downloading self.handler.connect("notify::downloading", self.on_downloading_changed) # React when installing self.handler.connect("notify::installing", self.on_installing_changed) # React when we know the total size to download self.handler.connect("notify::update-required-download", self.on_update_required_download_changed) # Ensure that the updates_frame is not sensitive when the settings # are locked... self.unlockbar.bind_property( "lock", self.objects.settings_frame, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN ) # Show the up-to-date container if the update list is empty self.update_list.bind_property( "empty", self.objects.application_updates_status, "visible", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "status-scene", self.objects.application_updates_status, "visible_child_name", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) #self.handler.bind_property( # "cache-operation", # self.objects.application_updates_checking_container, # "visible", # GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE #) #self.objects.application_updates_checking_container.bind_property( # "visible", # self.objects.software_uptodate_container, # "visible", # GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE #) self.update_list.bind_property( "empty", self.objects.application_updates_scroll, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.update_list.bind_property( "empty", self.objects.application_updates_actions, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Show download indicators when the cache is refreshing self.handler.bind_property( "cache-operation", self.objects.spinner, "active", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-operation", self.objects.download_rate, "visible", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-operation", self.objects.download_eta, "visible", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-rate", self.objects.download_rate, "label", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "download-eta", self.objects.download_eta, "label", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "download-current-item", self.objects.download_rate, "tooltip_text", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "cache-operation", self.objects.refresh_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "cache-operation", self.update_list, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "cache-operation", self.objects.application_updates_actions, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Remove sensitiveness on checkbuttons when downloading self.handler.bind_property( "downloading", self.update_list.package_checkbox, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Update labels in the action buttons accordingly to the current mode self.handler.bind_property( "download-operation-label", self.objects.download_button, "label", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "install-operation-label", self.objects.install_button, "label", GObject.BindingFlags.SYNC_CREATE ) # Update sensitiveness of the install_button when downloading self.handler.bind_property( "downloading", self.objects.install_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Update the installation_scene label self.handler.bind_property( "install-scene-label", self.objects.install_label, "label", GObject.BindingFlags.SYNC_CREATE ) # Show the log textview when "Show details" has been pressed self.objects.show_details_button.bind_property( "active", self.objects.details_scrolled, "visible", GObject.BindingFlags.SYNC_CREATE ) # Switch into the installation mode if channels is already installing if self.handler.props.installing: self.on_installing_changed() else: # Check for updates if not self.handler.props.refreshing: self.handler.check() # Downloading? Enable the mode if self.handler.props.downloading: #self.on_downloading_changed() self.update_list.enable_downloading_mode()
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "clicked": ( "change_fullname_button", "change_password_button", "change_groups_button", "delete_user_button", ), "response": ( "delete_user_dialog", "groups_dialog", ), "delete-event": ( "delete_user_dialog", "groups_dialog", ), "row_selected": ("user_list",), } on_edit_mode = False current_user = None current_user_properties = None current_user_box = None caller_user_box = None def handle_delete_events(self, window, event): """ Handles a dialog delete-event. """ return True on_delete_user_dialog_delete_event = on_groups_dialog_delete_event = handle_delete_events def on_locked(self, unlockbar): """ Fired when the Polkit auth has been revoked. """ # Switch again to the caller user self.objects.user_list.select_row(self.caller_user_box) def on_unlocked(self, unlockbar): """ Fired when the Polkit auth has been given. """ pass @quickstart.threads.on_idle def restore_edit_mode(self): """ Restores the full "edit" mode on the user details part. """ self.objects.fullname.show() self.objects.new_fullname.hide() fullname = self.current_user_properties.Get("(ss)", USER_IFACE, "Fullname" ) self.objects.fullname.set_markup(fullname if fullname else NO_FULLNAME_STRING) self.objects.change_fullname_button.set_image(self.objects.edit_image) self.on_edit_mode = False def on_change_fullname_button_clicked(self, button): """ Fired when the 'Edit fullname' button has been clicked. """ if not self.on_edit_mode: self.objects.fullname.hide() self.objects.new_fullname.show() self.objects.new_fullname.set_text( self.objects.fullname.get_text() if self.objects.fullname.get_text() != NO_FULLNAME_STRING.replace("<i>","").replace("</i>","") else "" ) self.objects.new_fullname.grab_focus() self.objects.change_fullname_button.set_image(self.objects.apply_image) self.on_edit_mode = True else: # Save changes self.current_user_properties.Set("(sss)", USER_IFACE, "Fullname", self.objects.new_fullname.get_text() ) self.current_user_box.set_username_and_fullname( self.current_user_box.user.get_text(), self.objects.new_fullname.get_text() ) self.restore_edit_mode() def on_change_password_button_clicked(self, button): """ Fired when the 'Click to change' button on the password field has been clicked. """ self.current_user.ChangePassword('(s)', os.environ["DISPLAY"]) def on_delete_user_button_clicked(self, button): """ Fired when the delete user button has been clicked. """ self.objects.delete_user_dialog.show() def on_delete_user_dialog_response(self, dialog, response_id): """ Fired when the delete user dialog got a response. """ if response_id == Gtk.ResponseType.OK: # Delete user # FIXME: error checking? self.current_user.DeleteUser("(b)", self.objects.delete_user_with_home.get_active()) # Reset delete_user_with_home self.objects.delete_user_with_home.set_active(False) dialog.hide() return False def on_change_groups_button_clicked(self, dialog): """ Fired when the change groups button has been clicked. """ self.objects.groups_store.clear() # Build group list user_in = self.GroupConfig.GetGroupsForUser('(s)', self.current_user_box._user) for group, name in self.GroupConfig.GetGroups().items(): name = name[0] if name in ("sudo",): # Skip continue self.objects.groups_store.insert_with_valuesv(-1, [0, 1, 2], [group, (name in user_in), name]) self.objects.groups_dialog.show() def on_groups_dialog_response(self, dialog, response_id): """ Fired when the groups dialog got a response. """ dialog.hide() return False def on_group_enabled_toggle_toggled(self, toggle, path): """ Fired when a toggle in the groups dialog has been... toggled. """ itr = self.objects.groups_store.get_iter(path) # Connect to group object group_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/group/%s" % self.objects.groups_store.get_value(itr, 0), "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Get current group members members = group_properties.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') if not toggle.get_active(): # Add user if not self.current_user_box._user in members: members.append(self.current_user_box._user) else: # Remove user if self.current_user_box._user in members: members.remove(self.current_user_box._user) # Set the new list try: group_properties.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) except: # Failed/Aborted return # Finally set the enabled boolean in the row self.objects.groups_store.set_value(itr, 1, not toggle.get_active()) @quickstart.threads.on_idle def build_user_list(self): """ Builds the user list. """ # Clear self.objects.user_list.foreach(lambda x: x.destroy()) # "Add new" self.objects.user_list.add(AddNewBox()) try: for uid, details in self.UsersConfig.GetUsers().items(): if uid < 1000 or uid == 65534: # Skip system groups or nobody continue box = Gtk.ListBoxRow() box.add(UserBox(uid, details[0], details[1], details[2])) box.show() self.objects.user_list.add(box) # Current user? if uid == CURRENT_UID: # Yes! self.caller_user_box = box self.objects.user_list.select_row(self.caller_user_box) except: # Unable to build user list, probably caused by a non-working DBus connection self.objects.main.set_sensitive(False) self.objects.content.hide() self.objects.unable_to_connect_warning.show() def on_user_list_row_selected(self, listbox, row): """ Fired when a user has been selected. """ if row == None: return userbox = row.get_child() if userbox.add_new: return self.UsersConfig.ShowUserCreationUI('(sas)', os.environ["DISPLAY"], DEFAULT_GROUPS) self.current_user_box = userbox # Caller users cannot remove themselves if row == self.caller_user_box: self.objects.delete_user_button.hide() else: self.objects.delete_user_button.show() # Obtain DBus object for the current user self.current_user = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, USER_IFACE, self.bus_cancellable ) self.current_user_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, "org.freedesktop.DBus.Properties", self.bus_cancellable ) self.objects.fullname.set_markup(userbox.fullname.get_text() if userbox.fullname.get_text() else NO_FULLNAME_STRING) self.objects.user.set_text(userbox.user.get_text()) # Administrator switch if self.SudoGroup: self.objects.administrator.set_active( (userbox.user.get_text() in self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members')) ) def determine_row_sorting(self, row1, row2): """ Used to sort the user list by UIDs. """ child1 = row1.get_child() child2 = row2.get_child() if child2.add_new: return 1 elif child1.add_new: return 0 elif child1.uid > child2.uid: return 1 else: return 0 def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "UserListChanged": self.build_user_list } # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN ) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.delete_user_dialog.add_buttons( _("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK ) self.objects.delete_user_dialog.get_widget_for_response(Gtk.ResponseType.OK).get_style_context().add_class("destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.groups_dialog.add_buttons( _("_Close"), Gtk.ResponseType.CLOSE ) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Enabled", self.group_enabled_toggle, active=1 ) ) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Group description", Gtk.CellRendererText(), text=2 ) ) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed) def on_administrator_changed(self, widget, param): """ Fired when the user wants to change the sudo membership of the selected user. """ members = self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') changed = False if widget.get_active(): # Add if not self.current_user_box._user in members: members.append(self.current_user_box._user) changed = True else: # Remove if self.current_user_box._user in members: members.remove(self.current_user_box._user) changed = True if changed: # Commit changes self.SudoGroup.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) def on_scene_called(self): """ Fired when the user wants to see this scene. """ # Recover from a failed connection state self.objects.main.set_sensitive(True) self.objects.content.show() self.objects.unable_to_connect_warning.hide() # We are locked! self.unlockbar.emit("locked") # Estabilish DBus connection self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.UsersConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.user", self.bus_cancellable ) self.UsersConfig.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal]() if signal in self.signal_handlers else None ) self.GroupConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.group", self.bus_cancellable ) # Estabilish DBus connection to the "sudo" group try: self.SudoGroup = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, self.GroupConfig.LookupGroup("(s)", "sudo"), "org.freedesktop.DBus.Properties", self.bus_cancellable ) except: self.SudoGroup = None if not self.SudoGroup: self.objects.administrator.set_sensitive(False) else: self.objects.administrator.set_sensitive(True) self.build_user_list()
def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "UserListChanged": self.build_user_list } # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN ) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.delete_user_dialog.add_buttons( _("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK ) self.objects.delete_user_dialog.get_widget_for_response(Gtk.ResponseType.OK).get_style_context().add_class("destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN ) self.objects.groups_dialog.add_buttons( _("_Close"), Gtk.ResponseType.CLOSE ) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Enabled", self.group_enabled_toggle, active=1 ) ) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn( "Group description", Gtk.CellRendererText(), text=2 ) ) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed)
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "clicked": ( "change_fullname_button", "change_password_button", "change_groups_button", "delete_user_button", ), "response": ( "delete_user_dialog", "groups_dialog", ), "delete-event": ( "delete_user_dialog", "groups_dialog", ), "row_selected": ("user_list", ), } on_edit_mode = False current_user = None current_user_properties = None current_user_box = None caller_user_box = None def handle_delete_events(self, window, event): """ Handles a dialog delete-event. """ return True on_delete_user_dialog_delete_event = on_groups_dialog_delete_event = handle_delete_events def on_locked(self, unlockbar): """ Fired when the Polkit auth has been revoked. """ # Switch again to the caller user self.objects.user_list.select_row(self.caller_user_box) def on_unlocked(self, unlockbar): """ Fired when the Polkit auth has been given. """ pass @quickstart.threads.on_idle def restore_edit_mode(self): """ Restores the full "edit" mode on the user details part. """ self.objects.fullname.show() self.objects.new_fullname.hide() fullname = self.current_user_properties.Get("(ss)", USER_IFACE, "Fullname") self.objects.fullname.set_markup( fullname if fullname else NO_FULLNAME_STRING) self.objects.change_fullname_button.set_image(self.objects.edit_image) self.on_edit_mode = False def on_change_fullname_button_clicked(self, button): """ Fired when the 'Edit fullname' button has been clicked. """ if not self.on_edit_mode: self.objects.fullname.hide() self.objects.new_fullname.show() self.objects.new_fullname.set_text( self.objects.fullname.get_text( ) if self.objects.fullname.get_text() != NO_FULLNAME_STRING. replace("<i>", "").replace("</i>", "") else "") self.objects.new_fullname.grab_focus() self.objects.change_fullname_button.set_image( self.objects.apply_image) self.on_edit_mode = True else: # Save changes self.current_user_properties.Set( "(sss)", USER_IFACE, "Fullname", self.objects.new_fullname.get_text()) self.current_user_box.set_username_and_fullname( self.current_user_box.user.get_text(), self.objects.new_fullname.get_text()) self.restore_edit_mode() def on_change_password_button_clicked(self, button): """ Fired when the 'Click to change' button on the password field has been clicked. """ self.current_user.ChangePassword('(s)', os.environ["DISPLAY"]) def on_delete_user_button_clicked(self, button): """ Fired when the delete user button has been clicked. """ self.objects.delete_user_dialog.show() def on_delete_user_dialog_response(self, dialog, response_id): """ Fired when the delete user dialog got a response. """ if response_id == Gtk.ResponseType.OK: # Delete user # FIXME: error checking? self.current_user.DeleteUser( "(b)", self.objects.delete_user_with_home.get_active()) # Reset delete_user_with_home self.objects.delete_user_with_home.set_active(False) dialog.hide() return False def on_change_groups_button_clicked(self, dialog): """ Fired when the change groups button has been clicked. """ self.objects.groups_store.clear() # Build group list user_in = self.GroupConfig.GetGroupsForUser( '(s)', self.current_user_box._user) for group, name in self.GroupConfig.GetGroups().items(): name = name[0] if name in ("sudo", ): # Skip continue self.objects.groups_store.insert_with_valuesv( -1, [0, 1, 2], [group, (name in user_in), name]) self.objects.groups_dialog.show() def on_groups_dialog_response(self, dialog, response_id): """ Fired when the groups dialog got a response. """ dialog.hide() return False def on_group_enabled_toggle_toggled(self, toggle, path): """ Fired when a toggle in the groups dialog has been... toggled. """ itr = self.objects.groups_store.get_iter(path) # Connect to group object group_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/group/%s" % self.objects.groups_store.get_value(itr, 0), "org.freedesktop.DBus.Properties", self.bus_cancellable) # Get current group members members = group_properties.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') if not toggle.get_active(): # Add user if not self.current_user_box._user in members: members.append(self.current_user_box._user) else: # Remove user if self.current_user_box._user in members: members.remove(self.current_user_box._user) # Set the new list try: group_properties.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) except: # Failed/Aborted return # Finally set the enabled boolean in the row self.objects.groups_store.set_value(itr, 1, not toggle.get_active()) @quickstart.threads.on_idle def build_user_list(self): """ Builds the user list. """ # Clear self.objects.user_list.foreach(lambda x: x.destroy()) # "Add new" self.objects.user_list.add(AddNewBox()) try: for uid, details in self.UsersConfig.GetUsers().items(): if uid < 1000 or uid == 65534: # Skip system groups or nobody continue box = Gtk.ListBoxRow() box.add(UserBox(uid, details[0], details[1], details[2])) box.show() self.objects.user_list.add(box) # Current user? if uid == CURRENT_UID: # Yes! self.caller_user_box = box self.objects.user_list.select_row(self.caller_user_box) except: # Unable to build user list, probably caused by a non-working DBus connection self.objects.main.set_sensitive(False) self.objects.content.hide() self.objects.unable_to_connect_warning.show() def on_user_list_row_selected(self, listbox, row): """ Fired when a user has been selected. """ if row == None: return userbox = row.get_child() if userbox.add_new: return self.UsersConfig.ShowUserCreationUI('(sas)', os.environ["DISPLAY"], DEFAULT_GROUPS) self.current_user_box = userbox # Caller users cannot remove themselves if row == self.caller_user_box: self.objects.delete_user_button.hide() else: self.objects.delete_user_button.show() # Obtain DBus object for the current user self.current_user = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, USER_IFACE, self.bus_cancellable) self.current_user_properties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd/user/%s" % userbox.uid, "org.freedesktop.DBus.Properties", self.bus_cancellable) self.objects.fullname.set_markup(userbox.fullname.get_text( ) if userbox.fullname.get_text() else NO_FULLNAME_STRING) self.objects.user.set_text(userbox.user.get_text()) # Administrator switch if self.SudoGroup: self.objects.administrator.set_active( (userbox.user.get_text() in self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members'))) def determine_row_sorting(self, row1, row2): """ Used to sort the user list by UIDs. """ child1 = row1.get_child() child2 = row2.get_child() if child2.add_new: return 1 elif child1.add_new: return 0 elif child1.uid > child2.uid: return 1 else: return 0 def prepare_scene(self): """ Fired when the module has just been loaded and we should setup things. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = {"UserListChanged": self.build_user_list} # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.usersd.manage") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) # Bind the locked state to the sensitiveness of the use_scrolled ScrolledWindow self.unlockbar.bind_property( "lock", self.objects.user_scrolled, "sensitive", GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN) # ListBox sort self.objects.user_list.set_sort_func(self.determine_row_sorting) # Delete user dialog self.objects.delete_user_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.delete_user_dialog.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete user"), Gtk.ResponseType.OK) self.objects.delete_user_dialog.get_widget_for_response( Gtk.ResponseType.OK).get_style_context().add_class( "destructive-action") # Groups dialog self.objects.groups_dialog.bind_property( "visible", self.objects.main, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.INVERT_BOOLEAN) self.objects.groups_dialog.add_buttons(_("_Close"), Gtk.ResponseType.CLOSE) self.group_enabled_toggle = Gtk.CellRendererToggle() self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Enabled", self.group_enabled_toggle, active=1)) self.group_enabled_toggle.connect("toggled", self.on_group_enabled_toggle_toggled) self.objects.groups_treeview.append_column( Gtk.TreeViewColumn("Group description", Gtk.CellRendererText(), text=2)) self.objects.groups_store.set_sort_column_id(0, Gtk.SortType.ASCENDING) self.objects.administrator.connect("notify::active", self.on_administrator_changed) def on_administrator_changed(self, widget, param): """ Fired when the user wants to change the sudo membership of the selected user. """ members = self.SudoGroup.Get('(ss)', 'org.semplicelinux.usersd.group', 'Members') changed = False if widget.get_active(): # Add if not self.current_user_box._user in members: members.append(self.current_user_box._user) changed = True else: # Remove if self.current_user_box._user in members: members.remove(self.current_user_box._user) changed = True if changed: # Commit changes self.SudoGroup.Set('(ssas)', 'org.semplicelinux.usersd.group', 'Members', members) def on_scene_called(self): """ Fired when the user wants to see this scene. """ # Recover from a failed connection state self.objects.main.set_sensitive(True) self.objects.content.show() self.objects.unable_to_connect_warning.hide() # We are locked! self.unlockbar.emit("locked") # Estabilish DBus connection self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.UsersConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.user", self.bus_cancellable) self.UsersConfig.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal] () if signal in self.signal_handlers else None) self.GroupConfig = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/usersd", "org.semplicelinux.usersd.group", self.bus_cancellable) # Estabilish DBus connection to the "sudo" group try: self.SudoGroup = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, self.GroupConfig.LookupGroup("(s)", "sudo"), "org.freedesktop.DBus.Properties", self.bus_cancellable) except: self.SudoGroup = None if not self.SudoGroup: self.objects.administrator.set_sensitive(False) else: self.objects.administrator.set_sensitive(True) self.build_user_list()
class Scene(quickstart.scenes.BaseScene): """ Time & Date settings. """ events = { "delete-event": ("select_timezone_dialog", ), "realize": ("select_timezone_dialog", ), "button-press-event": ("main", ), "clicked": ("time_button", "location_button", "apply_timezone"), "toggled": ("calendar_button", "ntp_enabled"), "value-changed": ("hours_adjustment", "minutes_adjustment", "seconds_adjustment"), "wrapped": ("hours", ), "output": ("hours", "minutes", "seconds") } @quickstart.threads.on_idle def build_timezone_list(self): """ Builds the timezone list. """ self.objects.timezones.clear() for item, key in self.TimeZone.supported.items(): for zone in key: zone1 = "%s/%s" % (item, zone) itr = self.objects.timezones.append([zone1, zone]) # Save the iter if it's the default if zone1 == self.TimeZone.default: # save the iter! ;) self.default = itr if self.default: sel = self.objects.timezone_treeview.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.timezone_treeview.scroll_to_cell, sel.get_selected_rows()[1][0]) GObject.idle_add(self.objects.timezone_treeview.grab_focus) def on_apply_timezone_clicked(self, button): """ Fired when the apply_timezone button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.hide) # Get selected sel = self.objects.timezone_treeview.get_selection() if not sel: return model, itr = sel.get_selected() if not itr: return selected = self.objects.timezones.get_value(itr, 0) if selected == self.objects.location_button.get_label(): return # Ugly! try: self.TimeDate.SetTimezone('(sb)', selected, True) self.default = itr self.current_datetime = datetime.datetime.now() except: sel.select_iter(self.default) self.objects.location_button.set_label( self.objects.timezones.get_value(self.default, 0)) def on_select_timezone_dialog_realize(self, widget): """ Fired when the select_timezone_dialog is going to be shown. """ self.build_timezone_list() def on_select_timezone_dialog_delete_event(self, widget, event): """ Fired when the select_timezone_dialog is going to be destroyed. """ widget.hide() return True # do not destroy def on_main_button_press_event(self, eventbox, event): """ Fired when the user has clicked on the eventbox. We use this call to return to the 'read-only' mode on the time view. """ if self.objects.time_modify.props.visible and event.type == Gdk.EventType.BUTTON_PRESS: self.refresh_infos() self.objects.time_modify.hide() self.objects.time_button.show() def on_spinbutton_output(self, spinbutton): """ Fired when a spinbutton has been changed. We use this method to ensure to have two digits everytime. Code comes from http://stackoverflow.com/a/9998968 (thanks!) (not connected to anything, due to a current limitation of quickstart we will associate on_*widget*_output to this method later) """ spinbutton.set_text('{:02d}'.format( int(spinbutton.get_adjustment().get_value()))) return True on_hours_output = on_spinbutton_output on_minutes_output = on_spinbutton_output on_seconds_output = on_spinbutton_output def on_hours_wrapped(self, spinbutton): """ Fired when the hours spinbutton has been wrapped. """ if self.timezone12: if int(spinbutton.get_text()) == 1: # +1 self.hour_offset = 0 if self.hour_offset == 12 else 12 else: # -1 self.hour_offset = 12 if self.hour_offset == 0 else 0 # Reset time self.on_hours_adjustment_value_changed(spinbutton.get_adjustment()) def set_time(self): """ Actually sets the time. """ print("Setting time...") self.TimeDate.SetTime( '(xbb)', time.mktime(self.current_datetime.timetuple()) * 1000000, False, False) def on_hours_adjustment_value_changed(self, adjustment): """ Fired when the hours adjustment has been changed. """ if self.hours_input: value = int(adjustment.get_value()) if self.timezone12: value += self.hour_offset self.current_datetime = self.current_datetime.replace( hour=value if not self.timezone12 else ( 0 if value == 24 else value)) if self.timezone12: self.objects.timezone12_edit.set_text( self.current_datetime.strftime("%p")) self.set_time() def on_minutes_adjustment_value_changed(self, adjustment): """ Fired when the minutes adjustment has been changed. """ if self.minutes_input: self.current_datetime = self.current_datetime.replace( minute=int(adjustment.get_value())) self.set_time() def on_seconds_adjustment_value_changed(self, adjustment): """ Fired when the seconds adjustment has been changed. """ if self.seconds_input: self.current_datetime = self.current_datetime.replace( second=int(adjustment.get_value())) self.set_time() def on_location_button_clicked(self, button): """ Fired when the location_button has been clicked. """ GObject.idle_add(self.objects.select_timezone_dialog.present) def on_time_button_clicked(self, button): """ Fired when the time_button has been clicked. """ self.hours_input = self.minutes_input = self.seconds_input = False button.hide() self.objects.time_modify.show() # Load the adjustments with current data time = self.current_datetime.time() # Handle 12-hour timezones if not self.timezone12: # 24 hour self.objects.hours_adjustment.set_value(time.hour) self.objects.timezone12_edit.hide() else: # 12 hour self.objects.hours_adjustment.set_value(int(time.strftime("%I"))) self.objects.timezone12_edit.set_text( self.current_datetime.strftime("%p")) self.objects.minutes_adjustment.set_value(time.minute) self.objects.seconds_adjustment.set_value(time.second) def on_calendar_button_toggled(self, button): """ Fired when the calendar_button has been toggled. """ if button.get_active(): self.calendar_popover.show_all() def on_day_selected(self, calendar): """ Fired when a day in the calendar has been selected. """ date = calendar.get_date() self.current_datetime = self.current_datetime.replace(year=date[0], month=date[1] + 1, day=date[2]) self.objects.calendar_button.set_label( self.current_datetime.date().strftime("%A %d %B %Y")) # Update time if we should if self.date_input: self.set_time() def on_ntp_enabled_toggled(self, checkbox): """ Fired when the ntp_enabled checkbox has been toggled. """ if self.unlockbar.current_state == ActionResponse.UNLOCK: print("Setting NTP to %s" % checkbox.get_active()) try: self.TimeDate.SetNTP('(bb)', checkbox.get_active(), False) except GLib.GError as error: # FIXME? Here it raises FileNotFound error, I think # is some Debian bug? pass # Set sensitivity on the manual container self.objects.manual_container.set_sensitive(not checkbox.get_active()) @quickstart.threads.on_idle def refresh_infos(self): """ Refreshes the information displayed. """ _datetime = datetime.datetime.now() self.objects.time.set_text(_datetime.time().strftime("%X")) @quickstart.threads.on_idle def update_date(self): """ Updates the date. """ date = self.current_datetime.date() self.date_input = False self.calendar.select_month(date.month - 1, date.year) self.calendar.select_day(date.day) self.date_input = True @quickstart.threads.on_idle def update_time(self): """ Updates the time. """ self.current_datetime += datetime.timedelta(seconds=1) time = self.current_datetime.time() if time.hour == 00 and time.minute == 00 and time.second == 00: # Day changed self.update_date() # Update the current visible time object if self.objects.time_modify.props.visible: if time.minute == 00 and time.second == 00: self.hours_input = False self.objects.hours_adjustment.set_value(time.hour) if time.second == 00: self.minutes_input = False self.objects.minutes_adjustment.set_value(time.minute) self.seconds_input = False self.objects.seconds_adjustment.set_value(time.second) self.hours_input = self.minutes_input = self.seconds_input = True else: self.objects.time.set_text(time.strftime("%X")) def on_locked(self, unlockbar): """ Fired when the user (or policykit) has locked the special temporary privilegies. """ # Locked, disable sensitivity on everything... self.objects.container.set_sensitive(False) def on_unlocked(self, unlockbar): """ Fired when the user has got the special temporary privilegies. """ # Unlocked, re-set sensitivity self.objects.container.set_sensitive(True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync(self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn("Timezone", Gtk.CellRendererText(), text=0)) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect( "closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE * 80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class( "no-borders") self.objects.calendar_button.get_style_context().add_class( "no-borders") def on_scene_called(self): """ Called when switching to this scene. We will handle here all timeouts to ensure we syncronize the time label with the actual time. """ self.objects.main.show_all() self.objects.time_modify.hide() # We are locked self.unlockbar.emit("locked") self.current_datetime = datetime.datetime.now() self.timezone12 = (not self.current_datetime.strftime("%p") == "") adj = self.objects.hours.get_adjustment() if self.timezone12: adj.set_upper(12) adj.set_lower(1) else: adj.set_upper(23) adj.set_lower(0) if self.current_datetime.time().hour > 12: self.hour_offset = 12 self.update_date() self.update_time() # NTP ntp = bool(self.TimeDateProperties.Get('(ss)', BUS_NAME, 'NTP')) self.objects.ntp_enabled.set_active(ntp) # Timezone self.objects.location_button.set_label( self.TimeDateProperties.Get('(ss)', BUS_NAME, 'Timezone')) self.label_timeout = GLib.timeout_add_seconds(1, self.update_time) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ GLib.source_remove(self.label_timeout) self.unlockbar.cancel_authorization() self.bus_cancellable.cancel() return True
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync(self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn("Timezone", Gtk.CellRendererText(), text=0)) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect( "closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE * 80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class( "no-borders") self.objects.calendar_button.get_style_context().add_class( "no-borders")
class Scene(quickstart.scenes.BaseScene): """ Keyboard settings. """ events = { "changed": ("model_combo", ), "cursor-changed": ("layout_view", "variant_view"), } def setxkbmap(self): """ Invokes setxkbmap and sets the currently selected layout, variant and model. """ subprocess.call([ "setxkbmap", self.Keyboard.default_layout, self.Keyboard.default_variant if self.Keyboard.default_variant else "", "-model" if self.Keyboard.default_model else "", self.Keyboard.default_model if self.Keyboard.default_model else "" ]) def on_model_combo_changed(self, combobox): """ Fired when the user changes the keyboard model. """ itr = self.objects.model_combo.get_active_iter() if not itr: return selected = self.objects.models.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(model=selected) self.default_model = itr self.setxkbmap() except: self.objects.model_combo.set_active_iter(self.default_model) def on_variant_view_cursor_changed(self, treeview): """ Fired when the user changes the variant. """ if self.building_list: return # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.variants.get_value(itr, 0) if selected == self.Keyboard.default_variant: return try: self.Keyboard.set(variant=selected) self.default_variant = itr self.setxkbmap() except: sel.select_iter(self.default_variant) def on_layout_view_cursor_changed(self, treeview): """ Fired when the user changes the locale. """ # selection sel = treeview.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.layouts.get_value(itr, 0) if selected == self.Keyboard.default_layout: return # Reset default variant self.default_variant = None try: self.Keyboard.set(layout=selected, variant='') self.default = itr self.setxkbmap() except: sel.select_iter(self.default) # Rebuild variant list GObject.idle_add(self.build_variant_list, selected) def build_variant_list(self, layout): """ Populates the variant list. """ self.building_list = True self.objects.variants.clear() models, layouts = self.Keyboard.supported() variants = layouts[layout]["variants"] for variant in variants: for item, key in variant.items(): itr = self.objects.variants.append([item, key]) # Save the default variant if item == self.Keyboard.default_variant: self.default_variant = itr # No variant reciter = self.objects.variants.prepend( ['', layouts[layout]["description"]]) sel = self.objects.variant_view.get_selection() if self.default_variant: sel.select_iter(self.default) else: # No variant, use reciter sel.select_iter(reciter) GObject.idle_add(self.objects.variant_view.scroll_to_cell, sel.get_selected_rows()[1][0]) self.building_list = False def build_model_list(self): """ Populates the model list. """ pc105 = None self.objects.models.clear() models = self.Keyboard.supported()[0] for item, key in models.items(): itr = self.objects.models.append([item, key]) # Save the default model if item == self.Keyboard.default_model: self.default_model = itr elif item == "pc105": # Fallback pc105 = itr if self.default_model: self.objects.model_combo.set_active_iter(self.default_model) else: self.objects.model_combo.set_active_iter(pc105) def build_layout_list(self): """ Populates the layout list. """ self.objects.layouts.clear() models, layouts = self.Keyboard.supported() for item, key in layouts.items(): itr = self.objects.layouts.append([item, key["description"]]) # Save iter if it's the default... if item == self.Keyboard.default_layout: self.default = itr if self.default: sel = self.objects.layout_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.layout_view.scroll_to_cell, sel.get_selected_rows()[1][0]) # Also build the variant view! GObject.idle_add(self.build_variant_list, self.Keyboard.default_layout) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.locale1.set-keyboard") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Keyboard = Keyboard() self.building_list = False self.default = None self.default_variant = None self.default_model = None # Make the layout_view treeview working... layout_renderer = Gtk.CellRendererText() self.layout_column = Gtk.TreeViewColumn("Layout", layout_renderer, text=1) self.objects.layouts.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.layout_view.append_column(self.layout_column) # Do the same for the variant_view... variant_renderer = Gtk.CellRendererText() self.variant_column = Gtk.TreeViewColumn("Variant", variant_renderer, text=1) self.objects.variants.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.variant_view.append_column(self.variant_column) # And something similar for the model combobox... model_renderer = Gtk.CellRendererText() self.objects.model_combo.pack_start(model_renderer, True) self.objects.model_combo.add_attribute(model_renderer, "text", 1) self.objects.models.set_sort_column_id(1, Gtk.SortType.ASCENDING) # Populate the layout list GObject.idle_add(self.build_layout_list) # Populate the model list GObject.idle_add(self.build_model_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() return True
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "changed" : ["selected_channel"], "toggled" : ["enable_proposed_updates", "enable_development_updates", "show_details_button"], "clicked" : ["refresh_button", "download_button", "install_button"], "realize" : ["install_scene"], "size-allocate" : ["details_textview"], } # Used to define the currently-enabled semplice-base channel. base_channel_enabled = None # Current variant current_variant = "current" building = False package_transactions = {} def on_scene_asked_to_close(self): """ Do some cleanup """ # If we are in the install scene, return to the main one, # but only if the installation has been completed if self.objects.main.get_visible_child() == self.objects.install_scene and not self.handler.props.installing: self.objects.main.set_visible_child(self.objects.summary) # Clear list self.update_list.clear() # Check for updates self.handler.check(force=True) return False # This is relatively safe self.bus_cancellable.cancel() return True def prepare_scene(self): """ Fired when doing the scene setup. """ self.scene_container = self.objects.main # Connect to the handler self.handler = UpdateHandler() # Convenience variable that houses the "installing" state. # Used when checking whether to continue following the apt log, # without flooding the DBus bus. self._installing = False # Determines if we are following the log or not self.following_log = False # APT_LOG follower (FIXME: should move it elsewhere) self.follower = None # Determines if the log has been fully written to the buffer self.log_written = False # Set appropriate font size and weight for the "Distribution upgrades" label context = self.objects.distribution_upgrade_label.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE*13) # Size self.objects.distribution_upgrade_label.override_font(desc) # Do the same for the "Application updates" label self.objects.application_updates_label.override_font(desc) # ...and for the "The software is up-to-date" one self.objects.application_updates_status.override_font(desc) # ..and for the error_description desc.set_size(Pango.SCALE*10) self.objects.error_description.override_font(desc) # ...and for the "Semplice is installing the updates" one desc.set_size(Pango.SCALE*20) self.objects.install_label.override_font(desc) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.channels.manage") self.objects.summary.pack_start(self.unlockbar, False, False, 0) # Set-up channel combobox renderer = Gtk.CellRendererText() self.objects.selected_channel.pack_start(renderer, True) self.objects.selected_channel.add_attribute(renderer, "text", 1) self.objects.selected_channel.add_attribute(renderer, "sensitive", 2) # Create update list self.update_list = UpdateList() self.objects.application_updates_scroll.add(self.update_list) #self.objects.application_updates_content.pack_start(self.update_list, True, True, 5) #self.update_list.prepend(Gtk.Label("HAAA")) #print(len(self.update_list), self.update_list.props.empty, self.update_list.props.selection_mode) self.update_list.show_all() # Open AppStream database # FIXME pending AppStream API update. See #4 #Database.open() #for item in ["glade", "pokerth", "banshee", "gnome-software"]: # self.update_list.add_item(-1, item, "XX", "upgrade", True) # FIXME self.objects.distribution_upgrade.hide() # Connect to status-toggled self.update_list.connect("status-toggled", self.on_status_toggled) # Connect to lock failed: self.handler.connect("lock-failed", self.on_lock_failed) # Connect to generic failure: self.handler.connect("generic-failure", self.on_generic_failure) # Connect to update found: self.handler.connect("update-found", self.on_update_found) # Connect to package-status-changed self.handler.connect("package-status-changed", self.on_package_status_changed) # Connect to package-fetch signals self.handler.connect("package-fetch-started", self.on_package_fetch_started) self.handler.connect("package-fetch-failed", self.on_package_fetch_failed) self.handler.connect("package-fetch-finished", self.on_package_fetch_finished) # React when checking changes self.handler.connect("notify::checking", self.on_checking_changed) # React when refreshing self.handler.connect("notify::refreshing", self.on_refreshing_changed) # React when downloading self.handler.connect("notify::downloading", self.on_downloading_changed) # React when installing self.handler.connect("notify::installing", self.on_installing_changed) # React when we know the total size to download self.handler.connect("notify::update-required-download", self.on_update_required_download_changed) # Ensure that the updates_frame is not sensitive when the settings # are locked... self.unlockbar.bind_property( "lock", self.objects.settings_frame, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN ) # Show the up-to-date container if the update list is empty self.update_list.bind_property( "empty", self.objects.application_updates_status, "visible", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "status-scene", self.objects.application_updates_status, "visible_child_name", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) #self.handler.bind_property( # "cache-operation", # self.objects.application_updates_checking_container, # "visible", # GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE #) #self.objects.application_updates_checking_container.bind_property( # "visible", # self.objects.software_uptodate_container, # "visible", # GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE #) self.update_list.bind_property( "empty", self.objects.application_updates_scroll, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.update_list.bind_property( "empty", self.objects.application_updates_actions, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Show download indicators when the cache is refreshing self.handler.bind_property( "cache-operation", self.objects.spinner, "active", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-operation", self.objects.download_rate, "visible", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-operation", self.objects.download_eta, "visible", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "download-rate", self.objects.download_rate, "label", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "download-eta", self.objects.download_eta, "label", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "download-current-item", self.objects.download_rate, "tooltip_text", GObject.BindingFlags.DEFAULT ) self.handler.bind_property( "cache-operation", self.objects.refresh_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "cache-operation", self.update_list, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "cache-operation", self.objects.application_updates_actions, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Remove sensitiveness on checkbuttons when downloading self.handler.bind_property( "downloading", self.update_list.package_checkbox, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Update labels in the action buttons accordingly to the current mode self.handler.bind_property( "download-operation-label", self.objects.download_button, "label", GObject.BindingFlags.SYNC_CREATE ) self.handler.bind_property( "install-operation-label", self.objects.install_button, "label", GObject.BindingFlags.SYNC_CREATE ) # Update sensitiveness of the install_button when downloading self.handler.bind_property( "downloading", self.objects.install_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) # Update the installation_scene label self.handler.bind_property( "install-scene-label", self.objects.install_label, "label", GObject.BindingFlags.SYNC_CREATE ) # Show the log textview when "Show details" has been pressed self.objects.show_details_button.bind_property( "active", self.objects.details_scrolled, "visible", GObject.BindingFlags.SYNC_CREATE ) # Switch into the installation mode if channels is already installing if self.handler.props.installing: self.on_installing_changed() else: # Check for updates if not self.handler.props.refreshing: self.handler.check() # Downloading? Enable the mode if self.handler.props.downloading: #self.on_downloading_changed() self.update_list.enable_downloading_mode() def on_install_scene_realize(self, widget): """ Fired when the install_progress is going to be realized. """ # Create a circular progress bar, and add it to the stack self.install_progress = CircularProgressBar() self.objects.progress_stack.add_named(self.install_progress, "progress") self.handler.bind_property( "install-progress", self.install_progress, "fraction", GObject.BindingFlags.SYNC_CREATE ) # Resize the CircularProgressBar so that it has the same requested # width and height of the details_scrolled width, height = self.objects.details_scrolled.get_size_request() self.install_progress.set_size_request(width, height) # Ensure that the CircularProgressBar stays visible when the # details textview is not self.objects.details_scrolled.bind_property( "visible", self.install_progress, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE ) #self.install_progress.show() #self.objects.show_details_button.set_active(True) def on_show_details_button_toggled(self, button): """ Fired when the show_details_button has been toggled. """ if button.props.active and not self.following_log and not self.log_written: self.follow_log() @quickstart.threads.on_idle def append_text(self, line): """ Appends the text to the details_buffer. """ self.objects.details_buffer.insert( self.objects.details_buffer.get_end_iter(), line ) @quickstart.threads.thread def follow_log(self): """ Follows the APT logfile """ self.following_log = True # Wait until it has been properly created while not os.path.exists(APT_LOGFILE) and self._installing: time.sleep(1) if self._installing: self.follower = Follower(APT_LOGFILE) for line in self.follower: Gdk.threads_enter() self.append_text(line) Gdk.threads_leave() else: # Not installed anymore, the upgrade process completed # So directly dump the entire file: with open(APT_LOGFILE) as f: for line in f: Gdk.threads_enter() self.append_text(line) Gdk.threads_leave() self.log_written = True self.following_log = False def on_details_textview_size_allocate(self, textview, allocation): """ Fired when the new text has been properly allocated. """ adj = self.objects.details_scrolled.get_vadjustment() adj.set_value(adj.props.upper - adj.props.page_size) def on_installing_changed(self, handler=None, value=None): """ Fired when the service began the package installation. """ # FIXME: Remove that once proper DBus proprieties caching # has been implemented self._installing = self.handler.props.installing # FIXME: Should move that elsewhere if not self._installing and self.following_log and self.follower: # Stop the follower print("Stopping follower") self.follower.stop() self.log_written = True self.follower = None if self._installing: # New installation, ensure that we are in a somewhat clean state self.log_written = False # Hide the details scrolled for now self.objects.details_scrolled.hide() # Finally, show the scene! self.scene_container.set_visible_child(self.objects.install_scene) def on_generic_failure(self, handler, error, description): """ Fired when the APT Lock failed. """ dialog = Gtk.MessageDialog( self.objects.main.get_toplevel(), Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, error ) dialog.set_title(_("Error")) dialog.format_secondary_text(description) dialog.run() dialog.destroy() def on_lock_failed(self, handler): """ Fired when the APT Lock failed. """ return self.on_generic_failure( handler, _("Unable to lock the APT database"), _("Please close the active package managers.") ) @quickstart.threads.on_idle def on_update_found(self, handler, id, name, version, reason, status, size): """ Fired when an update has been found. """ self.update_list.add_item(id, name, version, reason, status, size) @quickstart.threads.on_idle def on_package_status_changed(self, handler, id, reason): """ Fired when a package status has been changed. """ print("STATUS!") self.update_list.update_status(id, reason) def on_package_fetch_started(self, handler, transaction_id, description, shortdesc): """ Fired when a package is being fetched. """ if not shortdesc in self.update_list.names_with_id: # wat? return # Get package id from the UpdateList id = self.update_list.names_with_id[shortdesc] # Associate transaction with the id self.package_transactions[transaction_id] = id # Finally update the status on the list self.update_list.set_downloading(id, True) def on_package_fetch_failed(self, handler, transaction_id): """ Fired when a package failed to download. """ # FIXME: should notify the user! if not transaction_id in self.package_transactions: return self.update_list.set_downloading(self.package_transactions[transaction_id], False) def on_package_fetch_finished(self, handler, transaction_id): """ Fired when a package has been downloaded. """ if not transaction_id in self.package_transactions: return self.update_list.set_downloading(self.package_transactions[transaction_id], False) def on_downloading_changed(self, handler, value): """ Fired when handler's downloading property changed. """ if handler.props.downloading: self.update_list.enable_downloading_mode() else: self.update_list.disable_downloading_mode() self.package_transactions = {} def on_status_toggled(self, updatelist, id, reason): """ Fired when a package status changed locally. """ print(id, reason) self.handler.change_status(id, reason) def on_download_button_clicked(self, button): """ Fired when the download button has been clicked. """ if not self.handler.props.downloading: # Start fetching self.handler.fetch() else: # Stop fetching self.handler.fetch_stop() def on_install_button_clicked(self, button): """ Fired when the install button has been clicked. """ if not self.handler.props.downloading: # Start fetching self.handler.fetch(trigger_installation=True) def on_refresh_button_clicked(self, button): """ Fired when the refresh button has been clicked. """ # Clear list self.update_list.clear() self.handler.refresh() def on_checking_changed(self, handler, value): """ Fired when handler's checking property changed. """ print("Checking changed!") if not self.handler.props.checking: # Expand the update_list self.update_list.expand_all() def on_refreshing_changed(self, handler, value): """ Fired when handler's refreshing property changed. """ if not self.handler.props.refreshing: # Check for updates self.handler.check() def on_update_required_download_changed(self, handler, value): """ Fired when handler's update-required-download property changed. """ self.objects.download_size.show() self.objects.download_size.set_text( _("Total download size: %s") % self.handler.props.update_required_download ) def on_selected_channel_changed(self, combobox): """ Fired when the selected channel has been changed. """ if self.building: return new_channel = self.objects.semplice_base_channels.get_value(combobox.get_active_iter(), 0) self.Channels.Enable("(s)", new_channel) # Store the new choice self.base_channel_enabled = new_channel # Check for features self.check_for_features() def on_enable_proposed_updates_toggled(self, checkbutton): """ Fired when the Enable proposed updates checkbutton has been toggled. """ if self.building: return (self.Channels.EnableComponent if checkbutton.get_active() else self.Channels.DisableComponent)("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT) def on_enable_development_updates_toggled(self, checkbutton): """ Fired when the Enable development updates checkbutton has been toggled. """ if self.building: return (self.Channels.Enable if checkbutton.get_active() else self.Channels.Disable)("(s)", DEVELOPMENT_CHANNEL) @quickstart.threads.on_idle def check_for_features(self): """ "Enable proposed updates" and "Enable development updates" are available only on semplice-current. This method ensures that those checkbuttons are not sensitive if the selected base channel is not semplice-current and loads their settings if it is. """ self.building = True if self.base_channel_enabled == CURRENT_CHANNEL: self.objects.enable_development_updates.set_sensitive(True) if self.Channels.GetEnabled("(s)", DEVELOPMENT_CHANNEL): self.objects.enable_development_updates.set_active(True) else: self.objects.enable_development_updates.set_active(False) else: self.objects.enable_development_updates.set_sensitive(False) self.objects.enable_development_updates.set_active(False) # Check if the "proposed" channel is available if self.Channels.HasComponent("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT): self.objects.enable_proposed_updates.set_sensitive(True) if self.Channels.GetComponentEnabled("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT): self.objects.enable_proposed_updates.set_active(True) else: self.objects.enable_proposed_updates.set_active(False) else: self.objects.enable_proposed_updates.set_sensitive(False) self.objects.enable_proposed_updates.set_active(False) self.building = False @quickstart.threads.on_idle def load(self): """ Reloads the semplice_base_channels ListStore. """ self.building = True self.objects.semplice_base_channels.clear() for channel in self.Providers.WhatProvides("(s)", BASE_PROVIDER): details = self.Channels.GetDetails("(sas)", channel, ["name"]) itr = self.objects.semplice_base_channels.append( [ channel, details["name"] if "name" in details else channel, False if self.current_variant == "current" and channel != CURRENT_CHANNEL else True ] ) if self.Channels.GetEnabled("(s)", channel): self.objects.selected_channel.set_active_iter(itr) self.base_channel_enabled = channel self.check_for_features() # self.building will be restored by check_for_features() def on_scene_called(self): """ Fired when the scene has been called. """ # Load current variant if os.path.exists(VARIANT_FILE): with open(VARIANT_FILE, "r") as f: self.current_variant = f.read().strip() # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.Channels = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/channels/channels", "org.semplicelinux.channels.channels", self.bus_cancellable ) self.Providers = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/channels/providers", "org.semplicelinux.channels.providers", self.bus_cancellable ) # We are locked self.unlockbar.emit("locked") self.load()
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "toggled": ("show_all", "savespace_enable"), "cursor-changed": ("locale_view",), } @quickstart.threads.thread def set_locale(self, locale, sel, itr): """ Sets the given locale. """ try: self.Locale.set(locale) self.default = itr # Create stamp self.Locale.create_stamp([".alan2-locale-changed"]) GObject.idle_add(self.RebootDialog.show) except: sel.select_iter(self.default) GObject.idle_add(self.objects.region_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) @quickstart.threads.thread def savespace_purge(self, locale): """ Purges foreign locales. """ self.Locale.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.hide) GObject.idle_add(self.scene_container.set_sensitive, True) def on_locale_view_cursor_changed(self, locale_view): """ Fired when the user changes the locale. """ # selection sel = self.objects.locale_view.get_selection() if not sel: return # iter model, itr = sel.get_selected() if not itr: return selected = self.objects.locales.get_value(itr, 0) if selected == self.Locale.default: return if self.objects.savespace_enable.get_active(): # Display warning if self.objects.savespace_warning.run() == Gtk.ResponseType.NO: self.objects.savespace_warning.hide() sel.select_iter(self.default) return self.objects.savespace_enable.set_active(False) self.objects.savespace_warning.hide() GObject.idle_add(self.objects.region_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.set_locale(selected, sel, itr) def on_show_all_toggled(self, checkbutton): """ Fired when the 'Show all locales' checkbutton has been clicked. """ GObject.idle_add(self.build_locale_list, self.objects.show_all.get_active()) def on_savespace_enable_toggled(self, checkbutton): """ Fired when the 'Enable savespace' checkbutton has been clicked. """ locale = self.objects.locales.get_value(self.default, 0) if checkbutton.get_active(): self.Locale.savespace_enable(locale) # Purge window if self.objects.savespace_window.run() == Gtk.ResponseType.YES: # Purge!! self.savespace_purge(locale) GObject.idle_add(self.objects.other_spinner.show) GObject.idle_add(self.scene_container.set_sensitive, False) self.objects.savespace_window.hide() else: self.Locale.savespace_disable() def build_locale_list(self, all=False): """ Populates the listbox with locales. """ self.objects.locales.clear() for locale, human in self.Locale.human_form(all=all).items(): if all: codepage = self.Locale.codepages[locale] else: codepage = "" itr = self.objects.locales.append((locale, human, codepage)) # Save iter if this is the default... if locale == self.Locale.default: self.default = itr if self.default: sel = self.objects.locale_view.get_selection() sel.select_iter(self.default) GObject.idle_add(self.objects.locale_view.scroll_to_cell, sel.get_selected_rows()[1][0]) def on_locked(self, unlockbar): """ Fired when the scene has been locked. """ GObject.idle_add(self.objects.content.set_sensitive, False) def on_unlocked(self, unlockbar): """ Fired when the scene has been unlocked. """ GObject.idle_add(self.objects.content.set_sensitive, True) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # Check for savespace... if os.path.exists("/etc/dpkg/dpkg.cfg.d/keeptalking"): self.objects.savespace_enable.set_active(True) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.keeptalking2.change-locale") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main.pack_start(self.unlockbar, False, False, 0) self.Locale = Locale() self.default = None # Make the locale_view treeview working... locale_renderer = Gtk.CellRendererText() self.locale_column = Gtk.TreeViewColumn("Locale", locale_renderer, text=1) self.objects.locales.set_sort_column_id(1, Gtk.SortType.ASCENDING) self.objects.locale_view.append_column(self.locale_column) type_renderer = Gtk.CellRendererText() self.type_column = Gtk.TreeViewColumn("Type", type_renderer, text=2) self.objects.locale_view.append_column(self.type_column) # Populate the locale list GObject.idle_add(self.build_locale_list) def on_scene_called(self): """ Show the scene! """ # We are locked self.unlockbar.emit("locked") self.cancellable = Gio.Cancellable() self.RebootDialog = RebootDialog(self.cancellable) self.RebootDialog.bind_property( "visible", self.scene_container, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN ) def on_scene_asked_to_close(self): """ Do some cleanup before returning home """ self.unlockbar.cancel_authorization() self.cancellable.cancel() return True
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "BrightnessChanged" : self.on_brightness_level_changed_external, } # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.vera.powermanager.modify-logind") self.objects.main.pack_start(self.unlockbar, False, False, 0) # Search for batteries for device in self.client.get_devices(): if device.props.is_present and device.props.power_supply and device.props.kind == Up.DeviceKind.BATTERY: # Found a power supply, and we'll show this in the UI. self.with_battery = device # We show only one battery, otherwise the UI will be a mess break # If there is a battery, bind properties to get live updates on the status if self.with_battery: # Show the battery frame self.objects.battery_frame.show() # Name self.objects.battery_name.set_text( "%s %s" % ( self.with_battery.props.vendor, self.with_battery.props.model ) ) # Percentage on charge_bar self.with_battery.bind_property( "percentage", self.objects.charge_bar, "value", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) # Percentage on label # Currently python-gi doesn't support bind_property_full(), which is # a shame because it's fantastic. # So we are using a connection here. percentage_callback = lambda x, y: self.objects.battery_percentage.set_text("%s%%" % int(self.with_battery.props.percentage)) self.with_battery.connect( "notify::percentage", percentage_callback ) percentage_callback(None, None) # Status on label # Unforunately, same as above. status_callback = lambda x, y: self.objects.battery_status.set_text("%s, " % BATTERY_STATE[self.with_battery.props.state]) self.with_battery.connect( "notify::state", status_callback ) status_callback(None, None) # Check for lid switch if os.path.exists("/proc/acpi/button/lid"): # That's a pretty dirty check self.objects.lid_switch_container.show() # Create cells for the comboboxes for combo in (self.objects.power_button_action, self.objects.lid_switch_action): cellrenderer = Gtk.CellRendererText() combo.pack_start(cellrenderer, True) combo.add_attribute(cellrenderer, "text", 1) combo.connect("changed", self.on_combobox_changed) # Disable "Buttons" frame when locked self.unlockbar.bind_property( "lock", self.objects.buttons_frame, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN )
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "value-changed": ["brightness_scale"], } building = False client = Up.Client.new() with_battery = False @quickstart.threads.thread def set_value(self, obj, value): """ Sets the value """ obj('(sb)', value, True) def on_brightness_scale_value_changed(self, scale): """ Fired when the brightness level has been changed. """ if not self.building: self.VeraPowerManager.SetBrightness('(i)', int(scale.get_value()) + 1) def on_brightness_level_changed_external(self, params=None): """ Fired when the brightness level has been changed from the outside. """ if not params: params = (self.VeraPowerManager.GetBrightness(), ) self.building = True self.objects.brightness_level.set_value(float(params[0])) self.building = False def on_combobox_changed(self, combobox): """ Fired when a combobox has been changed. """ if self.building: return if combobox == self.objects.lid_switch_action: # Lid switch obj = self.VeraPowerManager.SetHandleLidSwitch elif combobox == self.objects.power_button_action: # Power button obj = self.VeraPowerManager.SetHandlePowerKey self.set_value(obj, ACTIONS[combobox.get_active()]) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "BrightnessChanged": self.on_brightness_level_changed_external, } # Create unlockbar self.unlockbar = UnlockBar( "org.semplicelinux.vera.powermanager.modify-logind") self.objects.main.pack_start(self.unlockbar, False, False, 0) # Search for batteries for device in self.client.get_devices(): if device.props.is_present and device.props.power_supply and device.props.kind == Up.DeviceKind.BATTERY: # Found a power supply, and we'll show this in the UI. self.with_battery = device # We show only one battery, otherwise the UI will be a mess break # If there is a battery, bind properties to get live updates on the status if self.with_battery: # Show the battery frame self.objects.battery_frame.show() # Name self.objects.battery_name.set_text("%s %s" % (self.with_battery.props.vendor, self.with_battery.props.model)) # Percentage on charge_bar self.with_battery.bind_property( "percentage", self.objects.charge_bar, "value", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) # Percentage on label # Currently python-gi doesn't support bind_property_full(), which is # a shame because it's fantastic. # So we are using a connection here. percentage_callback = lambda x, y: self.objects.battery_percentage.set_text( "%s%%" % int(self.with_battery.props.percentage)) self.with_battery.connect("notify::percentage", percentage_callback) percentage_callback(None, None) # Status on label # Unforunately, same as above. status_callback = lambda x, y: self.objects.battery_status.set_text( "%s, " % BATTERY_STATE[self.with_battery.props.state]) self.with_battery.connect("notify::state", status_callback) status_callback(None, None) # Check for lid switch if os.path.exists("/proc/acpi/button/lid"): # That's a pretty dirty check self.objects.lid_switch_container.show() # Create cells for the comboboxes for combo in (self.objects.power_button_action, self.objects.lid_switch_action): cellrenderer = Gtk.CellRendererText() combo.pack_start(cellrenderer, True) combo.add_attribute(cellrenderer, "text", 1) combo.connect("changed", self.on_combobox_changed) # Disable "Buttons" frame when locked self.unlockbar.bind_property( "lock", self.objects.buttons_frame, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN) def on_scene_called(self): """ Fired when the scene has been called. """ # Locked self.unlockbar.emit("locked") # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.VeraPowerManager = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/vera/powermanager", BUS_NAME, self.bus_cancellable) # connect signals self.VeraPowerManager.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal] (params) if signal in self.signal_handlers else None) # Check for backlight support if self.VeraPowerManager.IsBacklightSupported(): self.objects.display_frame.show() self.on_brightness_level_changed_external() else: self.objects.display_frame.hide() # Update comboboxes self.building = True self.objects.power_button_action.set_active( ACTIONS.index(self.VeraPowerManager.GetHandlePowerKey())) self.objects.lid_switch_action.set_active( ACTIONS.index(self.VeraPowerManager.GetHandleLidSwitch())) self.building = False def on_scene_asked_to_close(self): """ Fired when the scene has been asked to close. """ # Close dbus connection self.bus_cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "value-changed" : ["brightness_scale"], } building = False client = Up.Client.new() with_battery = False @quickstart.threads.thread def set_value(self, obj, value): """ Sets the value """ obj('(sb)', value, True) def on_brightness_scale_value_changed(self, scale): """ Fired when the brightness level has been changed. """ if not self.building: self.VeraPowerManager.SetBrightness('(i)', int(scale.get_value())+1) def on_brightness_level_changed_external(self, params=None): """ Fired when the brightness level has been changed from the outside. """ if not params: params = (self.VeraPowerManager.GetBrightness(),) self.building = True self.objects.brightness_level.set_value(float(params[0])) self.building = False def on_combobox_changed(self, combobox): """ Fired when a combobox has been changed. """ if self.building: return if combobox == self.objects.lid_switch_action: # Lid switch obj = self.VeraPowerManager.SetHandleLidSwitch elif combobox == self.objects.power_button_action: # Power button obj = self.VeraPowerManager.SetHandlePowerKey self.set_value(obj, ACTIONS[combobox.get_active()]) def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main # g-signals self.signal_handlers = { "BrightnessChanged" : self.on_brightness_level_changed_external, } # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.vera.powermanager.modify-logind") self.objects.main.pack_start(self.unlockbar, False, False, 0) # Search for batteries for device in self.client.get_devices(): if device.props.is_present and device.props.power_supply and device.props.kind == Up.DeviceKind.BATTERY: # Found a power supply, and we'll show this in the UI. self.with_battery = device # We show only one battery, otherwise the UI will be a mess break # If there is a battery, bind properties to get live updates on the status if self.with_battery: # Show the battery frame self.objects.battery_frame.show() # Name self.objects.battery_name.set_text( "%s %s" % ( self.with_battery.props.vendor, self.with_battery.props.model ) ) # Percentage on charge_bar self.with_battery.bind_property( "percentage", self.objects.charge_bar, "value", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE ) # Percentage on label # Currently python-gi doesn't support bind_property_full(), which is # a shame because it's fantastic. # So we are using a connection here. percentage_callback = lambda x, y: self.objects.battery_percentage.set_text("%s%%" % int(self.with_battery.props.percentage)) self.with_battery.connect( "notify::percentage", percentage_callback ) percentage_callback(None, None) # Status on label # Unforunately, same as above. status_callback = lambda x, y: self.objects.battery_status.set_text("%s, " % BATTERY_STATE[self.with_battery.props.state]) self.with_battery.connect( "notify::state", status_callback ) status_callback(None, None) # Check for lid switch if os.path.exists("/proc/acpi/button/lid"): # That's a pretty dirty check self.objects.lid_switch_container.show() # Create cells for the comboboxes for combo in (self.objects.power_button_action, self.objects.lid_switch_action): cellrenderer = Gtk.CellRendererText() combo.pack_start(cellrenderer, True) combo.add_attribute(cellrenderer, "text", 1) combo.connect("changed", self.on_combobox_changed) # Disable "Buttons" frame when locked self.unlockbar.bind_property( "lock", self.objects.buttons_frame, "sensitive", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN ) def on_scene_called(self): """ Fired when the scene has been called. """ # Locked self.unlockbar.emit("locked") # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.VeraPowerManager = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/vera/powermanager", BUS_NAME, self.bus_cancellable ) # connect signals self.VeraPowerManager.connect( "g-signal", lambda proxy, sender, signal, params: self.signal_handlers[signal](params) if signal in self.signal_handlers else None ) # Check for backlight support if self.VeraPowerManager.IsBacklightSupported(): self.objects.display_frame.show() self.on_brightness_level_changed_external() else: self.objects.display_frame.hide() # Update comboboxes self.building = True self.objects.power_button_action.set_active( ACTIONS.index(self.VeraPowerManager.GetHandlePowerKey()) ) self.objects.lid_switch_action.set_active( ACTIONS.index(self.VeraPowerManager.GetHandleLidSwitch()) ) self.building = False def on_scene_asked_to_close(self): """ Fired when the scene has been asked to close. """ # Close dbus connection self.bus_cancellable.cancel() return True
class Scene(quickstart.scenes.BaseScene): """ Desktop preferences. """ events = { "changed": ["selected_channel"], "toggled": [ "enable_proposed_updates", "enable_development_updates", "show_details_button" ], "clicked": ["refresh_button", "download_button", "install_button"], "realize": ["install_scene"], "size-allocate": ["details_textview"], } # Used to define the currently-enabled semplice-base channel. base_channel_enabled = None # Current variant current_variant = "current" building = False package_transactions = {} def on_scene_asked_to_close(self): """ Do some cleanup """ # If we are in the install scene, return to the main one, # but only if the installation has been completed if self.objects.main.get_visible_child( ) == self.objects.install_scene and not self.handler.props.installing: self.objects.main.set_visible_child(self.objects.summary) # Clear list self.update_list.clear() # Check for updates self.handler.check(force=True) return False # This is relatively safe self.bus_cancellable.cancel() return True def prepare_scene(self): """ Fired when doing the scene setup. """ self.scene_container = self.objects.main # Connect to the handler self.handler = UpdateHandler() # Convenience variable that houses the "installing" state. # Used when checking whether to continue following the apt log, # without flooding the DBus bus. self._installing = False # Determines if we are following the log or not self.following_log = False # APT_LOG follower (FIXME: should move it elsewhere) self.follower = None # Determines if the log has been fully written to the buffer self.log_written = False # Set appropriate font size and weight for the "Distribution upgrades" label context = self.objects.distribution_upgrade_label.create_pango_context( ) desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE * 13) # Size self.objects.distribution_upgrade_label.override_font(desc) # Do the same for the "Application updates" label self.objects.application_updates_label.override_font(desc) # ...and for the "The software is up-to-date" one self.objects.application_updates_status.override_font(desc) # ..and for the error_description desc.set_size(Pango.SCALE * 10) self.objects.error_description.override_font(desc) # ...and for the "Semplice is installing the updates" one desc.set_size(Pango.SCALE * 20) self.objects.install_label.override_font(desc) # Create unlockbar self.unlockbar = UnlockBar("org.semplicelinux.channels.manage") self.objects.summary.pack_start(self.unlockbar, False, False, 0) # Set-up channel combobox renderer = Gtk.CellRendererText() self.objects.selected_channel.pack_start(renderer, True) self.objects.selected_channel.add_attribute(renderer, "text", 1) self.objects.selected_channel.add_attribute(renderer, "sensitive", 2) # Create update list self.update_list = UpdateList() self.objects.application_updates_scroll.add(self.update_list) #self.objects.application_updates_content.pack_start(self.update_list, True, True, 5) #self.update_list.prepend(Gtk.Label("HAAA")) #print(len(self.update_list), self.update_list.props.empty, self.update_list.props.selection_mode) self.update_list.show_all() # Open AppStream database # FIXME pending AppStream API update. See #4 #Database.open() #for item in ["glade", "pokerth", "banshee", "gnome-software"]: # self.update_list.add_item(-1, item, "XX", "upgrade", True) # FIXME self.objects.distribution_upgrade.hide() # Connect to status-toggled self.update_list.connect("status-toggled", self.on_status_toggled) # Connect to lock failed: self.handler.connect("lock-failed", self.on_lock_failed) # Connect to generic failure: self.handler.connect("generic-failure", self.on_generic_failure) # Connect to update found: self.handler.connect("update-found", self.on_update_found) # Connect to package-status-changed self.handler.connect("package-status-changed", self.on_package_status_changed) # Connect to package-fetch signals self.handler.connect("package-fetch-started", self.on_package_fetch_started) self.handler.connect("package-fetch-failed", self.on_package_fetch_failed) self.handler.connect("package-fetch-finished", self.on_package_fetch_finished) # React when checking changes self.handler.connect("notify::checking", self.on_checking_changed) # React when refreshing self.handler.connect("notify::refreshing", self.on_refreshing_changed) # React when downloading self.handler.connect("notify::downloading", self.on_downloading_changed) # React when installing self.handler.connect("notify::installing", self.on_installing_changed) # React when we know the total size to download self.handler.connect("notify::update-required-download", self.on_update_required_download_changed) # Ensure that the updates_frame is not sensitive when the settings # are locked... self.unlockbar.bind_property("lock", self.objects.settings_frame, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN) # Show the up-to-date container if the update list is empty self.update_list.bind_property( "empty", self.objects.application_updates_status, "visible", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "status-scene", self.objects.application_updates_status, "visible_child_name", GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE) #self.handler.bind_property( # "cache-operation", # self.objects.application_updates_checking_container, # "visible", # GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE #) #self.objects.application_updates_checking_container.bind_property( # "visible", # self.objects.software_uptodate_container, # "visible", # GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE #) self.update_list.bind_property( "empty", self.objects.application_updates_scroll, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.update_list.bind_property( "empty", self.objects.application_updates_actions, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Show download indicators when the cache is refreshing self.handler.bind_property("cache-operation", self.objects.spinner, "active", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-operation", self.objects.download_rate, "visible", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-operation", self.objects.download_eta, "visible", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("download-rate", self.objects.download_rate, "label", GObject.BindingFlags.DEFAULT) self.handler.bind_property("download-eta", self.objects.download_eta, "label", GObject.BindingFlags.DEFAULT) self.handler.bind_property("download-current-item", self.objects.download_rate, "tooltip_text", GObject.BindingFlags.DEFAULT) self.handler.bind_property( "cache-operation", self.objects.refresh_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "cache-operation", self.update_list, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property( "cache-operation", self.objects.application_updates_actions, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Remove sensitiveness on checkbuttons when downloading self.handler.bind_property( "downloading", self.update_list.package_checkbox, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Update labels in the action buttons accordingly to the current mode self.handler.bind_property("download-operation-label", self.objects.download_button, "label", GObject.BindingFlags.SYNC_CREATE) self.handler.bind_property("install-operation-label", self.objects.install_button, "label", GObject.BindingFlags.SYNC_CREATE) # Update sensitiveness of the install_button when downloading self.handler.bind_property( "downloading", self.objects.install_button, "sensitive", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) # Update the installation_scene label self.handler.bind_property("install-scene-label", self.objects.install_label, "label", GObject.BindingFlags.SYNC_CREATE) # Show the log textview when "Show details" has been pressed self.objects.show_details_button.bind_property( "active", self.objects.details_scrolled, "visible", GObject.BindingFlags.SYNC_CREATE) # Switch into the installation mode if channels is already installing if self.handler.props.installing: self.on_installing_changed() else: # Check for updates if not self.handler.props.refreshing: self.handler.check() # Downloading? Enable the mode if self.handler.props.downloading: #self.on_downloading_changed() self.update_list.enable_downloading_mode() def on_install_scene_realize(self, widget): """ Fired when the install_progress is going to be realized. """ # Create a circular progress bar, and add it to the stack self.install_progress = CircularProgressBar() self.objects.progress_stack.add_named(self.install_progress, "progress") self.handler.bind_property("install-progress", self.install_progress, "fraction", GObject.BindingFlags.SYNC_CREATE) # Resize the CircularProgressBar so that it has the same requested # width and height of the details_scrolled width, height = self.objects.details_scrolled.get_size_request() self.install_progress.set_size_request(width, height) # Ensure that the CircularProgressBar stays visible when the # details textview is not self.objects.details_scrolled.bind_property( "visible", self.install_progress, "visible", GObject.BindingFlags.INVERT_BOOLEAN | GObject.BindingFlags.SYNC_CREATE) #self.install_progress.show() #self.objects.show_details_button.set_active(True) def on_show_details_button_toggled(self, button): """ Fired when the show_details_button has been toggled. """ if button.props.active and not self.following_log and not self.log_written: self.follow_log() @quickstart.threads.on_idle def append_text(self, line): """ Appends the text to the details_buffer. """ self.objects.details_buffer.insert( self.objects.details_buffer.get_end_iter(), line) @quickstart.threads.thread def follow_log(self): """ Follows the APT logfile """ self.following_log = True # Wait until it has been properly created while not os.path.exists(APT_LOGFILE) and self._installing: time.sleep(1) if self._installing: self.follower = Follower(APT_LOGFILE) for line in self.follower: Gdk.threads_enter() self.append_text(line) Gdk.threads_leave() else: # Not installed anymore, the upgrade process completed # So directly dump the entire file: with open(APT_LOGFILE) as f: for line in f: Gdk.threads_enter() self.append_text(line) Gdk.threads_leave() self.log_written = True self.following_log = False def on_details_textview_size_allocate(self, textview, allocation): """ Fired when the new text has been properly allocated. """ adj = self.objects.details_scrolled.get_vadjustment() adj.set_value(adj.props.upper - adj.props.page_size) def on_installing_changed(self, handler=None, value=None): """ Fired when the service began the package installation. """ # FIXME: Remove that once proper DBus proprieties caching # has been implemented self._installing = self.handler.props.installing # FIXME: Should move that elsewhere if not self._installing and self.following_log and self.follower: # Stop the follower print("Stopping follower") self.follower.stop() self.log_written = True self.follower = None if self._installing: # New installation, ensure that we are in a somewhat clean state self.log_written = False # Hide the details scrolled for now self.objects.details_scrolled.hide() # Finally, show the scene! self.scene_container.set_visible_child(self.objects.install_scene) def on_generic_failure(self, handler, error, description): """ Fired when the APT Lock failed. """ dialog = Gtk.MessageDialog( self.objects.main.get_toplevel(), Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, error) dialog.set_title(_("Error")) dialog.format_secondary_text(description) dialog.run() dialog.destroy() def on_lock_failed(self, handler): """ Fired when the APT Lock failed. """ return self.on_generic_failure( handler, _("Unable to lock the APT database"), _("Please close the active package managers.")) @quickstart.threads.on_idle def on_update_found(self, handler, id, name, version, reason, status, size): """ Fired when an update has been found. """ self.update_list.add_item(id, name, version, reason, status, size) @quickstart.threads.on_idle def on_package_status_changed(self, handler, id, reason): """ Fired when a package status has been changed. """ print("STATUS!") self.update_list.update_status(id, reason) def on_package_fetch_started(self, handler, transaction_id, description, shortdesc): """ Fired when a package is being fetched. """ if not shortdesc in self.update_list.names_with_id: # wat? return # Get package id from the UpdateList id = self.update_list.names_with_id[shortdesc] # Associate transaction with the id self.package_transactions[transaction_id] = id # Finally update the status on the list self.update_list.set_downloading(id, True) def on_package_fetch_failed(self, handler, transaction_id): """ Fired when a package failed to download. """ # FIXME: should notify the user! if not transaction_id in self.package_transactions: return self.update_list.set_downloading( self.package_transactions[transaction_id], False) def on_package_fetch_finished(self, handler, transaction_id): """ Fired when a package has been downloaded. """ if not transaction_id in self.package_transactions: return self.update_list.set_downloading( self.package_transactions[transaction_id], False) def on_downloading_changed(self, handler, value): """ Fired when handler's downloading property changed. """ if handler.props.downloading: self.update_list.enable_downloading_mode() else: self.update_list.disable_downloading_mode() self.package_transactions = {} def on_status_toggled(self, updatelist, id, reason): """ Fired when a package status changed locally. """ print(id, reason) self.handler.change_status(id, reason) def on_download_button_clicked(self, button): """ Fired when the download button has been clicked. """ if not self.handler.props.downloading: # Start fetching self.handler.fetch() else: # Stop fetching self.handler.fetch_stop() def on_install_button_clicked(self, button): """ Fired when the install button has been clicked. """ if not self.handler.props.downloading: # Start fetching self.handler.fetch(trigger_installation=True) def on_refresh_button_clicked(self, button): """ Fired when the refresh button has been clicked. """ # Clear list self.update_list.clear() self.handler.refresh() def on_checking_changed(self, handler, value): """ Fired when handler's checking property changed. """ print("Checking changed!") if not self.handler.props.checking: # Expand the update_list self.update_list.expand_all() def on_refreshing_changed(self, handler, value): """ Fired when handler's refreshing property changed. """ if not self.handler.props.refreshing: # Check for updates self.handler.check() def on_update_required_download_changed(self, handler, value): """ Fired when handler's update-required-download property changed. """ self.objects.download_size.show() self.objects.download_size.set_text( _("Total download size: %s") % self.handler.props.update_required_download) def on_selected_channel_changed(self, combobox): """ Fired when the selected channel has been changed. """ if self.building: return new_channel = self.objects.semplice_base_channels.get_value( combobox.get_active_iter(), 0) self.Channels.Enable("(s)", new_channel) # Store the new choice self.base_channel_enabled = new_channel # Check for features self.check_for_features() def on_enable_proposed_updates_toggled(self, checkbutton): """ Fired when the Enable proposed updates checkbutton has been toggled. """ if self.building: return (self.Channels.EnableComponent if checkbutton.get_active() else self.Channels.DisableComponent)("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT) def on_enable_development_updates_toggled(self, checkbutton): """ Fired when the Enable development updates checkbutton has been toggled. """ if self.building: return (self.Channels.Enable if checkbutton.get_active() else self.Channels.Disable)("(s)", DEVELOPMENT_CHANNEL) @quickstart.threads.on_idle def check_for_features(self): """ "Enable proposed updates" and "Enable development updates" are available only on semplice-current. This method ensures that those checkbuttons are not sensitive if the selected base channel is not semplice-current and loads their settings if it is. """ self.building = True if self.base_channel_enabled == CURRENT_CHANNEL: self.objects.enable_development_updates.set_sensitive(True) if self.Channels.GetEnabled("(s)", DEVELOPMENT_CHANNEL): self.objects.enable_development_updates.set_active(True) else: self.objects.enable_development_updates.set_active(False) else: self.objects.enable_development_updates.set_sensitive(False) self.objects.enable_development_updates.set_active(False) # Check if the "proposed" channel is available if self.Channels.HasComponent("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT): self.objects.enable_proposed_updates.set_sensitive(True) if self.Channels.GetComponentEnabled("(ss)", self.base_channel_enabled, PROPOSED_COMPONENT): self.objects.enable_proposed_updates.set_active(True) else: self.objects.enable_proposed_updates.set_active(False) else: self.objects.enable_proposed_updates.set_sensitive(False) self.objects.enable_proposed_updates.set_active(False) self.building = False @quickstart.threads.on_idle def load(self): """ Reloads the semplice_base_channels ListStore. """ self.building = True self.objects.semplice_base_channels.clear() for channel in self.Providers.WhatProvides("(s)", BASE_PROVIDER): details = self.Channels.GetDetails("(sas)", channel, ["name"]) itr = self.objects.semplice_base_channels.append([ channel, details["name"] if "name" in details else channel, False if self.current_variant == "current" and channel != CURRENT_CHANNEL else True ]) if self.Channels.GetEnabled("(s)", channel): self.objects.selected_channel.set_active_iter(itr) self.base_channel_enabled = channel self.check_for_features() # self.building will be restored by check_for_features() def on_scene_called(self): """ Fired when the scene has been called. """ # Load current variant if os.path.exists(VARIANT_FILE): with open(VARIANT_FILE, "r") as f: self.current_variant = f.read().strip() # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.Channels = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/channels/channels", "org.semplicelinux.channels.channels", self.bus_cancellable) self.Providers = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/semplicelinux/channels/providers", "org.semplicelinux.channels.providers", self.bus_cancellable) # We are locked self.unlockbar.emit("locked") self.load()
def prepare_scene(self): """ Called when doing the scene setup. """ self.scene_container = self.objects.main self.default = None self.TimeZone = TimeZone() self.hour_offset = 0 self.timezone12 = None # Create unlockbar self.unlockbar = UnlockBar("org.freedesktop.timedate1.set-time") self.unlockbar.connect("locked", self.on_locked) self.unlockbar.connect("unlocked", self.on_unlocked) self.objects.main_box.pack_start(self.unlockbar, False, False, 0) # Enter in the bus self.bus_cancellable = Gio.Cancellable() self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, self.bus_cancellable) self.TimeDate = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", BUS_NAME, self.bus_cancellable ) self.TimeDateProperties = Gio.DBusProxy.new_sync( self.bus, 0, None, BUS_NAME, "/org/freedesktop/timedate1", "org.freedesktop.DBus.Properties", self.bus_cancellable ) # Really we should create a new proxy to get the properties?! #self.refresh_infos() # Set-up select timezone dialog self.objects.timezone_treeview.append_column( Gtk.TreeViewColumn( "Timezone", Gtk.CellRendererText(), text=0 ) ) self.objects.timezones.set_sort_column_id(0, Gtk.SortType.ASCENDING) # Create calendar popover self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_day_selected) self.calendar_popover = Gtk.Popover.new(self.objects.calendar_button) self.calendar_popover.set_modal(True) self.calendar_popover.connect("closed", lambda x: self.objects.calendar_button.set_active(False)) self.calendar_popover.add(self.calendar) # Set appropriate font size and weight context = self.objects.time.create_pango_context() desc = context.get_font_description() desc.set_weight(Pango.Weight.LIGHT) # Weight desc.set_size(Pango.SCALE*80) # Size self.objects.time.override_font(desc) self.objects.hours.override_font(desc) self.objects.minutes.override_font(desc) self.objects.seconds.override_font(desc) self.objects.timezone12_edit.override_font(desc) # Set mask on the main eventbox self.objects.main.set_events(Gdk.EventMask.BUTTON_PRESS_MASK) # Styling self.objects.time_button.get_style_context().add_class("no-borders") self.objects.location_button.get_style_context().add_class("no-borders") self.objects.calendar_button.get_style_context().add_class("no-borders")