Added "0install apps"
[zeroinstall.git] / zeroinstall / 0launch-gui / main.py
blob487f7cf793e40edb89062b9f3e2bc7e2dc1193b8
1 # Copyright (C) 2009, Thomas Leonard
2 # See the README file for details, or visit http://0install.net.
4 from __future__ import print_function
6 import os, sys
7 import logging
8 import warnings
10 from optparse import OptionParser
12 from zeroinstall import _, SafeException
13 from zeroinstall.injector import requirements
14 from zeroinstall.injector.driver import Driver
15 from zeroinstall.injector.config import load_config
16 from zeroinstall.support import tasks
18 _recalculate = tasks.Blocker('recalculate')
20 def recalculate():
21 """Ask the mainloop to recalculate. If we're already recalculating, wait for that to finish
22 and then do it again."""
23 global _recalculate
24 _recalculate.trigger()
25 _recalculate = tasks.Blocker('recalculate')
27 def run_gui(args):
28 parser = OptionParser(usage=_("usage: %prog [options] interface"))
29 parser.add_option("", "--apps", help=_("show app list"), action='store_true')
30 parser.add_option("", "--before", help=_("choose a version before this"), metavar='VERSION')
31 parser.add_option("", "--cpu", help=_("target CPU type"), metavar='CPU')
32 parser.add_option("", "--command", help=_("command to select"), metavar='COMMAND')
33 parser.add_option("-d", "--download-only", help=_("fetch but don't run"), action='store_true')
34 parser.add_option("-g", "--force-gui", help=_("display an error if there's no GUI"), action='store_true')
35 parser.add_option("", "--message", help=_("message to display when interacting with user"))
36 parser.add_option("", "--not-before", help=_("minimum version to choose"), metavar='VERSION')
37 parser.add_option("", "--os", help=_("target operation system type"), metavar='OS')
38 parser.add_option("-r", "--refresh", help=_("check for updates of all interfaces"), action='store_true')
39 parser.add_option("", "--select-only", help=_("only download the feeds"), action='store_true')
40 parser.add_option("-s", "--source", help=_("select source code"), action='store_true')
41 parser.add_option("", "--systray", help=_("download in the background"), action='store_true')
42 parser.add_option("-v", "--verbose", help=_("more verbose output"), action='count')
43 parser.add_option("-V", "--version", help=_("display version information"), action='store_true')
44 parser.add_option("", "--with-store", help=_("add an implementation cache"), action='append', metavar='DIR')
46 parser.disable_interspersed_args()
48 (options, args) = parser.parse_args(args)
50 if options.verbose:
51 logger = logging.getLogger()
52 if options.verbose == 1:
53 logger.setLevel(logging.INFO)
54 else:
55 logger.setLevel(logging.DEBUG)
57 if options.version:
58 print("0launch-gui (zero-install) " + gui.version)
59 print("Copyright (C) 2010 Thomas Leonard")
60 print(_("This program comes with ABSOLUTELY NO WARRANTY,"
61 "\nto the extent permitted by law."
62 "\nYou may redistribute copies of this program"
63 "\nunder the terms of the GNU Lesser General Public License."
64 "\nFor more information about these matters, see the file named COPYING."))
65 sys.exit(0)
67 def nogui(ex):
68 if options.force_gui:
69 fn = logging.warn
70 else:
71 fn = logging.info
72 fn("No GUI available", exc_info = ex)
73 sys.exit(100)
75 with warnings.catch_warnings():
76 if not options.force_gui:
77 warnings.filterwarnings("ignore")
78 if sys.version_info[0] < 3:
79 try:
80 import pygtk; pygtk.require('2.0')
81 except ImportError as ex:
82 nogui(ex)
84 import gui
86 try:
87 if sys.version_info[0] > 2:
88 from zeroinstall.gtkui import pygtkcompat
89 pygtkcompat.enable()
90 pygtkcompat.enable_gtk(version = '3.0')
91 import gtk
92 except (ImportError, ValueError) as ex:
93 nogui(ex)
95 if gtk.gdk.get_display() is None:
96 try:
97 raise SafeException("Failed to connect to display.")
98 except SafeException as ex:
99 nogui(ex) # logging needs this as a raised exception
101 if options.apps:
102 from zeroinstall.gtkui.applistbox import AppListBox, AppList
103 from zeroinstall.injector.iface_cache import iface_cache
104 box = AppListBox(iface_cache, AppList())
105 done = tasks.Blocker('close app list')
106 box.window.connect('destroy', lambda w: done.trigger())
107 box.window.show()
108 tasks.wait_for_blocker(done)
109 sys.exit(0)
111 handler = gui.GUIHandler()
113 config = load_config(handler)
115 if options.with_store:
116 from zeroinstall import zerostore
117 for x in options.with_store:
118 config.stores.stores.append(zerostore.Store(os.path.abspath(x)))
120 if len(args) < 1:
121 @tasks.async
122 def prefs_main():
123 import preferences
124 box = preferences.show_preferences(config)
125 done = tasks.Blocker('close preferences')
126 box.connect('destroy', lambda w: done.trigger())
127 yield done
128 tasks.wait_for_blocker(prefs_main())
129 sys.exit(0)
131 interface_uri = args[0]
133 if len(args) > 1:
134 parser.print_help()
135 sys.exit(1)
137 import mainwindow, dialog
139 r = requirements.Requirements(interface_uri)
140 r.parse_options(options)
142 widgets = dialog.Template('main')
144 driver = Driver(config = config, requirements = r)
145 root_iface = config.iface_cache.get_interface(interface_uri)
146 driver.solver.record_details = True
148 window = mainwindow.MainWindow(driver, widgets, download_only = bool(options.download_only), select_only = bool(options.select_only))
149 handler.mainwindow = window
151 if options.message:
152 window.set_message(options.message)
154 root = config.iface_cache.get_interface(r.interface_uri)
155 window.browser.set_root(root)
157 window.window.connect('destroy', lambda w: handler.abort_all_downloads())
159 if options.systray:
160 window.use_systray_icon()
162 @tasks.async
163 def main():
164 force_refresh = bool(options.refresh)
165 while True:
166 window.refresh_button.set_sensitive(False)
167 window.browser.set_update_icons(force_refresh)
169 solved = driver.solve_with_downloads(force = force_refresh, update_local = True)
171 if not window.systray_icon:
172 window.show()
173 yield solved
174 try:
175 window.refresh_button.set_sensitive(True)
176 window.browser.highlight_problems()
177 tasks.check(solved)
178 except Exception as ex:
179 window.report_exception(ex)
181 if window.systray_icon and window.systray_icon.get_visible() and \
182 window.systray_icon.is_embedded():
183 if driver.solver.ready:
184 window.systray_icon.set_tooltip(_('Downloading updates for %s') % root_iface.get_name())
185 window.run_button.set_active(True)
186 else:
187 # Should already be reporting an error, but
188 # blink it again just in case
189 window.systray_icon.set_blinking(True)
191 refresh_clicked = dialog.ButtonClickedBlocker(window.refresh_button)
192 yield refresh_clicked, _recalculate
194 if refresh_clicked.happened:
195 force_refresh = True
197 tasks.wait_for_blocker(main())