source: src/mlx/gui/update.py@ 84:40b2d74e74f4

Last change on this file since 84:40b2d74e74f4 was 38:08f7e6592452, checked in by István Váradi <ivaradi@…>, 13 years ago

The status icon is now hidden properly when the program quits, and made restarting nicer

File size: 10.9 KB
Line 
1# The GUI part of the update
2
3#-------------------------------------------------------------------------------
4
5from mlx.gui.common import *
6
7from mlx.update import update
8
9import mlx.const as const
10
11import threading
12import os
13import sys
14
15#-------------------------------------------------------------------------------
16
17class Updater(threading.Thread):
18 """The updater thread."""
19
20 # The removal or renaming of one file equals to the downloading of this
21 # many bytes in time (for the progress calculation)
22 REMOVE2BYTES = 100
23
24 _progressWindow = None
25
26 _progressLabel = None
27
28 _progressBar = None
29
30 _progressOKButton = None
31
32 _sudoDialog = None
33
34 @staticmethod
35 def _createGUI(parentWindow):
36 """Create the GUI elements, if needed."""
37 if Updater._progressWindow is not None:
38 return
39
40 Updater._progressWindow = window = gtk.Window()
41 window.set_title("MAVA Logger X " + const.VERSION + " Update")
42 window.set_transient_for(parentWindow)
43 #win.set_icon_from_file(os.path.join(iconDirectory, "logo.ico"))
44 window.set_size_request(400, -1)
45 window.set_resizable(False)
46 window.set_modal(True)
47 window.connect("delete-event", lambda a, b: True)
48 window.set_deletable(False)
49 window.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
50 else gtk.WIN_POS_CENTER_ON_PARENT)
51
52 mainAlignment = gtk.Alignment(xscale = 1.0)
53 mainAlignment.set_padding(padding_top = 4, padding_bottom = 10,
54 padding_left = 16, padding_right = 16)
55 window.add(mainAlignment)
56
57 mainVBox = gtk.VBox()
58 mainAlignment.add(mainVBox)
59
60 labelAlignment = gtk.Alignment(xalign = 0.0, xscale = 0.0)
61 Updater._progressLabel = progressLabel = gtk.Label()
62 labelAlignment.add(progressLabel)
63 mainVBox.pack_start(labelAlignment, True, True, 4)
64
65 Updater._progressBar = progressBar = gtk.ProgressBar()
66 mainVBox.pack_start(progressBar, True, True, 4)
67
68 buttonAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
69 Updater._progressOKButton = progressOKButton = gtk.Button("OK")
70 buttonAlignment.add(progressOKButton)
71 mainVBox.pack_start(buttonAlignment, True, True, 4)
72
73 Updater._sudoDialog = sudoDialog = \
74 gtk.Dialog("MAVA Logger X " + const.VERSION + " Update",
75 parentWindow,
76 gtk.DialogFlags.MODAL if pygobject else gtk.DIALOG_MODAL,
77 (gtk.STOCK_CANCEL, 0,
78 gtk.STOCK_OK, 1))
79
80 infoLabelAlignment = gtk.Alignment(xalign = 0.5, xscale = 0.1)
81 infoLabelAlignment.set_padding(padding_top = 4, padding_bottom = 10,
82 padding_left = 16, padding_right = 16)
83
84 infoLabel = gtk.Label("There is an update available, but the program cannot write\n"
85 "its directory due to insufficient privileges.\n\n"
86 "Click OK, if you want to run a helper program\n"
87 "with administrator privileges "
88 "to complete the update,\n"
89 "Cancel otherwise.")
90 infoLabel.set_justify(gtk.Justification.CENTER if pygobject
91 else gtk.JUSTIFY_CENTER)
92 infoLabelAlignment.add(infoLabel)
93 sudoDialog.vbox.pack_start(infoLabelAlignment, True, True, 4)
94
95 sudoDialog.set_position(gtk.WindowPosition.CENTER_ON_PARENT if pygobject
96 else gtk.WIN_POS_CENTER_ON_PARENT)
97
98 def __init__(self, gui, programDirectory, updateURL, parentWindow):
99 """Construct the updater. If not created yet, the windows used by the
100 updater are also created."""
101 super(Updater, self).__init__()
102
103 self._gui = gui
104
105 self._programDirectory = programDirectory
106 self._updateURL = updateURL
107
108 self._totalProgress = 0
109 self._waitAfterFinish = False
110 self._restart = False
111
112 self._sudoReply = None
113 self._sudoCondition = threading.Condition()
114
115 Updater._createGUI(parentWindow)
116
117 def run(self):
118 """Execute the thread's operation."""
119 gobject.idle_add(self._startUpdate)
120 update(self._programDirectory, self._updateURL, self, fromGUI = True)
121 if not self._waitAfterFinish:
122 gobject.idle_add(self._progressWindow.hide)
123
124 def downloadingManifest(self):
125 """Called when the downloading of the manifest has started."""
126 gobject.idle_add(self._downloadingManifest)
127
128 def _downloadingManifest(self):
129 """Called when the downloading of the manifest has started."""
130 self._progressLabel.set_text("Downloading manifest...")
131 self._progressBar.set_fraction(0)
132
133 def downloadedManifest(self):
134 """Called when the downloading of the manifest has finished."""
135 gobject.idle_add(self._downloadedManifest)
136
137 def _downloadedManifest(self):
138 """Called when the downloading of the manifest has finished."""
139 self._progressLabel.set_text("Downloaded manifest...")
140 self._progressBar.set_fraction(0.05)
141
142 def needSudo(self):
143 """Called when the program is interested in whether we want to run a
144 program with administrator rights to do the update."""
145 gobject.idle_add(self._needSudo)
146 with self._sudoCondition:
147 while self._sudoReply is None:
148 self._sudoCondition.wait(1)
149 return self._sudoReply
150
151 def _needSudo(self):
152 """Called when the program is interested in whether we want to run a
153 program with administrator rights to do the update."""
154 self._sudoDialog.show_all()
155 result = self._sudoDialog.run()
156 self._sudoDialog.hide()
157 with self._sudoCondition:
158 self._sudoReply = result!=0
159 self._sudoCondition.notify()
160
161 def setTotalSize(self, numToModifyAndNew, totalSize, numToRemove,
162 numToRemoveLocal):
163 """Called when the downloading of the files has started."""
164 self._numToModifyAndNew = numToModifyAndNew
165 self._numModifiedOrNew = 0
166 self._totalSize = totalSize
167 self._downloaded = 0
168 self._numToRemove = numToRemove
169 self._numToRemoveLocal = numToRemoveLocal
170 self._numRemoved = 0
171
172 self._totalProgress = self._totalSize + \
173 (self._numToModifyAndNew + \
174 self._numToRemove + self._numToRemoveLocal) * \
175 Updater.REMOVE2BYTES
176 self._waitAfterFinish = self._totalSize > 0 or self._numToRemove > 0
177
178 def _startDownload(self):
179 """Called when the download has started."""
180 self._progressLabel.set_text("Downloading files...")
181
182 def setDownloaded(self, downloaded):
183 """Called when a certain number of bytes are downloaded."""
184 self._downloaded = downloaded
185 gobject.idle_add(self._setDownloaded, downloaded)
186
187 def _setDownloaded(self, downloaded):
188 """Called when a certain number of bytes are downloaded."""
189 self._progressLabel.set_text("Downloaded %d of %d" % \
190 (downloaded, self._totalSize))
191 self._setProgress()
192
193 def startRenaming(self):
194 """Called when the renaming of files has started."""
195 gobject.idle_add(self._startRenaming)
196
197 def _startRenaming(self):
198 """Called when the renaming of files has started."""
199 self._progressLabel.set_text("Renaming downloaded files...")
200
201 def renamed(self, path, count):
202 """Called when a file has been renamed."""
203 self._numModifiedOrNew = count
204 gobject.idle_add(self._renamed, path, count)
205
206 def _renamed(self, path, count):
207 """Called when a file has been renamed."""
208 self._progressLabel.set_text("Renamed %s" % (path,))
209 self._setProgress()
210
211 def startRemoving(self):
212 """Called when the removing of files has started."""
213 gobject.idle_add(self._startRemoving)
214
215 def _startRemoving(self):
216 """Called when the removing of files has started."""
217 self._progressLabel.set_text("Removing files...")
218
219 def removed(self, path, count):
220 """Called when a file has been removed."""
221 self._numRemoved = count
222 gobject.idle_add(self._removed, path, count)
223
224 def _removed(self, path, count):
225 """Called when a file has been removed."""
226 self._progressLabel.set_text("Removed %s" % (path,))
227 self._setProgress()
228
229 def writingManifest(self):
230 """Called when the writing of the new manifest file has started."""
231 gobject.idle_add(self._writingManifest)
232
233 def _writingManifest(self):
234 """Called when the writing of the new manifest file has started."""
235 self._progressLabel.set_text("Writing the new manifest")
236
237 def done(self):
238 """Called when the update has been done."""
239 gobject.idle_add(self._done)
240 self._restart = self._waitAfterFinish
241
242 def _done(self):
243 """Called when the writing of the new manifest file has started."""
244 self._progressBar.set_fraction(1)
245 if self._totalProgress>0:
246 self._progressLabel.set_text("Finished updating. Press OK to restart the program.")
247 self._progressOKButton.set_sensitive(True)
248 else:
249 self._progressLabel.set_text("There was nothing to update")
250
251 def _setProgress(self):
252 """Set the progress bar based on the current stage."""
253 if self._totalProgress>0:
254 progress = self._downloaded + \
255 (self._numModifiedOrNew + self._numRemoved) * \
256 Updater.REMOVE2BYTES
257 self._progressBar.set_fraction(0.05 + 0.94 * progress / self._totalProgress)
258
259 def failed(self, what):
260 """Called when the downloading has failed."""
261 self._waitAfterFinish = True
262 gobject.idle_add(self._failed, what)
263
264 def _failed(self, what):
265 """Called when the downloading has failed."""
266 self._progressLabel.set_text("Failed, see the debug log for details.")
267 self._progressBar.set_fraction(1)
268 self._progressOKButton.set_sensitive(True)
269
270 def _startUpdate(self):
271 """Start the update.
272
273 It resets the GUI elements."""
274 self._progressLabel.set_text("")
275 self._progressBar.set_fraction(0)
276 self._progressWindow.show_all()
277 self._progressOKButton.set_sensitive(False)
278 self._progressOKButton.connect("clicked", self._progressOKClicked)
279
280 def _progressOKClicked(self, button):
281 """Called when the OK button on the progress window is clicked."""
282 self._progressWindow.hide()
283 if self._restart:
284 self._gui.restart()
285
286#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.