Pass through SystemExit (for Python 2.4)
[zeroinstall/zeroinstall-mseaborn.git] / zeroinstall / 0launch-gui / mainwindow.py
blobabf9cd221d0e38b24a72ec21c715d6e577d4b095
1 import gtk
2 from logging import warn
3 import os, sys
4 from zeroinstall import SafeException
5 from zeroinstall.support import tasks, pretty_size
6 from zeroinstall.injector import download
7 from iface_browser import InterfaceBrowser
8 import help_box
9 import dialog
11 tips = gtk.Tooltips()
13 SHOW_PREFERENCES = 0
15 class MainWindow:
16 progress = None
17 progress_area = None
18 browser = None
19 window = None
20 cancel_download_and_run = None
21 policy = None
23 def __init__(self, policy, widgets, download_only):
24 self.policy = policy
26 policy.watchers.append(lambda: self.window.set_response_sensitive(gtk.RESPONSE_OK, policy.solver.ready))
28 self.window = widgets.get_widget('main')
29 self.window.set_default_size(gtk.gdk.screen_width() * 2 / 5, 300)
31 self.progress = widgets.get_widget('progress')
32 self.progress_area = widgets.get_widget('progress_area')
34 widgets.get_widget('stop').connect('clicked', lambda b: policy.handler.abort_all_downloads())
36 cache = widgets.get_widget('show_cache')
37 cache.connect('clicked',
38 lambda b: os.spawnlp(os.P_WAIT, sys.argv[0], sys.argv[0], '-c'))
40 self.refresh_button = widgets.get_widget('refresh')
42 # Tree view
43 self.browser = InterfaceBrowser(policy, widgets)
45 prefs = widgets.get_widget('preferences')
46 self.window.action_area.set_child_secondary(prefs, True)
48 # Glade won't let me add this to the template!
49 if download_only:
50 run_button = dialog.MixedButton("_Download", gtk.STOCK_EXECUTE, button = gtk.ToggleButton())
51 else:
52 run_button = dialog.MixedButton("_Run", gtk.STOCK_EXECUTE, button = gtk.ToggleButton())
53 self.window.add_action_widget(run_button, gtk.RESPONSE_OK)
54 run_button.show_all()
55 run_button.set_flags(gtk.CAN_DEFAULT)
57 self.window.set_default_response(gtk.RESPONSE_OK)
58 self.window.default_widget.grab_focus()
60 def response(dialog, resp):
61 if resp in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT):
62 self.window.destroy()
63 sys.exit(1)
64 elif resp == gtk.RESPONSE_OK:
65 if self.cancel_download_and_run:
66 self.cancel_download_and_run.trigger()
67 if run_button.get_active():
68 self.cancel_download_and_run = tasks.Blocker("cancel downloads")
69 self.download_and_run(run_button, self.cancel_download_and_run)
70 elif resp == gtk.RESPONSE_HELP:
71 gui_help.display()
72 elif resp == SHOW_PREFERENCES:
73 import preferences
74 preferences.show_preferences(policy)
75 self.window.connect('response', response)
77 def destroy(self):
78 self.window.destroy()
80 def show(self):
81 self.window.show()
83 def set_response_sensitive(self, response, sensitive):
84 self.window.set_response_sensitive(response, sensitive)
86 @tasks.async
87 def download_and_run(self, run_button, cancelled):
88 try:
89 downloaded = self.policy.download_uncached_implementations()
91 if downloaded:
92 # We need to wait until everything is downloaded...
93 blockers = [downloaded, cancelled]
94 yield blockers
95 tasks.check(blockers)
97 if cancelled.happened:
98 return
100 if self.policy.get_uncached_implementations():
101 dialog.alert(self.window, 'Not all downloads succeeded; cannot run program.')
102 else:
103 from zeroinstall.injector import selections
104 sels = selections.Selections(self.policy)
105 doc = sels.toDOM()
106 reply = doc.toxml('utf-8')
107 sys.stdout.write(('Length:%8x\n' % len(reply)) + reply)
108 self.window.destroy()
109 sys.exit(0) # Success
110 except SafeException, ex:
111 run_button.set_active(False)
112 self.policy.handler.report_error(ex)
113 except SystemExit:
114 raise
115 except Exception, ex:
116 run_button.set_active(False)
117 import traceback
118 traceback.print_exc()
119 self.policy.handler.report_error(ex)
121 def update_download_status(self):
122 """Called at regular intervals while there are downloads in progress,
123 and once at the end. Update the display."""
124 monitored_downloads = self.policy.handler.monitored_downloads
126 self.browser.update_download_status()
128 if not monitored_downloads:
129 self.progress_area.hide()
130 return
132 self.progress_area.show()
134 any_known = False
135 done = total = self.policy.handler.total_bytes_downloaded # Completed downloads
136 n_downloads = self.policy.handler.n_completed_downloads
137 # Now add downloads in progress...
138 for x in monitored_downloads.values():
139 if x.status != download.download_fetching: continue
140 n_downloads += 1
141 if x.expected_size:
142 any_known = True
143 so_far = x.get_bytes_downloaded_so_far()
144 total += x.expected_size or max(4096, so_far) # Guess about 4K for feeds/icons
145 done += so_far
147 progress_text = '%s / %s' % (pretty_size(done), pretty_size(total))
148 if n_downloads == 1:
149 self.progress.set_text('Downloading one file (%s)' % progress_text)
150 else:
151 self.progress.set_text('Downloading %d files (%s)' % (n_downloads, progress_text))
153 if total == 0 or (n_downloads < 2 and not any_known):
154 self.progress.pulse()
155 else:
156 self.progress.set_fraction(float(done) / total)
158 gui_help = help_box.HelpBox("Injector Help",
159 ('Overview', """
160 A program is made up of many different components, typically written by different \
161 groups of people. Each component is available in multiple versions. The injector is \
162 used when starting a program. Its job is to decide which implementation of each required \
163 component to use.
165 An interface describes what a component does. The injector starts with \
166 the interface for the program you want to run (like 'The Gimp') and chooses an \
167 implementation (like 'The Gimp 2.2.0'). However, this implementation \
168 will in turn depend on other interfaces, such as 'GTK' (which draws the menus \
169 and buttons). Thus, the injector must choose implementations of \
170 each dependency (each of which may require further interfaces, and so on)."""),
172 ('List of interfaces', """
173 The main window displays all these interfaces, and the version of each chosen \
174 implementation. The top-most one represents the program you tried to run, and each direct \
175 child is a dependency. The 'Fetch' column shows the amount of data that needs to be \
176 downloaded, or '(cached)' if it is already on this computer.
178 If you are happy with the choices shown, click on the Download (or Run) button to \
179 download (and run) the program."""),
181 ('Choosing different versions', """
182 To control which implementations (versions) are chosen you can click on Preferences \
183 and adjust the network policy and the overall stability policy. These settings affect \
184 all programs run using Zero Install.
186 Alternatively, you can edit the policy of an individual interface by selecting it \
187 and clicking on the 'Interface Properties' button. \
188 See that dialog's help text for more information.
190 Right-click on an interface in the list for a menu.
191 """),
193 ('Reporting bugs', """
194 To report a bug, right-click over the interface which you think contains the problem \
195 and choose 'Report a Bug...' from the menu. If you don't know which one is the cause, \
196 choose the top one (i.e. the program itself). The program's author can reassign the \
197 bug if necessary, or switch to using a different version of the library.
198 """),
200 ('The cache', """
201 Each version of a program that is downloaded is stored in the Zero Install cache. This \
202 means that it won't need to be downloaded again each time you run the program. Click on \
203 the 'Show Cache' button to see what is currently in the cache, or to remove versions \
204 you no longer need to save disk space."""),