def __init__(self, driver, widgets, download_only, resolve, select_only=False): self.driver = driver self.select_only = select_only self.resolve = resolve def update_ok_state(): self.window.set_response_sensitive(gtk.RESPONSE_OK, driver.ready) if driver.ready and self.window.get_focus() is None: run_button.grab_focus() driver.watchers.append(update_ok_state) self.window = widgets.get_widget("main") self.window.set_default_size(gtk.gdk.screen_width() * 2 / 5, 300) self.progress = widgets.get_widget("progress") self.progress_area = widgets.get_widget("progress_area") self.comment = widgets.get_widget("comment") widgets.get_widget("stop").connect("clicked", lambda b: driver.config.handler.abort_all_downloads()) self.refresh_button = widgets.get_widget("refresh") # Tree view self.browser = InterfaceBrowser(driver, widgets) prefs = widgets.get_widget("preferences") self.window.get_action_area().set_child_secondary(prefs, True) # Glade won't let me add this to the template! if select_only: run_button = dialog.MixedButton(_("_Select"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) elif download_only: run_button = dialog.MixedButton(_("_Download"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) else: run_button = dialog.MixedButton(_("_Run"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) self.window.add_action_widget(run_button, gtk.RESPONSE_OK) run_button.show_all() if gtk.pygtk_version >= (2, 22, 0): run_button.set_can_default(True) else: run_button.set_flags(gtk.CAN_DEFAULT) self.run_button = run_button run_button.grab_focus() def response(dialog, resp): if resp in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT): self.driver.config.handler.abort_all_downloads() resolve("cancel") elif resp == gtk.RESPONSE_OK: self.driver.config.handler.abort_all_downloads() if run_button.get_active(): self.download_and_run(run_button) elif resp == gtk.RESPONSE_HELP: gui_help.display() elif resp == SHOW_PREFERENCES: from zeroinstall.gui import preferences, main preferences.show_preferences(driver.config, notify_cb=main.recalculate) self.window.connect("response", response) self.window.realize() # Make busy pointer work, even with --systray
class MainWindow(object): progress = None progress_area = None browser = None window = None driver = None comment = None systray_icon = None systray_icon_blocker = None def __init__(self, driver, widgets, download_only, resolve, select_only=False): self.driver = driver self.select_only = select_only self.resolve = resolve def update_ok_state(): self.window.set_response_sensitive(gtk.RESPONSE_OK, driver.ready) if driver.ready and self.window.get_focus() is None: run_button.grab_focus() driver.watchers.append(update_ok_state) self.window = widgets.get_widget("main") self.window.set_default_size(gtk.gdk.screen_width() * 2 / 5, 300) self.progress = widgets.get_widget("progress") self.progress_area = widgets.get_widget("progress_area") self.comment = widgets.get_widget("comment") widgets.get_widget("stop").connect("clicked", lambda b: driver.config.handler.abort_all_downloads()) self.refresh_button = widgets.get_widget("refresh") # Tree view self.browser = InterfaceBrowser(driver, widgets) prefs = widgets.get_widget("preferences") self.window.get_action_area().set_child_secondary(prefs, True) # Glade won't let me add this to the template! if select_only: run_button = dialog.MixedButton(_("_Select"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) elif download_only: run_button = dialog.MixedButton(_("_Download"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) else: run_button = dialog.MixedButton(_("_Run"), gtk.STOCK_EXECUTE, button=gtk.ToggleButton()) self.window.add_action_widget(run_button, gtk.RESPONSE_OK) run_button.show_all() if gtk.pygtk_version >= (2, 22, 0): run_button.set_can_default(True) else: run_button.set_flags(gtk.CAN_DEFAULT) self.run_button = run_button run_button.grab_focus() def response(dialog, resp): if resp in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT): self.driver.config.handler.abort_all_downloads() resolve("cancel") elif resp == gtk.RESPONSE_OK: self.driver.config.handler.abort_all_downloads() if run_button.get_active(): self.download_and_run(run_button) elif resp == gtk.RESPONSE_HELP: gui_help.display() elif resp == SHOW_PREFERENCES: from zeroinstall.gui import preferences, main preferences.show_preferences(driver.config, notify_cb=main.recalculate) self.window.connect("response", response) self.window.realize() # Make busy pointer work, even with --systray def destroy(self): self.window.destroy() def show(self): self.window.show() def set_response_sensitive(self, response, sensitive): self.window.set_response_sensitive(response, sensitive) @tasks.async def download_and_run(self, run_button): try: blocker = slave.download_archives() yield blocker tasks.check(blocker) if blocker.result == "aborted-by-user": run_button.set_active(False) # Don't bother reporting this to the user elif blocker.result == "ok": if not run_button.get_active(): return self.driver.config.handler.abort_all_downloads() self.resolve("ok") else: assert 0, blocker.result except SystemExit: raise except Exception as ex: run_button.set_active(False) self.report_exception(ex) def update_download_status(self, only_update_visible=False): """Called at regular intervals while there are downloads in progress, and once at the end. Update the display.""" if not self.window: return # (being destroyed) if not self.window.get_window(): return # (being destroyed) monitored_downloads = self.driver.config.handler.monitored_downloads self.browser.update_download_status(only_update_visible) if not monitored_downloads: self.progress_area.hide() self.window.get_window().set_cursor(None) return if not self.progress_area.get_property("visible"): self.progress_area.show() self.window.get_window().set_cursor(gtkutils.get_busy_pointer()) any_known = False done = total = self.driver.config.handler.total_bytes_downloaded # Completed downloads n_downloads = self.driver.config.handler.n_completed_downloads # Now add downloads in progress... for x in monitored_downloads: if x.status != download.download_fetching: continue n_downloads += 1 if x.expected_size: any_known = True so_far = x.get_bytes_downloaded_so_far() total += x.expected_size or max(4096, so_far) # Guess about 4K for feeds/icons done += so_far progress_text = "%s / %s" % (pretty_size(done), pretty_size(total)) self.progress.set_text( ngettext("Downloading one file (%(progress)s)", "Downloading %(number)d files (%(progress)s)", n_downloads) % {"progress": progress_text, "number": n_downloads} ) if total == 0 or (n_downloads < 2 and not any_known): self.progress.pulse() else: self.progress.set_fraction(float(done) / total) def set_message(self, message): import pango self.comment.set_text(message) attrs = pango.AttrList() try: attrs.insert(pango.AttrWeight(pango.WEIGHT_BOLD, end_index=len(message))) except AttributeError: pass # PyGtk 3 self.comment.set_attributes(attrs) self.comment.show() def use_systray_icon(self, root_iface): try: if sys.version_info[0] > 2: self.systray_icon = gtk.StatusIcon.new_from_icon_name("zeroinstall") else: self.systray_icon = gtk.status_icon_new_from_icon_name("zeroinstall") except Exception as ex: info(_("No system tray support: %s"), ex) else: self.systray_icon.set_tooltip(_("Checking for updates for %s") % root_iface.get_name()) self.systray_icon.connect("activate", self.remove_systray_icon) self.systray_icon_blocker = tasks.Blocker("Tray icon clicked") def remove_systray_icon(self, i=None): assert self.systray_icon, i self.show() self.systray_icon.set_visible(False) self.systray_icon = None self.systray_icon_blocker.trigger() self.systray_icon_blocker = None def report_exception(self, ex, tb=None): if not isinstance(ex, SafeException): if tb is None: warning(ex, exc_info=True) else: warning(ex, exc_info=(type(ex), ex, tb)) if isinstance(ex, AssertionError): # Assertions often don't say that they're errors (and are frequently # blank). ex = repr(ex) if self.systray_icon: if hasattr(self.systray_icon, "set_blinking"): self.systray_icon.set_blinking(True) self.systray_icon.set_tooltip(str(ex) + "\n" + _("(click for details)")) else: dialog.alert(self.window, str(ex) or repr(ex))