1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 A cross-platform graphical interface for the LiveUSBCreator
24 """
25
26 import os
27 import logging
28
29 from time import sleep
30 from datetime import datetime
31 from PyQt4 import QtCore, QtGui
32
33 from liveusb import LiveUSBCreator, LiveUSBError, LiveUSBInterface, _
34 from liveusb.releases import releases
35 from liveusb.urlgrabber.grabber import URLGrabber, URLGrabError
36 from liveusb.urlgrabber.progress import BaseMeter
37
38 try:
39 import dbus.mainloop.qt
40 dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
41 except:
42 pass
46 """ Main application class """
48 QtGui.QApplication.__init__(self, args)
49 self.mywindow = LiveUSBDialog(opts)
50 self.mywindow.show()
51 try:
52 self.exec_()
53 finally:
54 self.mywindow.terminate()
55
58
59 - def __init__(self, release, progress, proxies):
60 QtCore.QThread.__init__(self)
61 self.release = release
62 self.progress = progress
63 self.proxies = proxies
64 for rel in releases:
65 if rel['name'] == str(release):
66 self.url = rel['url']
67 break
68 else:
69 raise LiveUSBError(_("Unknown release: %s" % release))
70
72 self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"),
73 _("Downloading %s..." % os.path.basename(self.url)))
74 grabber = URLGrabber(progress_obj=self.progress, proxies=self.proxies)
75 try:
76 iso = grabber.urlgrab(self.url, reget='simple')
77 except URLGrabError, e:
78 self.emit(QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"), e.strerror)
79 else:
80 self.emit(QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"), iso)
81
84 """ A QObject urlgrabber BaseMeter class.
85
86 This class is called automatically by urlgrabber with our download details.
87 This class then sends signals to our main dialog window to update the
88 progress bar.
89 """
90 - def start(self, filename=None, url=None, basename=None, size=None,
91 now=None, text=None):
92 self.emit(QtCore.SIGNAL("maxprogress(int)"), size)
93
94 - def update(self, amount_read, now=None):
95 """ Update our download progressbar.
96
97 :read: the number of bytes read so far
98 """
99 self.emit(QtCore.SIGNAL("progress(int)"), amount_read)
100
101 - def end(self, amount_read):
103
106 """ A thread that monitors the progress of Live USB creation.
107
108 This thread periodically checks the amount of free space left on the
109 given drive and sends a signal to our main dialog window to update the
110 progress bar.
111 """
112 totalsize = 0
113 orig_free = 0
114 drive = None
115 get_free_bytes = None
116
117 - def set_data(self, size, drive, freebytes):
123
125 while True:
126 free = self.get_free_bytes()
127 value = (self.orig_free - free) / 1024
128 self.emit(QtCore.SIGNAL("progress(int)"), value)
129 if value >= self.totalsize:
130 break
131 sleep(4)
132
136
139
140 - def __init__(self, live, progress, parent=None):
145
147 self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"), text)
148
197
199 self.emit(QtCore.SIGNAL("maxprogress(int)"), maximum)
200
202 self.emit(QtCore.SIGNAL("progress(int)"), value)
203
206
207
208 -class LiveUSBLogHandler(logging.Handler):
209
210 - def __init__(self, cb):
211 logging.Handler.__init__(self)
212 self.cb = cb
213
214 - def emit(self, record):
215 if record.levelname in ('INFO', 'ERROR'):
216 self.cb(record.msg.encode('utf8', 'replace'))
217
220 """ Our main dialog class """
241
243 self.driveBox.clear()
244 self.textEdit.clear()
245 try:
246 self.live.detect_removable_drives()
247 for device, info in self.live.drives.items():
248 if info['label']:
249 self.driveBox.addItem("%s (%s)" % (device, info['label']))
250 else:
251 self.driveBox.addItem(device)
252 self.startButton.setEnabled(True)
253 except LiveUSBError, e:
254 self.textEdit.setPlainText(e.message.encode('utf8'))
255 self.startButton.setEnabled(False)
256
258 for release in [release['name'] for release in releases]:
259 self.downloadCombo.addItem(release)
260
262 self.connect(self.isoBttn, QtCore.SIGNAL("clicked()"), self.selectfile)
263 self.connect(self.startButton, QtCore.SIGNAL("clicked()"), self.begin)
264 self.connect(self.overlaySlider, QtCore.SIGNAL("valueChanged(int)"),
265 self.overlay_value)
266 self.connect(self.live_thread, QtCore.SIGNAL("status(PyQt_PyObject)"),
267 self.status)
268 self.connect(self.live_thread, QtCore.SIGNAL("finished()"),
269 lambda: self.enable_widgets(True))
270 self.connect(self.live_thread, QtCore.SIGNAL("terminated()"),
271 lambda: self.enable_widgets(True))
272 self.connect(self.live_thread, QtCore.SIGNAL("progress(int)"),
273 self.progress)
274 self.connect(self.live_thread, QtCore.SIGNAL("maxprogress(int)"),
275 self.maxprogress)
276 self.connect(self.progress_thread, QtCore.SIGNAL("progress(int)"),
277 self.progress)
278 self.connect(self.progress_thread, QtCore.SIGNAL("maxprogress(int)"),
279 self.maxprogress)
280 self.connect(self.download_progress, QtCore.SIGNAL("maxprogress(int)"),
281 self.maxprogress)
282 self.connect(self.download_progress, QtCore.SIGNAL("progress(int)"),
283 self.progress)
284 if hasattr(self, 'refreshDevicesButton'):
285 self.connect(self.refreshDevicesButton, QtCore.SIGNAL("clicked()"),
286 self.populate_devices)
287
288
289 if hasattr(self.live, 'hal'):
290 self.live.hal.connect_to_signal('DeviceAdded',
291 self.populate_devices)
292 self.live.hal.connect_to_signal('DeviceRemoved',
293 self.populate_devices)
294
295 @QtCore.pyqtSignature("QString")
297 """ Change the maximum overlay size when each drive is selected.
298
299 This sets the maximum megabyte size of the persistent storage slider
300 to the number of free megabytes on the currently selected
301 "Target Device". If the device is not mounted, or if it has more than
302 2gigs of free space, set the maximum to 2047mb, which is apparently
303 the largest file we can/should store on a vfat partition.
304 """
305 if not str(drive):
306 return
307 freespace = self.live.drives[str(drive).split()[0]]['free']
308 if not freespace or freespace > 2047:
309 freespace = 2047
310 self.overlaySlider.setMaximum(freespace)
311
313 self.progressBar.setValue(value)
314
316 self.progressBar.setMaximum(value)
317
319 self.textEdit.append(text.encode('utf8', 'replace'))
320
329
331 self.overlayTitle.setTitle(_("Persistent Storage") + " (%d MB)" % value)
332
334 return str(self.driveBox.currentText()).split()[0]
335
390
392 """ Called by our ReleaseDownloader thread upon completion.
393
394 Upon success, the thread passes in the filename of the downloaded
395 release. If the 'iso' argument is not an existing file, then
396 it is assumed that the download failed and 'iso' should contain
397 the error message.
398 """
399 if os.path.exists(iso):
400 self.status(_("Download complete!"))
401 self.live.iso = iso
402 self.live_thread.start()
403 else:
404 self.status(_("Download failed: " + iso))
405 self.status(_("You can try again to resume your download"))
406 self.enable_widgets(True)
407
409 isofile = QtGui.QFileDialog.getOpenFileName(self, _("Select Live ISO"),
410 ".", "ISO (*.iso)" )
411 if isofile:
412 try:
413 self.live.iso = self._to_unicode(isofile)
414 except Exception, e:
415 self.live.log.error(e.message.encode('utf8'))
416 self.status(_("Sorry, I'm having trouble encoding the filename "
417 "of your livecd. You may have better luck if "
418 "you move your ISO to the root of your drive "
419 "(ie: C:\)"))
420
421 self.live.log.info('%s ' % os.path.basename(self.live.iso) +
422 _("selected"))
423
425 """ Terminate any processes that we have spawned """
426 self.live.terminate()
427
429 if hasattr(obj, 'toUtf8'):
430 obj = str(obj.toUtf8())
431 if isinstance(obj, basestring):
432 if not isinstance(obj, unicode):
433 obj = unicode(obj, encoding, 'replace')
434 return obj
435