[Pkg-privacy-commits] [tails-installer] 115/210: Merge branch 'feature/udisks2' into feature/new-ui
Intrigeri
intrigeri at moszumanska.debian.org
Wed May 24 15:26:35 UTC 2017
This is an automated email from the git hooks/post-receive script.
intrigeri pushed a commit to tag 3.90.0
in repository tails-installer.
commit 631ae9ab919cf2c4ca2c0745b850724bfd0ffcca
Merge: 6684521 a3f79a2
Author: Martin Briza <mbriza at redhat.com>
Date: Thu Jul 2 15:21:43 2015 +0200
Merge branch 'feature/udisks2' into feature/new-ui
Conflicts:
liveusb/creator.py
liveusb/gui.py
README.rst | 2 +
liveusb/creator.py | 175 +++++++++++++++++++++++++++++++++++++++++------------
liveusb/gui.py | 20 ++----
3 files changed, 143 insertions(+), 54 deletions(-)
diff --cc liveusb/creator.py
index b204799,b57c41c..85505be
--- a/liveusb/creator.py
+++ b/liveusb/creator.py
@@@ -394,11 -389,8 +394,18 @@@ class LiveUSBCreator(object)
return release
def _set_drive(self, drive):
+ if drive == None:
+ self._drive = None
+ return
if not self.drives.has_key(drive):
-- raise LiveUSBError(_("Cannot find device %s" % drive))
++ found = False
++ for key in self.drives.keys():
++ if self.drives[key]['device'] == drive:
++ drive = key
++ found = True
++ break
++ if not found:
++ raise LiveUSBError(_("Cannot find device %s" % drive))
self.log.debug("%s selected: %s" % (drive, self.drives[drive]))
self._drive = drive
self.uuid = self.drives[drive]['uuid']
@@@ -510,99 -501,124 +516,194 @@@ class LinuxLiveUSBCreator(LiveUSBCreato
'that does not support the ext4 filesystem'))
self.valid_fstypes -= set(['ext4'])
- def detect_removable_drives(self, callback=None):
- """ Detect all removable USB storage devices using UDisks via D-Bus """
+ def detect_removable_drives(self, callbackAdded=None, callbackRemoved=None):
+ """ Detect all removable USB storage devices using UDisks2 via D-Bus """
import dbus
+ self.callbackAdded = callbackAdded
+ self.callbackRemoved = callbackRemoved
self.drives = {}
self.bus = dbus.SystemBus()
++ """
+ udisks_obj = self.bus.get_object("org.freedesktop.UDisks",
+ "/org/freedesktop/UDisks")
+ self.udisks = dbus.Interface(udisks_obj, "org.freedesktop.UDisks")
+
+ def handle_reply(devices):
+ for device in devices:
+ dev_obj = self.bus.get_object("org.freedesktop.UDisks", device)
+ dev = dbus.Interface(dev_obj, "org.freedesktop.DBus.Properties")
+
+ data = {
+ 'udi': str(device),
+ 'is_optical': bool(dev.Get(device, 'DeviceIsOpticalDisc')),
+ 'label': unicode(dev.Get(device, 'IdLabel')).replace(' ', '_'),
+ 'fstype': str(dev.Get(device, 'IdType')),
+ 'fsversion': str(dev.Get(device, 'IdVersion')),
+ 'uuid': str(dev.Get(device, 'IdUuid')),
+ 'device': str(dev.Get(device, 'DeviceFile')),
+ 'mount': map(unicode, list(dev.Get(device, 'DeviceMountPaths'))),
+ 'bootable': 'boot' in map(str,
+ list(dev.Get(device, 'PartitionFlags'))),
+ 'parent': None,
+ 'size': int(dev.Get(device, 'DeviceSize')),
+ 'fullSize': int(dev.Get(device, 'DeviceSize')),
+ 'model': str(dev.Get(device, 'DriveModel')),
+ 'vendor': str(dev.Get(device, 'DriveVendor'))
+ }
+
+ # Only pay attention to USB devices, unless --force'd
+ iface = str(dev.Get(device, 'DriveConnectionInterface'))
+ if iface != 'usb' and self.opts.force != data['device']:
+ self.log.warning('Skipping non-usb drive: %s' % device)
+ continue
+
+ # Skip optical drives
+ if data['is_optical'] and self.opts.force != data['device']:
+ self.log.debug('Skipping optical device: %s' % data['device'])
+ continue
+
+ # Skip things without a size
+ if not data['size'] and not self.opts.force:
+ self.log.debug('Skipping device without size: %s' % device)
+ continue
+
+ # Skip devices with unknown filesystems
+ if data['fstype'] not in self.valid_fstypes and \
+ self.opts.force != data['device']:
+ self.log.debug('Skipping %s with unknown filesystem: %s' % (
+ data['device'], data['fstype']))
+ continue
+
+ parent = dev.Get(device, 'PartitionSlave')
+ if parent and parent != '/':
+ data['parent'] = str(dbus.Interface(self._get_device(parent),
+ 'org.freedesktop.DBus.Properties').Get(parent,
+ 'DeviceFile'))
+ data['fullSize'] = int(dbus.Interface(self._get_device(parent),
+ 'org.freedesktop.DBus.Properties').Get(parent,
+ 'DeviceSize'))
+
+ mount = data['mount']
+ if mount:
+ if len(mount) > 1:
+ self.log.warning('Multiple mount points for %s' %
+ data['device'])
+ mount = data['mount'] = data['mount'][0]
+ else:
+ mount = data['mount'] = None
++ """
+ udisks_obj = self.bus.get_object("org.freedesktop.UDisks2",
+ "/org/freedesktop/UDisks2")
+ self.udisks = dbus.Interface(udisks_obj, 'org.freedesktop.DBus.ObjectManager')
+
+ def strify(s):
+ return bytearray(s).replace(b'\x00', b'').decode('utf-8')
+
+ def handleAdded(name, device):
+ if ('org.freedesktop.UDisks2.Block' in device and
+ 'org.freedesktop.UDisks2.Filesystem' in device and
+ 'org.freedesktop.UDisks2.Partition' in device):
+ self.log.debug('Found block device with filesystem on %s' % name)
+ else:
+ return
+
+ partition = device['org.freedesktop.UDisks2.Partition']
+ fs = device['org.freedesktop.UDisks2.Filesystem']
+ blk = device['org.freedesktop.UDisks2.Block']
+
+ if blk['Drive'] == '/':
+ self.log.debug('Skipping root drive: %s' % name)
+ return
+
+ drive_obj = self.bus.get_object("org.freedesktop.UDisks2", blk['Drive'])
+ drive = dbus.Interface(drive_obj, "org.freedesktop.DBus.Properties").GetAll("org.freedesktop.UDisks2.Drive")
+
+ # this is probably the only check we need, including Drive != "/"
+ if (not drive[u'Removable'] or
+ drive[u'Optical'] or
+ (drive[u'ConnectionBus'] != 'usb' and
+ drive[u'ConnectionBus'] != 'sdio')):
+ self.log.debug('Skipping a device that is not removable, connected via USB or is optical: %s' % name)
+ return
+
+ data = {
+ 'udi': str(blk['Drive']),
+ 'label': str(blk['IdLabel']),
+ 'fstype': str(blk['IdType']),
+ 'fsversion': str(blk['IdVersion']),
+ 'uuid': str(blk['IdUUID']),
+ 'device': strify(blk['Device']),
+ 'mount': map(strify, fs['MountPoints']),
+ 'size': int(blk['Size']),
+ }
+ self.log.debug('data = %r' % data)
+
+ if '/boot' in data['mount']:
+ self.log.debug('Skipping boot device: %s' % name)
+ return
+
+ # Skip things without a size
+ if not data['size'] and not self.opts.force:
+ self.log.debug('Skipping device without size: %s' % device)
+ return
+
+ # Skip devices with unknown filesystems
+ if data['fstype'] not in self.valid_fstypes and \
+ self.opts.force != data['device']:
+ self.log.debug('Skipping %s with unknown filesystem: %s' % (
+ data['device'], data['fstype']))
+ return
+
+ mount = data['mount']
+ if mount:
+ if len(mount) > 1:
+ self.log.warning('Multiple mount points for %s' %
+ data['device'])
+ mount = data['mount'] = data['mount'][0]
+ else:
+ mount = data['mount'] = None
- data['free'] = mount and \
- self.get_free_bytes(mount) / 1024**2 or None
+ data['free'] = mount and \
+ self.get_free_bytes(mount) / 1024**2 or None
- self.log.debug(pformat(data))
+
- self.drives[data['device']] = data
+ parent_obj = self.bus.get_object("org.freedesktop.UDisks2", partition[u'Table'])
+ parent = dbus.Interface(parent_obj, "org.freedesktop.DBus.Properties").Get("org.freedesktop.UDisks2.Block", "Device")
+ data['parent'] = strify(parent)
- # Remove parent drives if a valid partition exists
- for parent in [d['parent'] for d in self.drives.values()]:
- if parent in self.drives:
- del(self.drives[parent])
+ self.log.debug(pformat(data))
- if callback:
- callback()
+ self.drives[name] = data
- def handle_error(error):
- self.log.error(str(error))
+ if self.callbackAdded:
+ self.callbackAdded()
- self.udisks.EnumerateDevices(reply_handler=handle_reply,
- error_handler=handle_error)
+ def handleRemoved(path, interfaces):
+
+ print ("KEYS!", path, self.drives.keys())
+ if self.drives.has_key(path):
+ print("PRE REMOVED", path, interfaces)
+ del self.drives[path]
+ print("POST REMOVED", path, interfaces)
+
+ if self.callbackRemoved:
+ self.callbackRemoved()
+ #blk = device['org.freedesktop.UDisks2.Block']
+ #if path.startswith("/org/freedesktop/UDisks2"):
+
+ #if 'org.freedesktop.UDisks2.Block' in interfaces:
+ #object = self.bus.get_object("org.freedesktop.UDisks2", path)
+ #device = dbus.Interface(object, "org.freedesktop.UDisks2.Block").GetAll("Drive")
+ #print device
+ #print("FOUND:", device['org.freedesktop.UDisks2.Block'])
+ #print("GOT:", self.drives.keys())
+ #callbackRemoved()
+
+ self.bus.add_signal_receiver(handleAdded, "InterfacesAdded", "org.freedesktop.DBus.ObjectManager", "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2")
+ self.bus.add_signal_receiver(handleRemoved, "InterfacesRemoved", "org.freedesktop.DBus.ObjectManager", "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2")
+
+ for name, device in self.udisks.GetManagedObjects().iteritems():
+ handleAdded(name, device)
def _storage_bus(self, dev):
storage_bus = None
diff --cc liveusb/gui.py
index 0deb08e,5360917..5da2857
--- a/liveusb/gui.py
+++ b/liveusb/gui.py
@@@ -216,776 -152,510 +216,768 @@@ class ReleaseWriterProgressThread(QThre
self.alive = False
def terminate(self):
- self.emit(QtCore.SIGNAL("progress(int)"), self.totalsize)
- QtCore.QThread.terminate(self)
+ self.parent().progress = self.totalSize
+ self.terminate()
-class LiveUSBThread(QtCore.QThread):
+class ReleaseWriterThread(QThread):
+ """ The actual write to the portable drive """
- def __init__(self, live, progress, parent=None):
- QtCore.QThread.__init__(self, parent)
- self.progress = progress
+ def __init__(self, parent, progressThread):
+ QThread.__init__(self, parent)
+
+ self.live = parent.live
self.parent = parent
- self.live = live
+ self.progressThread = progressThread
- def status(self, text):
- self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"), text)
def run(self):
- handler = LiveUSBLogHandler(self.status)
- self.live.log.addHandler(handler)
+ # TODO move this to the backend
+ #handler = LiveUSBLogHandler(self.parent.status)
+ #self.live.log.addHandler(handler)
now = datetime.now()
try:
- #if self.parent.opts.format:
- # self.live.unmount_device()
- # self.live.format_device()
-
- # Initialize zip-drive-compatible geometry
- #if self.parent.opts.zip:
- # self.live.dest = self.live.drive['mount']
- # self.live.drive['unmount'] = True
- # self.live.unmount_device()
- # self.live.initialize_zip_geometry()
- # self.live.drive = self.parent.get_selected_drive()
- # self.live.dest = self.live.drive['mount']
- # self.live.drive['unmount'] = True
- # self.live.unmount_device()
- # self.live.format_device()
-
- # If we're going to dd the image
- if self.parent.destructiveButton.isChecked():
- self.parent.progressBar.setRange(0, 0)
- self.live.dd_image()
- self.live.log.removeHandler(handler)
- duration = str(datetime.now() - now).split('.')[0]
- self.status(_("Complete! (%s)") % duration)
- self.parent.progressBar.setRange(0, 1)
- return
+ if self.parent.release.liveUSBData.option('dd'):
+ self.ddImage(now)
+ else:
+ self.copyImage(now)
+ except Exception, e:
+ self.parent.release.addError(e.args[0])
+ self.live.log.exception(e)
- self.live.verify_filesystem()
- if not self.live.drive['uuid'] and not self.live.label:
- self.status(_("Error: Cannot set the label or obtain "
- "the UUID of your device. Unable to continue."))
- self.live.log.removeHandler(handler)
+ self.parent.running = False
+ #self.live.log.removeHandler(handler)
+
+ def ddImage(self, now):
+ # TODO move this to the backend
+ self.live.dd_image()
+ #self.live.log.removeHandler(handler)
+ #duration = str(datetime.now() - now).split('.')[0]
+ self.parent.status = 'Finished!'
+ self.parent.finished = True
+ self.progressThread.stop()
+ return
+
+ def copyImage(self, now):
+ # TODO move this to the backend
+
+ self.parent.status = _('Checking the source image')
+ self.live.check_free_space()
+
+ if not self.live.opts.noverify:
+ # Verify the MD5 checksum inside of the ISO image
+ if not self.live.verify_iso_md5():
+ #self.live.log.removeHandler(handler)
return
- self.live.check_free_space()
-
- if not self.parent.opts.noverify:
- # Verify the MD5 checksum inside of the ISO image
- if not self.live.verify_iso_md5():
- self.live.log.removeHandler(handler)
+ # If we know about this ISO, and it's SHA1 -- verify it
+ release = self.live.get_release_from_iso()
+ if release and ('sha1' in release or 'sha256' in release):
+ if not self.live.verify_iso_sha1(self):
+ #self.live.log.removeHandler(handler)
return
- # If we know about this ISO, and it's SHA1 -- verify it
- release = self.live.get_release_from_iso()
- if release and ('sha1' in release or 'sha256' in release):
- if not self.live.verify_iso_sha1(progress=self):
- self.live.log.removeHandler(handler)
- return
+ self.parent.status = _('Unpacking the image')
+ # Setup the progress bar
+ self.progressThread.set_data(size=self.live.totalsize,
+ drive=self.live.drive['device'],
+ freebytes=self.live.get_free_bytes)
+ self.progressThread.start()
- # Setup the progress bar
- self.progress.set_data(size=self.live.totalsize,
- drive=self.live.drive['device'],
- freebytes=self.live.get_free_bytes)
- self.progress.start()
+ self.live.extract_iso()
- self.live.extract_iso()
- self.live.create_persistent_overlay()
- self.live.update_configs()
- self.live.install_bootloader()
- self.live.bootable_partition()
+ if self.live.blank_mbr() or self.parent.release.liveUSBData.option('resetMBR'):
+ self.live.reset_mbr()
- if self.parent.opts.device_checksum:
- self.live.calculate_device_checksum(progress=self)
- if self.parent.opts.liveos_checksum:
- self.live.calculate_liveos_checksum()
+ self.parent.status = _('Writing the data')
+ self.live.create_persistent_overlay()
+ self.live.update_configs()
+ self.live.install_bootloader()
+ self.live.bootable_partition()
- self.progress.stop()
+ self.parent.status = _('Checking the written data')
+ if self.live.opts.device_checksum:
+ self.live.calculate_device_checksum(progressThread=self)
+ if self.live.opts.liveos_checksum:
+ self.live.calculate_liveos_checksum()
- # Flush all filesystem buffers and unmount
- self.live.flush_buffers()
- self.live.unmount_device()
+ self.progressThread.stop()
- duration = str(datetime.now() - now).split('.')[0]
- self.status(_("Complete! (%s)" % duration))
+ # Flush all filesystem buffers and unmount
+ self.live.flush_buffers()
+ self.live.unmount_device()
+ self.parent.status = _('Finished!')
+ self.parent.finished = True
- except Exception, e:
- self.status(e.args[0])
- self.status(_("LiveUSB creation failed!"))
- self.live.log.exception(e)
+ duration = str(datetime.now() - now).split('.')[0]
+ #self.parent.status = 'Complete! (%s)' % duration
- self.live.log.removeHandler(handler)
- self.progress.terminate()
+ self.progressThread.stop()
def set_max_progress(self, maximum):
- self.emit(QtCore.SIGNAL("maxprogress(int)"), maximum)
+ self.parent.maxProgress = maximum
def update_progress(self, value):
- self.emit(QtCore.SIGNAL("progress(int)"), value)
+ self.parent.progress = value
+
+class ReleaseWriter(QObject):
+ """ Here we can track the progress of the writing and control it """
+ runningChanged = pyqtSignal()
+ currentChanged = pyqtSignal()
+ maximumChanged = pyqtSignal()
+ statusChanged = pyqtSignal()
+ finishedChanged = pyqtSignal()
+
+ _running = False
+ _current = -1.0
+ _maximum = -1.0
+ _status = ''
+ _finished = False
+
+ def __init__(self, parent):
+ QObject.__init__(self, parent)
+ self.live = parent.live
+ self.release = parent
+ self.progressWatcher = ReleaseWriterProgressThread(self)
+ self.worker = ReleaseWriterThread(self, self.progressWatcher)
+
+ def reset(self):
+ self._running = False
+ self._current = -1.0
+ self._maximum = -1.0
+ self.runningChanged.emit()
+ self.currentChanged.emit()
+ self.maximumChanged.emit()
+
+ @pyqtSlot()
+ def run(self):
+ self._running = True
+ self._current = 0.0
+ self._maximum = 100.0
+ self.runningChanged.emit()
+ self.currentChanged.emit()
+ self.maximumChanged.emit()
+ self.status = 'Writing'
+ self.worker.start()
+
+ @pyqtSlot()
+ def cancel(self):
+ self.progressWatcher.stop()
+ self.worker.terminate()
+ self.reset()
+
+ @pyqtProperty(bool, notify=runningChanged)
+ def running(self):
+ return self._running
+
+ @running.setter
+ def running(self, value):
+ if self._running != value:
+ self._running = value
+ self.runningChanged.emit()
+
+ @pyqtProperty(float, notify=maximumChanged)
+ def maxProgress(self):
+ return self._maximum
+
+ @maxProgress.setter
+ def maxProgress(self, value):
+ if (value != self._maximum):
+ self._maximum = value
+ self.maximumChanged.emit()
+
+ @pyqtProperty(float, notify=currentChanged)
+ def progress(self):
+ return self._current
+
+ @progress.setter
+ def progress(self, value):
+ if (value != self._current):
+ self._current = value
+ self.currentChanged.emit()
+
+ @pyqtProperty(str, notify=statusChanged)
+ def status(self):
+ return self._status
+
+ @status.setter
+ def status(self, s):
+ if self._status != s:
+ self._status = s
+ self.statusChanged.emit()
+
+ @pyqtProperty(bool, notify=finishedChanged)
+ def finished(self):
+ return self._finished
+
+ @finished.setter
+ def finished(self, value):
+ if self._finished != value:
+ self._finished = value
+ self.finishedChanged.emit()
+
+
+class Release(QObject):
+ ''' Contains the information about the particular release of Fedora
+ I think there should be a cleanup of all the properties - there seem to be more of them than needed
+ '''
+ screenshotsChanged = pyqtSignal()
+ errorChanged = pyqtSignal()
+ warningChanged = pyqtSignal()
+ infoChanged = pyqtSignal()
+ statusChanged = pyqtSignal()
+ pathChanged = pyqtSignal()
+ sizeChanged = pyqtSignal()
+
+ def __init__(self, parent, index, live, data):
+ QObject.__init__(self, parent)
+
+ self._index = index
+ self.live = live
+ self.liveUSBData = parent
- def __del__(self):
- self.wait()
+ self._data = data
+ """
+ self._name = name.replace('_', ' ')
+ self._logo = logo
+ self._size = size
+ self._arch = arch
+ self._fullName = fullName
+ self._releaseDate = releaseDate
+ self._summary = summary
+ self._fullDescription = fullDescription
+ self._isLocal = isLocal
+ self._screenshots = screenshots
+ self._url = url
+ """
+ self._path = ''
-class LiveUSBLogHandler(logging.Handler):
+ self._info = []
+ self._warning = []
+ self._error = []
- def __init__(self, cb):
- logging.Handler.__init__(self)
- self.cb = cb
+ """
+ if self._logo == '':
+ if self._name == 'Fedora Workstation':
+ self._logo = 'qrc:/logo-color-workstation.png'
+ elif self._name == 'Fedora Server':
+ self._logo = 'qrc:/logo-color-server.png'
+ elif self._name == 'Fedora Cloud':
+ self._logo = 'qrc:/logo-color-cloud.png'
+ elif self._name == 'Fedora KDE':
+ self._logo = 'qrc:/logo-plasma5.png'
+ elif self._name == 'Fedora Xfce':
+ self._logo = 'qrc:/logo-xfce.svg'
+ elif self._name == 'Fedora LXDE':
+ self._logo = 'qrc:/logo-lxde.png'
+ else:
+ self._logo = 'qrc:/logo-fedora.svg'
+
+ if self._name == 'Fedora Workstation':
+ self._fullDescription = _('Fedora Workstation is a reliable, user-friendly, and powerful operating system for your laptop or desktop computer. It supports a wide range of developers, from hobbyists and students to professionals in corporate environments.')
+ if self._name == 'Fedora Server':
+ self._fullDescription = _('Fedora Server is a powerful, flexible operating system that includes the best and latest datacenter technologies. It puts you in control of all your infrastructure and services.')
+ if self._name == 'Fedora Cloud':
+ self._fullDescription = _('Fedora Cloud provides a minimal image of Fedora for use in public and private cloud environments. It includes just the bare essentials, so you get enough to run your cloud application -- and nothing more.')
- def emit(self, record):
- if record.levelname in ('INFO', 'ERROR', 'WARN'):
- self.cb(record.msg)
+ """
+ self._download = ReleaseDownload(self)
+ self._download.pathChanged.connect(self.pathChanged)
-class LiveUSBWindow(QtGui.QMainWindow, LiveUSBInterface):
- """ Our main dialog class """
+ self._writer = ReleaseWriter(self)
- def __init__(self, opts, args):
- self.in_process = False
- QtGui.QMainWindow.__init__(self)
- LiveUSBInterface.__init__(self)
- self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint)
- self.opts = opts
- self.args = args
- self.setupUi(self)
- self.live = LiveUSBCreator(opts=opts)
- self.populate_releases()
- self.populate_devices()
- self.downloader = None
- self.progress_thread = ProgressThread()
- self.download_progress = DownloadProgress()
- self.live_thread = LiveUSBThread(live=self.live,
- progress=self.progress_thread,
- parent=self)
- self.connect_slots()
- self.confirmed = False
- self.mbr_reset_confirmed = False
-
- if self.opts.destructive:
- self.destructiveButton.setChecked(True)
-
- # Intercept all liveusb INFO log messages, and display them in the gui
- self.handler = LiveUSBLogHandler(lambda x: self.textEdit.append(x))
- self.live.log.addHandler(self.handler)
- if not self.opts.verbose:
- self.live.log.removeHandler(self.live.handler)
-
- # If an ISO was specified on the command line, use it.
- if args:
- for arg in self.args:
- if arg.lower().endswith('.iso') and os.path.exists(arg):
- self.selectfile(arg)
-
- # Determine if we have admin rights
- if not self.live.is_admin():
- self.live.log.error(_('Warning: This tool needs to be run as an '
- 'Administrator. To do this, right click on the icon and open '
- 'the Properties. Under the Compatibility tab, check the "Run '
- 'this program as an administrator" box.'))
-
- def populate_devices(self, *args, **kw):
- if self.in_process:
- return
- self.driveBox.clear()
- #self.textEdit.clear()
-
- def update_devices():
- self.driveBox.clear()
- if len(self.live.drives) <= 0:
- self.textEdit.setPlainText(_("Unable to find any USB drives"))
- self.startButton.setEnabled(False)
- return
- for device, info in self.live.drives.items():
- if info['label']:
- self.driveBox.addItem("%s (%s)" % (info['device'], info['label']))
- else:
- self.driveBox.addItem(device)
- self.startButton.setEnabled(True)
+ self._download.runningChanged.connect(self.inspectDestination)
- try:
- self.live.detect_removable_drives(callbackAdded=update_devices,callbackRemoved=update_devices)
- except LiveUSBError, e:
- self.textEdit.setPlainText(e.args[0])
- self.startButton.setEnabled(False)
+ self.pathChanged.connect(self.statusChanged)
+ self._download.runningChanged.connect(self.statusChanged)
+ self._writer.runningChanged.connect(self.statusChanged)
+ self._writer.statusChanged.connect(self.statusChanged)
- update_devices()
- def populate_releases(self):
- for release in [release['name'] for release in releases]:
- self.downloadCombo.addItem(release)
+ @pyqtSlot()
+ def get(self):
+ if len(self._path) <= 0:
+ self._download.run()
- def refresh_releases(self):
- self.live.log.info(_('Refreshing releases...'))
- global releases
- try:
- releases = get_fedora_releases()
- self.downloadCombo.clear()
- for release in [release['name'] for release in releases]:
- self.downloadCombo.addItem(release)
- self.live.log.info(_('Releases updated!'))
- except Exception, e:
- self.live.log.error(_('Unable to fetch releases: %r') % e)
-
- def connect_slots(self):
- self.connect(self, QtCore.SIGNAL('triggered()'), self.terminate)
- self.connect(self.isoBttn, QtCore.SIGNAL("clicked()"), self.selectfile)
- self.connect(self.startButton, QtCore.SIGNAL("clicked()"), self.begin)
- self.connect(self.overlaySlider, QtCore.SIGNAL("valueChanged(int)"),
- self.overlay_value)
- self.connect(self.live_thread, QtCore.SIGNAL("status(PyQt_PyObject)"),
- self.status)
- self.connect(self.live_thread, QtCore.SIGNAL("finished()"),
- lambda: self.enable_widgets(True))
- self.connect(self.live_thread, QtCore.SIGNAL("terminated()"),
- lambda: self.enable_widgets(True))
- self.connect(self.live_thread, QtCore.SIGNAL("progress(int)"),
- self.progress)
- self.connect(self.live_thread, QtCore.SIGNAL("maxprogress(int)"),
- self.maxprogress)
- self.connect(self.progress_thread, QtCore.SIGNAL("progress(int)"),
- self.progress)
- self.connect(self.progress_thread, QtCore.SIGNAL("maxprogress(int)"),
- self.maxprogress)
- self.connect(self.download_progress, QtCore.SIGNAL("maxprogress(int)"),
- self.maxprogress)
- self.connect(self.download_progress, QtCore.SIGNAL("progress(int)"),
- self.progress)
- self.connect(self.destructiveButton, QtCore.SIGNAL("toggled(bool)"),
- self.method_destructive_toggled)
- self.connect(self.nonDestructiveButton, QtCore.SIGNAL("toggled(bool)"),
- self.method_nondestructive_toggled)
- if hasattr(self, 'refreshDevicesButton'):
- self.connect(self.refreshDevicesButton, QtCore.SIGNAL("clicked()"),
- self.populate_devices)
- if hasattr(self, 'refreshReleasesButton'):
- self.connect(self.refreshReleasesButton, QtCore.SIGNAL("clicked()"),
- self.refresh_releases)
-
- # If we have access to HAL & DBus, intercept some useful signals
- if hasattr(self.live, 'udisks'):
- self.live.udisks.connect_to_signal('DeviceAdded',
- self.populate_devices)
- self.live.udisks.connect_to_signal('DeviceRemoved',
- self.populate_devices)
-
- @QtCore.pyqtSignature("QString")
- def on_driveBox_currentIndexChanged(self, drive):
- """ Change the maximum overlay size when each drive is selected.
-
- This sets the maximum megabyte size of the persistent storage slider
- to the number of free megabytes on the currently selected
- "Target Device". If the device is not mounted, or if it has more than
- 2gigs of free space, set the maximum to 2047mb, which is apparently
- the largest file we can/should store on a vfat partition.
- """
- drive = unicode(drive)
- if not drive:
- return
- print self.live.drives
- for key in self.live.drives:
- if self.live.drives[key]['device'] == drive.split()[0]:
- self._refresh_overlay_slider(key)
+ @pyqtSlot()
+ def write(self):
+ self._warning = []
+ self._error = []
+ self.errorChanged.emit()
+ self.warningChanged.emit()
+ self._writer.run()
- def _refresh_overlay_slider(self, drive=None):
- """
- Reset the persistent storage slider based on the amount of free space
- on the device and the ISO size.
- """
- if not drive:
- drive = self.get_selected_drive()
- if not drive:
- return
+ @pyqtSlot()
+ def inspectDestination(self):
+ self._warning = []
+ self.warningChanged.emit()
+ self._info = []
+ self.infoChanged.emit()
+ self._error = []
+ self.errorChanged.emit()
+
+ if not self.live.drive:
+ return
- device = self.live.drives[drive]
- freespace = device['free']
- device_size = device['size'] / 1024**2
- current_overlay = self.overlaySlider.value()
-
- if device['fsversion'] == 'FAT32':
- self.live.log.debug(_('Partition is FAT32; Restricting overlay '
- 'size to 4G'))
- max_space = MAX_FAT32
- elif device['fsversion'] == 'FAT16':
- self.live.log.debug(_('Partition is FAT16; Restricting overlay '
- 'size to 2G'))
- max_space = MAX_FAT16
+ if self.parent().option('dd'):
+ self.addWarning(_('You are about to perform a destructive install. This will erase all data and partitions on your USB drive'))
else:
- max_space = MAX_EXT
-
- if freespace:
- if freespace > device_size:
- freespace = device_size
- if freespace > max_space:
- freespace = max_space
-
- if not device['mount']:
- self.live.log.warning(_('Device is not yet mounted, so we cannot '
- 'determine the amount of free space.'))
- if not freespace:
- freespace = device_size
+ if self.live.blank_mbr():
+ self.addInfo(_('The Master Boot Record on your device is blank. Writing the image will reset the MBR on this device'))
+ elif not self.live.mbr_matches_syslinux_bin() and not self.parent().option('resetMBR'):
+ self.addInfo(_('The Master Boot Record on your device does not match your system\'s syslinux MBR.\n'
+ 'If you have trouble booting it, try setting the \"Reset the MBR\" advanced option.'))
+
+ try:
+ self.live.mount_device()
+ except LiveUSBError, e:
+ self.info = e.args[0]
+ self._running = False
+ self.runningChanged.emit()
+ except OSError, e:
+ self.addInfo(_('Unable to mount device'))
+ self._running = False
+ self.runningChanged.emit()
+
+ if self.live.existing_liveos() and not self.parent().option('dd'):
+ self.addWarning(_('Your device already contains a live OS. If you continue, it will be overwritten.'))
+
+ self.live.verify_filesystem()
+ if not self.live.drive['uuid'] and not self.live.label:
+ self.parent.status = _('Error: Cannot set the label or obtain '
+ 'the UUID of your device. Unable to continue.')
+ #self.live.log.removeHandler(handler)
+ return
+
+ @pyqtProperty(int, constant=True)
+ def index(self):
+ return self._index
+
+ @pyqtProperty(str, constant=True)
+ def name(self):
+ return self._data['name']
+
+ @pyqtProperty(str, constant=True)
+ def logo(self):
+ return self._data['logo']
+
+ @pyqtProperty(float, notify=sizeChanged)
+ def size(self):
+ return self._data['size']
+
+ @size.setter
+ def size(self, value):
+ if value != self._size:
+ self._data['size'] = value
+ self.sizeChanged.emit()
+
+ @pyqtProperty(str, constant=True)
+ def arch(self):
+ return self._data['arch']
+
+ @pyqtProperty(QDateTime, constant=True)
+ def releaseDate(self):
+ return QDateTime.fromString(self._data['releaseDate'])
+
+ @pyqtProperty(str, constant=True)
+ def summary(self):
+ return self._data['summary']
+
+ @pyqtProperty(str, constant=True)
+ def description(self):
+ return self._data['description']
+
+ @pyqtProperty(bool, constant=True)
+ def isLocal(self):
+ return self._data['source'] == 'Local'
+
+ @pyqtProperty('QStringList', notify=screenshotsChanged)
+ def screenshots(self):
+ return self._data['screenshots']
+
+ @pyqtProperty(str, constant=True)
+ def url(self):
+ return self._data['url']
+
+ @pyqtProperty(str, notify=pathChanged)
+ def path(self):
+ return self._download.path
+
+ @path.setter
+ def path(self, value):
+ if value.startswith('file://'):
+ value = value.replace('file://', '', 1)
+ if self._path != value:
+ self._download.path = value
+ self.pathChanged.emit();
+ self.size = self.live.isosize
+
+ @pyqtProperty(bool, notify=pathChanged)
+ def readyToWrite(self):
+ return len(self.path) != 0
+
+ @pyqtProperty(ReleaseDownload, constant=True)
+ def download(self):
+ return self._download
+
+ @pyqtProperty(ReleaseWriter, constant=True)
+ def writer(self):
+ return self._writer
+
+ @pyqtProperty(str, notify=statusChanged)
+ def status(self):
+ if not self._download.running and not self.readyToWrite and not self._writer.running:
+ return _('Starting')
+ elif self._download.running:
+ return _('Downloading')
+ elif len(self._error) > 0:
+ return _('Error')
+ elif self.readyToWrite and not self._writer.running and not self._writer.finished:
+ return _('Ready to write')
+ elif self._writer.status:
+ return self._writer.status
else:
- if not freespace:
- self.live.log.warning(_('No free space on %s') % drive)
- freespace = 0
+ return _('Finished')
+
+ @pyqtProperty('QStringList', notify=infoChanged)
+ def info(self):
+ return self._info
+
+ @info.setter
+ def info(self, value):
+ if self._info != value:
+ self._info = value
+ self.infoChanged.emit()
+
+ def addInfo(self, value):
+ if value not in self._info:
+ self._info.append(value)
+ self.infoChanged.emit()
+
+ @pyqtProperty('QStringList', notify=warningChanged)
+ def warning(self):
+ return self._warning
+
+ def addWarning(self, value):
+ if value not in self._warning:
+ self._warning.append(value)
+ self.warningChanged.emit()
+
+ @pyqtProperty('QStringList', notify=errorChanged)
+ def error(self):
+ return self._error
+
+ def addError(self, value):
+ if value not in self._error:
+ self._error.append(value)
+ self.errorChanged.emit()
+
+class ReleaseListModel(QAbstractListModel):
+ """ An abstraction over the list of releases to have them nicely exposed to QML and ready to be filtered
+ """
+ def __init__(self, parent):
+ QAbstractListModel.__init__(self, parent)
- # Subtract the size of the ISO from our maximum overlay size
- if self.live.isosize:
- iso_size = self.live.isosize / 1024**2
- if freespace + iso_size > device['free']:
- freespace -= iso_size
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.parent().releaseData)
- freespace -= 1 # Don't fill the device 100%
+ def roleNames(self):
+ return {Qt.UserRole + 1 : 'release'}
- if freespace < 0:
- freespace = 0
- if freespace < current_overlay:
- self.overlaySlider.setValue(freespace)
- self.live.overlay = self.overlaySlider.value()
+ def data(self, index, role=Qt.DisplayRole):
+ if index.isValid():
+ return self.parent().releaseData[index.row()]
+ return None
- self.overlaySlider.setMaximum(freespace)
+class ReleaseListProxy(QSortFilterProxyModel):
+ """ Filtering proxy for the release list
+ """
+ archChanged = pyqtSignal()
+ nameFilterChanged = pyqtSignal()
+ isFrontChanged = pyqtSignal()
+
+ _archFilter = ['x86_64']
+ _nameFilter = ''
+ _frontPage = True
+
+ _archMap = {'64bit': ['x86_64'], '32bit': ['i686','i386']}
+
+ def __init__(self, parent, sourceModel):
+ QSortFilterProxyModel.__init__(self, parent)
+ self.setSourceModel(sourceModel)
+
+ def rowCount(self, parent=QModelIndex()):
+ if self._frontPage and self.sourceModel().rowCount(parent) > 4:
+ return 4
+ return self.sourceModel().rowCount(parent)
+
+ def filterAcceptsRow(self, sourceRow, sourceParent):
+ row = self.sourceModel().index(sourceRow, 0, sourceParent).data()
+ if len(self._archFilter) == 0 or row.arch.lower() in [x.lower() for x in self._archFilter] or row.isLocal:
+ if len(self._nameFilter) == 0 or self._nameFilter.lower() in row.name.lower() or self._nameFilter.lower() in row.summary.lower():
+ return True
+ return False
+
+ @pyqtProperty(str, notify=nameFilterChanged)
+ def nameFilter(self):
+ return self._nameFilter
+
+ @nameFilter.setter
+ def nameFilter(self, value):
+ if value != self._nameFilter:
+ self._nameFilter = value
+ self.nameFilterChanged.emit()
+ self.invalidateFilter()
+
+ @pyqtProperty('QStringList', constant=True)
+ def possibleArchs(self):
+ return self._archMap.keys()
+
+ @pyqtProperty(str, notify=archChanged)
+ def archFilter(self):
+ for name, abbrs in self._archMap.items():
+ if abbrs == self._archFilter:
+ return name
+ self.archFilter = '64bit'
+ return '64bit'
+
+ @archFilter.setter
+ def archFilter(self, value):
+ if self._archMap.has_key(value) and self.archFilter != self._archMap[value]:
+ self._archFilter = self._archMap[value]
+ self.archChanged.emit()
+ self.invalidateFilter()
+
+ @pyqtProperty(bool, notify=isFrontChanged)
+ def isFront(self):
+ return self._frontPage
+
+ @isFront.setter
+ def isFront(self, value):
+ if value != self._frontPage:
+ self._frontPage = value
+ self.isFrontChanged.emit()
+ self.invalidate()
- def progress(self, value):
- self.progressBar.setValue(value)
-
- def maxprogress(self, value):
- self.progressBar.setMaximum(value)
-
- def status(self, text):
- if not isinstance(text, basestring):
- text = str(text)
- self.textEdit.append(text)
-
- def enable_widgets(self, enabled=True):
- self.startButton.setEnabled(enabled)
- self.driveBox.setEnabled(enabled)
- self.overlaySlider.setEnabled(enabled)
- self.isoBttn.setEnabled(enabled)
- self.downloadCombo.setEnabled(enabled)
- self.destructiveButton.setEnabled(enabled)
- self.nonDestructiveButton.setEnabled(enabled)
- if hasattr(self, 'refreshDevicesButton'):
- self.refreshDevicesButton.setEnabled(enabled)
- if hasattr(self, 'refreshReleasesButton'):
- self.refreshReleasesButton.setEnabled(enabled)
- self.in_process = not enabled
-
- def overlay_value(self, value):
- self.overlayTitle.setTitle(_("Persistent Storage") + " (%d MB)" % value)
-
- def get_selected_drive(self):
- text = self.live._to_unicode(self.driveBox.currentText()).split()
- if text:
- for key, drive in self.live.drives:
- if drive['device'] == text:
- return key
-
- def begin(self):
- """ Begin the liveusb creation process.
--
- This method is called when the "Create LiveUSB" button is clicked.
- """
- self.enable_widgets(False)
- self.live.overlay = self.overlaySlider.value()
- self.live.drive = self.get_selected_drive()
+class LiveUSBLogHandler(logging.Handler):
- # Unmount the device and check the MBR
- if self.nonDestructiveButton.isChecked():
- if self.live.blank_mbr():
- if not self.mbr_reset_confirmed:
- self.status(_("The Master Boot Record on your device is blank. "
- "Pressing 'Create LiveUSB' again will reset the "
- "MBR on this device."))
- self.mbr_reset_confirmed = True
- self.enable_widgets(True)
- return
- if self.live.drive['mount']:
- self.live.dest = self.live.drive['mount']
- self.live.unmount_device()
- self.live.reset_mbr()
- elif not self.live.mbr_matches_syslinux_bin():
- if self.opts.reset_mbr:
- self.live.reset_mbr()
- else:
- self.live.log.warn(_("Warning: The Master Boot Record on your device "
- "does not match your system's syslinux MBR. If you "
- "have trouble booting this stick, try running the "
- "liveusb-creator with the --reset-mbr option."))
-
- try:
- self.live.mount_device()
- self._refresh_overlay_slider() # To reflect the drives free space
- except LiveUSBError, e:
- self.status(e.args[0])
- self.enable_widgets(True)
- return
- except OSError, e:
- self.status(_('Unable to mount device'))
- self.enable_widgets(True)
- return
+ def __init__(self, cb):
+ logging.Handler.__init__(self)
+ self.cb = cb
- if self.live.existing_liveos():
- if not self.confirmed:
- self.status(_("Your device already contains a LiveOS.\nIf you "
- "continue, this will be overwritten."))
- if self.live.existing_overlay() and self.overlaySlider.value():
- self.status(_("Warning: Creating a new persistent overlay "
- "will delete your existing one."))
- self.status(_("Press 'Create Live USB' again if you wish to "
- "continue."))
- self.confirmed = True
- #self.live.unmount_device()
- self.enable_widgets(True)
- return
- else:
- # The user has confirmed that they wish to overwrite their
- # existing Live OS. Here we delete it first, in order to
- # accurately calculate progress.
- self.confirmed = False
- try:
- self.live.delete_liveos()
- except LiveUSBError, e:
- self.status(e.args[0])
- #self.live.unmount_device()
- self.enable_widgets(True)
- return
- else:
- # Require confirmation for destructive installs
- if not self.confirmed:
- self.status(_("WARNING: You are about to perform a destructive install. This will destroy all data and partitions on your USB drive. Press 'Create Live USB' again to continue."))
- self.confirmed = True
- self.enable_widgets(True)
- return
+ def emit(self, record):
+ if record.levelname in ('INFO', 'ERROR', 'WARN'):
+ self.cb(record.msg)
- # Remove the log handler, because our live thread will register its own
- self.live.log.removeHandler(self.handler)
+class USBDrive(QObject):
- # If the user has selected an ISO, use it. If not, download one.
- if self.live.iso:
- self.live_thread.start()
- else:
- self.downloader = ReleaseDownloader(
- self.downloadCombo.currentText(),
- progress=self.download_progress,
- proxies=self.live.get_proxies())
- self.connect(self.downloader,
- QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"),
- self.download_complete)
- self.connect(self.downloader,
- QtCore.SIGNAL("status(PyQt_PyObject)"),
- self.status)
- self.downloader.start()
-
- def download_complete(self, iso):
- """ Called by our ReleaseDownloader thread upon completion.
-
- Upon success, the thread passes in the filename of the downloaded
- release. If the 'iso' argument is not an existing file, then
- it is assumed that the download failed and 'iso' should contain
- the error message.
- """
- if os.path.exists(iso):
- self.status(_("Download complete!"))
- self.live.iso = iso
- self.live_thread.start()
- else:
- self.status(_("Download failed: " + iso))
- self.status(_("You can try again to resume your download"))
- self.enable_widgets(True)
-
- def selectfile(self, isofile=None):
- if not isofile:
- isofile = QtGui.QFileDialog.getOpenFileName(self,
- _("Select Live ISO"), ".", "ISO (*.iso)" )
- if isofile:
- try:
- self.live.set_iso(isofile)
- except Exception, e:
- self.live.log.error(e.args[0])
- self.status(_("Unable to encode the filename of your livecd. "
- "You may have better luck if you move your ISO "
- "to the root of your drive (ie: C:\)"))
-
- self.live.log.info('%s ' % os.path.basename(self.live.iso) +
- _("selected"))
- self._refresh_overlay_slider()
- self.downloadGroup.setEnabled(False)
+ def __init__(self, parent, name, drive):
+ QObject.__init__(self, parent)
+ self._name = name
+ self._drive = drive
- def terminate(self):
- """ Terminate any processes that we have spawned """
- self.live.terminate()
+ @pyqtProperty(str, constant=True)
+ def text(self):
+ return self._name
+
+ @pyqtProperty(str, constant=True)
+ def drive(self):
+ return self._drive
- def method_destructive_toggled(self, enabled):
- if enabled:
- self.overlayTitle.setEnabled(False)
+class LiveUSBData(QObject):
+ """ An entry point to all the exposed properties.
+ There is a list of images and USB drives
+ """
+ releasesChanged = pyqtSignal()
+ currentImageChanged = pyqtSignal()
+ usbDrivesChanged = pyqtSignal()
+ currentDriveChanged = pyqtSignal()
+ optionsChanged = pyqtSignal()
+
+ _currentIndex = 0
+ _currentDrive = 0
+
+ # man, this is just awkward... but it seems like the only way to do it in a predictable manner without creating a new class
+ _optionKeys = ['dd', 'resetMBR']
+ _optionNames = {'dd': _('Use <b>dd</b> to write the image - this will erase everything on your portable drive'),
+ 'resetMBR': _('Reset the MBR (Master Boot Record)'),
+ }
+ _optionValues = {'dd': False,
+ 'resetMBR': False,
+ }
+
+ def __init__(self, opts):
+ QObject.__init__(self)
+ self.live = LiveUSBCreator(opts=opts)
+ self._releaseModel = ReleaseListModel(self)
+ self._releaseProxy = ReleaseListProxy(self, self._releaseModel)
+
+ self.releaseData = []
+
+ for release in releases:
+ self.releaseData.append(Release(self,
+ len(self.releaseData),
+ self.live,
+ release
+ ))
+ self._usbDrives = []
+ self.currentDriveChanged.connect(self.currentImage.inspectDestination)
+
- usbTimer = QTimer(self)
- usbTimer.setInterval(5000) # check for USB drives every 5 seconds
- usbTimer.timeout.connect(self.USBDeviceEnumerationStart)
- usbTimer.start()
++ self.live.detect_removable_drives(callbackAdded=self.USBDeviceCallback, callbackRemoved=self.USBDeviceCallback)
+
- @pyqtSlot()
- def USBDeviceEnumerationStart(self):
- try:
- self.live.detect_removable_drives(callback=self.USBDeviceCallback)
- except LiveUSBError, e:
- pass # TODO
+
+ def USBDeviceCallback(self):
+ tmpDrives = []
+ previouslySelected = ''
+ if len(self._usbDrives) > 0:
+ previouslySelected = self._usbDrives[self._currentDrive].drive['device']
+ for drive, info in self.live.drives.items():
+ name = ''
+ if 'vendor' in info and 'model' in info:
+ name = info['vendor'] + ' ' + info['model']
+ elif 'label' in info:
+ name = info['device'] + ' - ' + info['label']
+ else:
+ name = info['device']
+
+ gb = 1000.0 # if it's decided to use base 2 values, change this
+
+ if 'fullSize' in info:
+ usedSize = info['fullSize']
+ else:
+ usedSize = info['size']
+
+ if usedSize < gb ** 1:
+ name += ' (%.1f B)' % (usedSize / (gb ** 0))
+ elif usedSize < gb ** 2:
+ name += ' (%.1f KB)' % (usedSize / (gb ** 1))
+ elif usedSize < gb ** 3:
+ name += ' (%.1f MB)' % (usedSize / (gb ** 2))
+ elif usedSize < gb ** 4:
+ name += ' (%.1f GB)' % (usedSize / (gb ** 3))
+ else:
+ name += ' (%.1f TB)' % (usedSize / (gb ** 4))
+
+ tmpDrives.append(USBDrive(self, name, info))
+
+ if tmpDrives != self._usbDrives:
+ self._usbDrives = tmpDrives
+ self.usbDrivesChanged.emit()
+
+ self.currentDrive = -1
+ for i, drive in enumerate(self._usbDrives):
+ if drive.drive['device'] == previouslySelected:
+ self.currentDrive = i
+
+ @pyqtProperty(ReleaseListModel, notify=releasesChanged)
+ def releaseModel(self):
+ return self._releaseModel
+
+ @pyqtProperty(ReleaseListProxy, notify=releasesChanged)
+ def releaseProxyModel(self):
+ return self._releaseProxy
+
+ @pyqtProperty(int, notify=currentImageChanged)
+ def currentIndex(self):
+ return self._currentIndex
+
+ @currentIndex.setter
+ def currentIndex(self, value):
+ if value != self._currentIndex:
+ self.currentDriveChanged.disconnect(self.currentImage.inspectDestination)
+ self._currentIndex = value
+ self.currentImageChanged.emit()
+ self.currentDriveChanged.connect(self.currentImage.inspectDestination)
+
+ @pyqtProperty(Release, notify=currentImageChanged)
+ def currentImage(self):
+ return self.releaseData[self._currentIndex]
+
+ @pyqtProperty(QQmlListProperty, notify=usbDrivesChanged)
+ def usbDrives(self):
+ return QQmlListProperty(USBDrive, self, self._usbDrives)
+
+ @pyqtProperty('QStringList', notify=usbDrivesChanged)
+ def usbDriveNames(self):
+ return list(i.text for i in self._usbDrives)
+
+ @pyqtProperty(int, notify=currentDriveChanged)
+ def currentDrive(self):
+ return self._currentDrive
+
+ @currentDrive.setter
+ def currentDrive(self, value):
+ if len(self._usbDrives) == 0:
- self._currentDrive
++ self._currentDrive = -1
++ self.currentDriveChanged.emit()
++ self.live.drive = None
+ return
+ if value > len(self._usbDrives):
+ value = 0
- self.currentDriveChanged.emit()
- if len(self._usbDrives) != 0 and (self._currentDrive != value or self.live.drive != self._usbDrives[value].drive['device']):
++ print ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", value, len(self._usbDrives))
++ if self._currentDrive != value or self.live.drives[self.live.drive]['device'] != self._usbDrives[value].drive['device']:
+ self._currentDrive = value
+ if len(self._usbDrives) > 0:
+ self.live.drive = self._usbDrives[self._currentDrive].drive['device']
+ self.currentDriveChanged.emit()
+ for r in self.releaseData:
+ r.download.finished = False
+
+ @pyqtProperty('QStringList', constant=True)
+ def optionNames(self):
+ ret = []
+ for i in self._optionKeys:
+ ret.append(self._optionNames[i])
+ return ret
+
+ @pyqtProperty('QVariant', notify=optionsChanged)
+ def optionValues(self):
+ ret = []
+ for i in self._optionKeys:
+ ret.append(self._optionValues[i])
+ return ret
+
+ @pyqtSlot(int, bool)
+ def setOption(self, index, value):
+ key = self._optionKeys[index]
+ if self._optionValues[key] != value:
+ # dd and resetMBR options are mutually exclusive
+ if key == 'dd' and value:
+ self._optionValues['resetMBR'] = False
+ if key == 'resetMBR' and value:
+ self._optionValues['dd'] = False
+ self._optionValues[key] = value
+ self.optionsChanged.emit()
+ self.currentImage.inspectDestination()
+
+ @pyqtSlot()
+ def option(self, index):
+ return self._optionValues[index]
+
+
+class LiveUSBApp(QGuiApplication):
+ """ Main application class """
+ def __init__(self, opts, args):
+ QGuiApplication.__init__(self, args)
+ translator = QTranslator()
+ translator.load(QLocale.system().name(), "po")
+ self.installTranslator(translator)
+ qmlRegisterUncreatableType(ReleaseDownload, 'LiveUSB', 1, 0, 'Download', 'Not creatable directly, use the liveUSBData instance instead')
+ qmlRegisterUncreatableType(ReleaseWriter, 'LiveUSB', 1, 0, 'Writer', 'Not creatable directly, use the liveUSBData instance instead')
+ qmlRegisterUncreatableType(ReleaseListModel, 'LiveUSB', 1, 0, 'ReleaseModel', 'Not creatable directly, use the liveUSBData instance instead')
+ qmlRegisterUncreatableType(Release, 'LiveUSB', 1, 0, 'Release', 'Not creatable directly, use the liveUSBData instance instead')
+ qmlRegisterUncreatableType(USBDrive, 'LiveUSB', 1, 0, 'Drive', 'Not creatable directly, use the liveUSBData instance instead')
+ qmlRegisterUncreatableType(LiveUSBData, 'LiveUSB', 1, 0, 'Data', 'Use the liveUSBData root instance')
+
+ engine = QQmlApplicationEngine()
+ self.data = LiveUSBData(opts)
+ engine.rootContext().setContextProperty('liveUSBData', self.data)
+ if (opts.directqml):
+ engine.load(QUrl('liveusb/liveusb.qml'))
+ else:
+ engine.load(QUrl('qrc:/liveusb.qml'))
+ engine.rootObjects()[0].show()
- def method_nondestructive_toggled(self, enabled):
- if enabled:
- self.overlayTitle.setEnabled(True)
+ self.exec_()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/tails-installer.git
More information about the Pkg-privacy-commits
mailing list