diff --git a/Makefile b/Makefile index 1abbd976..3be19ce5 100644 --- a/Makefile +++ b/Makefile @@ -20,15 +20,15 @@ rpms-dom0: rpm --addsign $(RPMS_DIR)/x86_64/qubes-manager*$(VERSION)*.rpm qubesmanager/ui_%.py: ui/%.ui - pyuic4 --from-imports -o $@ $< + pyuic5 --from-imports -o $@ $< ui: $(patsubst ui/%.ui,qubesmanager/ui_%.py,$(wildcard ui/*.ui)) res: - pyrcc4 -py3 -o qubesmanager/resources_rc.py resources.qrc + pyrcc5 -o qubesmanager/resources_rc.py resources.qrc translations: - lrelease-qt4 qubesmanager.pro + lrelease-qt5 qubesmanager.pro python: $(PYTHON) ./setup.py build @@ -37,7 +37,7 @@ python_install: $(PYTHON) ./setup.py install -O1 --skip-build --root $(DESTDIR) update_ts: res - pylupdate4 qubesmanager.pro + pylupdate5 qubesmanager.pro update-repo-current: ln -f $(RPMS_DIR)/x86_64/qubes-manager-*$(VERSION)*.rpm ../yum/current-release/current/dom0/rpm/ diff --git a/build-deps.list b/build-deps.list index 32fd60c2..262823d4 100644 --- a/build-deps.list +++ b/build-deps.list @@ -1 +1 @@ -PyQt4-devel +PyQt5-devel diff --git a/qubesmanager/about.py b/qubesmanager/about.py index da54508d..49670b6c 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -20,7 +20,8 @@ # with this program; if not, see . # # -from PyQt4.QtGui import QDialog, QIcon # pylint: disable=import-error +from PyQt5.QtWidgets import QDialog # pylint: disable=import-error +from PyQt5.QtGui import QIcon # pylint: disable=import-error from qubesmanager.releasenotes import ReleaseNotesDialog from qubesmanager.informationnotes import InformationNotesDialog @@ -42,10 +43,12 @@ def __init__(self): self.releaseNotes.clicked.connect(on_release_notes_clicked) self.informationNotes.clicked.connect(on_information_notes_clicked) + def on_release_notes_clicked(): release_notes_dialog = ReleaseNotesDialog() release_notes_dialog.exec_() + def on_information_notes_clicked(): information_notes_dialog = InformationNotesDialog() information_notes_dialog.exec_() diff --git a/qubesmanager/appmenu_select.py b/qubesmanager/appmenu_select.py index f042011a..196dc638 100755 --- a/qubesmanager/appmenu_select.py +++ b/qubesmanager/appmenu_select.py @@ -20,13 +20,13 @@ # import subprocess +import PyQt5.QtWidgets # pylint: disable=import-error -import PyQt4.QtGui # pylint: disable=import-error # TODO description in tooltip # TODO icon # pylint: disable=too-few-public-methods -class AppListWidgetItem(PyQt4.QtGui.QListWidgetItem): +class AppListWidgetItem(PyQt5.QtWidgets.QListWidgetItem): def __init__(self, name, ident, parent=None): super(AppListWidgetItem, self).__init__(name, parent) # self.setToolTip(command) @@ -58,9 +58,10 @@ def fill_apps_list(self): self.app_list.clear() available_appmenus = [AppListWidgetItem.from_line(line) - for line in subprocess.check_output(['qvm-appmenus', - '--get-available', '--i-understand-format-is-unstable', - self.vm.name]).decode().splitlines()] + for line in subprocess.check_output( + ['qvm-appmenus', + '--get-available', '--i-understand-format-is-unstable', + self.vm.name]).decode().splitlines()] for app in available_appmenus: if app.ident in self.whitelisted: @@ -73,7 +74,7 @@ def fill_apps_list(self): def save_appmenu_select_changes(self): new_whitelisted = [self.app_list.selected_list.item(i).ident - for i in range(self.app_list.selected_list.count())] + for i in range(self.app_list.selected_list.count())] if set(new_whitelisted) == set(self.whitelisted): return False diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index 455500e8..03356485 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -30,8 +30,8 @@ from qubesadmin import events from qubes.storage.file import get_disk_usage -from PyQt4 import QtCore # pylint: disable=import-error -from PyQt4 import QtGui # pylint: disable=import-error +from PyQt5 import QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets # pylint: disable=import-error from . import ui_backupdlg # pylint: disable=no-name-in-module from . import multiselectwidget @@ -45,6 +45,7 @@ import asyncio from contextlib import suppress + # pylint: disable=too-few-public-methods class BackupThread(QtCore.QThread): def __init__(self, vm): @@ -67,7 +68,7 @@ def run(self): self.msg = '\n'.join(msg) -class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard): +class BackupVMsWindow(ui_backupdlg.Ui_Backup, QtWidgets.QWizard): def __init__(self, qt_app, qubes_app, dispatcher, parent=None): super(BackupVMsWindow, self).__init__(parent) @@ -86,34 +87,21 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.select_vms_widget = multiselectwidget.MultiSelectWidget(self) self.verticalLayout.insertWidget(1, self.select_vms_widget) - self.connect(self, QtCore.SIGNAL("currentIdChanged(int)"), - self.current_page_changed) - self.connect(self.select_vms_widget, - QtCore.SIGNAL("items_removed(PyQt_PyObject)"), - self.vms_removed) - self.connect(self.select_vms_widget, - QtCore.SIGNAL("items_added(PyQt_PyObject)"), - self.vms_added) - self.dir_line_edit.connect(self.dir_line_edit, - QtCore.SIGNAL("textChanged(QString)"), - self.backup_location_changed) + self.currentIdChanged.connect(self.current_page_changed) + self.select_vms_widget.itemsRemoved.connect(self.vms_removed) + self.select_vms_widget.itemsAdded.connect(self.vms_added) + self.dir_line_edit.textChanged.connect(self.backup_location_changed) self.select_vms_page.isComplete = self.has_selected_vms self.select_dir_page.isComplete = self.has_selected_dir_and_pass # FIXME # this causes to run isComplete() twice, I don't know why - self.select_vms_page.connect( - self.select_vms_widget, - QtCore.SIGNAL("selected_changed()"), - QtCore.SIGNAL("completeChanged()")) - self.passphrase_line_edit.connect( - self.passphrase_line_edit, - QtCore.SIGNAL("textChanged(QString)"), - self.backup_location_changed) - self.passphrase_line_edit_verify.connect( - self.passphrase_line_edit_verify, - QtCore.SIGNAL("textChanged(QString)"), - self.backup_location_changed) + self.select_vms_widget.selectedChanged.connect( + self.select_vms_page.completeChanged.emit) + self.passphrase_line_edit.textChanged.connect( + self.backup_location_changed) + self.passphrase_line_edit_verify.textChanged.connect( + self.backup_location_changed) self.total_size = 0 @@ -173,8 +161,8 @@ def load_settings(self): except FileNotFoundError: return except exc.QubesException: - QtGui.QMessageBox.information( - None, self.tr("Error loading backup profile"), + QtWidgets.QMessageBox.information( + self, self.tr("Error loading backup profile"), self.tr("Unable to load saved backup profile.")) return if not profile_data: @@ -216,7 +204,7 @@ def save_settings(self, use_temp): backup_utils.write_backup_profile(settings, use_temp) - class VmListItem(QtGui.QListWidgetItem): + class VmListItem(QtWidgets.QListWidgetItem): # pylint: disable=too-few-public-methods def __init__(self, vm): self.vm = vm @@ -276,33 +264,32 @@ def validateCurrentPage(self): elif self.currentPage() is self.select_dir_page: backup_location = str(self.dir_line_edit.text()) if not backup_location: - QtGui.QMessageBox.information( - None, self.tr("Wait!"), + QtWidgets.QMessageBox.information( + self, self.tr("Wait!"), self.tr("Enter backup target location first.")) return False if self.appvm_combobox.currentText() == "dom0" \ and not os.path.isdir(backup_location): - QtGui.QMessageBox.information( - None, self.tr("Wait!"), + QtWidgets.QMessageBox.information( + self, self.tr("Wait!"), self.tr("Selected directory do not exists or " "not a directory (%s).") % backup_location) return False if not self.passphrase_line_edit.text(): - QtGui.QMessageBox.information( - None, self.tr("Wait!"), + QtWidgets.QMessageBox.information( + self, self.tr("Wait!"), self.tr("Enter passphrase for backup " "encryption/verification first.")) return False if self.passphrase_line_edit.text() !=\ self.passphrase_line_edit_verify.text(): - QtGui.QMessageBox.information( - None, self.tr("Wait!"), + QtWidgets.QMessageBox.information( + self, self.tr("Wait!"), self.tr("Enter the same passphrase in both fields.")) return False return True - @staticmethod def cleanup_temporary_files(): try: @@ -310,7 +297,7 @@ def cleanup_temporary_files(): except FileNotFoundError: pass - def current_page_changed(self, page_id): # pylint: disable=unused-argument + def current_page_changed(self, page_id): # pylint: disable=unused-argument old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL) if self.currentPage() is self.confirm_page: @@ -347,7 +334,7 @@ def current_page_changed(self, page_id): # pylint: disable=unused-argument def backup_finished(self): if self.thread.msg: self.progress_status.setText(self.tr("Backup error.")) - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Backup error!"), self.tr("ERROR: {}").format( self.thread.msg)) @@ -383,7 +370,7 @@ def reject(self): 'dom0', 'admin.backup.Cancel', backup_utils.get_profile_name(True)) self.thread.wait() - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Backup aborted!"), self.tr("ERROR: {}").format("Aborted!")) @@ -403,7 +390,7 @@ def has_selected_dir_and_pass(self): def backup_location_changed(self, new_dir=None): # pylint: disable=unused-argument - self.select_dir_page.emit(QtCore.SIGNAL("completeChanged()")) + self.select_dir_page.completeChanged.emit() # Bases on the original code by: @@ -414,7 +401,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): filename = os.path.basename(filename) error = "%s: %s" % (exc_type.__name__, exc_value) - QtGui.QMessageBox.critical( + QtWidgets.QMessageBox.critical( None, "Houston, we have a problem...", "Whoops. A critical error has occured. This is most likely a bug " @@ -422,6 +409,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): error + "at line %d of file %s.

" % (line, filename)) + def loop_shutdown(): pending = asyncio.Task.all_tasks() for task in pending: @@ -430,7 +418,7 @@ def loop_shutdown(): def main(): - qt_app = QtGui.QApplication(sys.argv) + qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName("Qubes Backup VMs") @@ -452,7 +440,7 @@ def main(): asyncio.ensure_future(dispatcher.listen_for_events())) except asyncio.CancelledError: pass - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except loop_shutdown() exc_type, exc_value, exc_traceback = sys.exc_info()[:3] handle_exception(exc_type, exc_value, exc_traceback) diff --git a/qubesmanager/backup_utils.py b/qubesmanager/backup_utils.py index 7dfb8fd4..6d38274a 100644 --- a/qubesmanager/backup_utils.py +++ b/qubesmanager/backup_utils.py @@ -21,8 +21,7 @@ import re import socket -from PyQt4 import QtGui # pylint: disable=import-error -from PyQt4 import QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets # pylint: disable=import-error import subprocess from . import utils @@ -76,7 +75,7 @@ def select_path_button_clicked(dialog, select_file=False, read_only=False): vm = dialog.qubes_app.domains[new_appvm] try: if vm.name == socket.gethostname(): - file_dialog = QtGui.QFileDialog() + file_dialog = QtWidgets.QFileDialog() file_dialog.setReadOnly(True) if select_file: @@ -94,8 +93,8 @@ def select_path_button_clicked(dialog, select_file=False, read_only=False): else "qubes.SelectDirectory") except subprocess.CalledProcessError: if not read_only: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + dialog, dialog.tr("Nothing selected!"), dialog.tr("No file or directory selected.")) else: @@ -105,7 +104,7 @@ def select_path_button_clicked(dialog, select_file=False, read_only=False): dialog.dir_line_edit.setText(new_path) if new_path and backup_location and not read_only: - dialog.select_dir_page.emit(QtCore.SIGNAL("completeChanged()")) + dialog.select_dir_page.completeChanged.emit() def get_profile_name(use_temp): diff --git a/qubesmanager/bootfromdevice.py b/qubesmanager/bootfromdevice.py index 2849db2c..82ea5f55 100644 --- a/qubesmanager/bootfromdevice.py +++ b/qubesmanager/bootfromdevice.py @@ -21,12 +21,13 @@ import subprocess from . import utils from . import ui_bootfromdevice # pylint: disable=no-name-in-module -from PyQt4 import QtGui, QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets # pylint: disable=import-error from qubesadmin import tools from qubesadmin.tools import qvm_start -class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog): +class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, + QtWidgets.QDialog): def __init__(self, vm, qapp, parent=None): super(VMBootFromDeviceWindow, self).__init__(parent) @@ -37,11 +38,8 @@ def __init__(self, vm, qapp, parent=None): self.setWindowTitle( self.tr("Boot {vm} from device").format(vm=self.vm.name)) - self.connect( - self.buttonBox, - QtCore.SIGNAL("accepted()"), - self.save_and_apply) - self.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + self.buttonBox.accepted.connect(self.save_and_apply) + self.buttonBox.rejected.connect(self.reject) # populate buttons and such self.__init_buttons__() @@ -59,8 +57,8 @@ def save_and_apply(self): self.vm_list[self.fileVM.currentIndex()]) + \ ":" + self.pathText.text() else: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("ERROR!"), self.tr("No file or block device selected; please select one.")) return @@ -74,8 +72,8 @@ def save_and_apply(self): def __warn_if_running__(self): if self.vm.is_running(): - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Warning!"), self.tr("Qube must be turned off before booting it from " "device. Please turn off the qube.") @@ -102,7 +100,7 @@ def __init_buttons__(self): self.vm, None, [device for domain in self.vm.app.domains - for device in domain.devices["block"]], + for device in domain.devices["block"]], None, None, allow_default=False, allow_none=False @@ -134,7 +132,7 @@ def main(args=None): args = parser.parse_args(args) vm = args.domains.pop() - qapp = QtGui.QApplication(sys.argv) + qapp = QtWidgets.QApplication(sys.argv) qapp.setOrganizationName('Invisible Things Lab') qapp.setOrganizationDomain("https://www.qubes-os.org/") qapp.setApplicationName("Boot Qube From Device") @@ -148,5 +146,6 @@ def main(args=None): qapp.exec_() qapp.exit() + if __name__ == "__main__": main() diff --git a/qubesmanager/clipboard.py b/qubesmanager/clipboard.py index f4c57854..277e42fa 100644 --- a/qubesmanager/clipboard.py +++ b/qubesmanager/clipboard.py @@ -1,5 +1,4 @@ -#!/usr/bin/python2 -# pylint: skip-file +#!/usr/bin/python3 # # The Qubes OS Project, http://www.qubes-os.org # @@ -26,51 +25,63 @@ import fcntl from math import log -from PyQt4.QtGui import QApplication +# pylint: disable=import-error +from PyQt5.QtWidgets import QApplication, QMessageBox APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock" CLIPBOARD_CONTENTS = "/var/run/qubes/qubes-clipboard.bin" CLIPBOARD_SOURCE = CLIPBOARD_CONTENTS + ".source" + def do_dom0_copy(): copy_text_to_qubes_clipboard(QApplication.clipboard().text()) + def copy_text_to_qubes_clipboard(text): - #inter-appviewer lock + # inter-appviewer lock try: - fd = os.open(APPVIEWER_LOCK, os.O_RDWR|os.O_CREAT, 0o0666) - except: - QMessageBox.warning(None, "Warning!", "Error while accessing Qubes clipboard!") + file = os.open(APPVIEWER_LOCK, os.O_RDWR | os.O_CREAT, 0o0666) + except Exception: # pylint: disable=broad-except + QMessageBox.warning(None, "Warning!", + "Error while accessing Qubes clipboard!") else: try: - fcntl.flock(fd, fcntl.LOCK_EX) - except: - QMessageBox.warning(None, "Warning!", "Error while locking Qubes clipboard!") + fcntl.flock(file, fcntl.LOCK_EX) + except Exception: # pylint: disable=broad-except + QMessageBox.warning(None, "Warning!", + "Error while locking Qubes clipboard!") else: try: with open(CLIPBOARD_CONTENTS, "w") as contents: contents.write(text) with open(CLIPBOARD_SOURCE, "w") as source: source.write("dom0") - except: - QMessageBox.warning(None, "Warning!", "Error while writing to Qubes clipboard!") - fcntl.flock(fd, fcntl.LOCK_UN) - os.close(fd) + except Exception: # pylint: disable=broad-except + QMessageBox.warning(None, "Warning!", + "Error while writing to Qubes clipboard!") + fcntl.flock(file, fcntl.LOCK_UN) + os.close(file) + +# pylint: disable=invalid-name def get_qubes_clipboard_formatted_size(): units = ['B', 'KiB', 'MiB', 'GiB'] try: file_size = os.path.getsize(CLIPBOARD_CONTENTS) - except: - QMessageBox.warning(None, "Warning!", "Error while accessing Qubes clipboard!") + except Exception: # pylint: disable=broad-except + QMessageBox.warning(None, "Warning!", + "Error while accessing Qubes clipboard!") else: - formatted_bytes = '1 byte' if file_size == 1 else str(file_size) + ' bytes' + formatted_bytes = '1 byte' if file_size == 1 \ + else str(file_size) + ' bytes' if file_size > 0: magnitude = min(int(log(file_size)/log(2)*0.1), len(units)-1) if magnitude > 0: - return '%s (%.1f %s)' % (formatted_bytes, file_size/(2.0**(10*magnitude)), units[magnitude]) - return '%s' % (formatted_bytes) + return '%s (%.1f %s)' % (formatted_bytes, + file_size/(2.0**(10*magnitude)), + units[magnitude]) + return '%s' % formatted_bytes return '? bytes' diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index 75adc08d..e2bf9e5f 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -20,7 +20,7 @@ # -from PyQt4 import QtCore, QtGui # pylint: disable=import-error +from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error from contextlib import contextmanager from qubesadmin import exc @@ -29,10 +29,10 @@ @contextmanager def busy_cursor(): try: - QtGui.QApplication.setOverrideCursor(QtCore.Qt.BusyCursor) + QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BusyCursor) yield finally: - QtGui.QApplication.restoreOverrideCursor() + QtWidgets.QApplication.restoreOverrideCursor() # pylint: disable=too-few-public-methods diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index 0c82fb8e..442a6374 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -24,7 +24,7 @@ import sys import subprocess -from PyQt4 import QtCore, QtGui # pylint: disable=import-error +from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error import qubesadmin import qubesadmin.tools @@ -34,6 +34,7 @@ from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error + # pylint: disable=too-few-public-methods class CreateVMThread(QtCore.QThread): def __init__(self, app, vmclass, name, label, template, properties): @@ -59,8 +60,9 @@ def run(self): for k, v in self.properties.items(): setattr(vm, k, v) else: - vm = self.app.add_new_vm(self.vmclass, - name=self.name, label=self.label, template=self.template) + vm = self.app.add_new_vm( + self.vmclass, name=self.name, + label=self.label, template=self.template) for k, v in self.properties.items(): setattr(vm, k, v) @@ -70,7 +72,7 @@ def run(self): self.msg = repr(ex) -class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg): +class NewVmDlg(QtWidgets.QDialog, Ui_NewVMDlg): def __init__(self, qtapp, app, parent=None): super(NewVmDlg, self).__init__(parent) self.setupUi(self) @@ -111,7 +113,8 @@ def __init__(self, qtapp, app, parent=None): self.name.setFocus() if not self.template_list: - QtGui.QMessageBox.warning(None, + QtWidgets.QMessageBox.warning( + self, self.tr('No template available!'), self.tr('Cannot create a qube when no template exists.')) @@ -140,7 +143,8 @@ def accept(self): except LookupError: pass else: - QtGui.QMessageBox.warning(None, + QtWidgets.QMessageBox.warning( + self, self.tr('Incorrect qube name!'), self.tr('A qube with the name {} already exists in the ' 'system!').format(name)) @@ -153,20 +157,19 @@ def accept(self): else: template = self.template_list[self.template_vm.currentIndex()] - properties = {} - properties['provides_network'] = self.provides_network.isChecked() + properties = {'provides_network': self.provides_network.isChecked()} if self.netvm.currentIndex() != 0: properties['netvm'] = self.netvm_list[self.netvm.currentIndex()] if self.install_system.isChecked(): properties['virt_mode'] = 'hvm' properties['kernel'] = None - self.thread = CreateVMThread(self.app, vmclass, name, label, - template, properties) + self.thread = CreateVMThread( + self.app, vmclass, name, label, template, properties) self.thread.finished.connect(self.create_finished) self.thread.start() - self.progress = QtGui.QProgressDialog( + self.progress = QtWidgets.QProgressDialog( self.tr("Creating new qube {}...").format(name), "", 0, 0) self.progress.setCancelButton(None) self.progress.setModal(True) @@ -176,7 +179,8 @@ def create_finished(self): self.progress.hide() if self.thread.msg: - QtGui.QMessageBox.warning(None, + QtWidgets.QMessageBox.warning( + self, self.tr("Error creating the qube!"), self.tr("ERROR: {}").format(self.thread.msg)) @@ -185,12 +189,11 @@ def create_finished(self): if not self.thread.msg: if self.launch_settings.isChecked(): subprocess.check_call(['qubes-vm-settings', - str(self.name.text())]) + str(self.name.text())]) if self.install_system.isChecked(): subprocess.check_call( ['qubes-vm-boot-from-device', str(self.name.text())]) - def type_change(self): # AppVM if self.vm_type.currentIndex() == 0: @@ -221,12 +224,14 @@ def settings_change(self): if self.launch_settings.isChecked() and self.install_system.isEnabled(): self.install_system.setChecked(False) + parser = qubesadmin.tools.QubesArgumentParser() + def main(args=None): args = parser.parse_args(args) - qtapp = QtGui.QApplication(sys.argv) + qtapp = QtWidgets.QApplication(sys.argv) qtapp.setOrganizationName('Invisible Things Lab') qtapp.setOrganizationDomain('https://www.qubes-os.org/') qtapp.setApplicationName('Create qube') diff --git a/qubesmanager/device_list.py b/qubesmanager/device_list.py index a739b063..80a4b250 100644 --- a/qubesmanager/device_list.py +++ b/qubesmanager/device_list.py @@ -18,10 +18,10 @@ # from . import ui_devicelist # pylint: disable=no-name-in-module -from PyQt4 import QtGui, QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets # pylint: disable=import-error -class PCIDeviceListWindow(ui_devicelist.Ui_Dialog, QtGui.QDialog): +class PCIDeviceListWindow(ui_devicelist.Ui_Dialog, QtWidgets.QDialog): def __init__(self, vm, qapp, dev_list, no_strict_reset_list, parent=None): super(PCIDeviceListWindow, self).__init__(parent) @@ -32,10 +32,8 @@ def __init__(self, vm, qapp, dev_list, no_strict_reset_list, parent=None): self.setupUi(self) - self.connect( - self.buttonBox, QtCore.SIGNAL("accepted()"), self.save_and_apply) - self.connect( - self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) + self.buttonBox.accepted.connect(self.save_and_apply) + self.buttonBox.rejected.connect(self.reject) self.ident_list = {} self.fill_device_list() diff --git a/qubesmanager/firewall.py b/qubesmanager/firewall.py index 8e2f8f27..c1e595ed 100644 --- a/qubesmanager/firewall.py +++ b/qubesmanager/firewall.py @@ -21,7 +21,7 @@ import datetime import re -from PyQt4 import QtCore, QtGui # pylint: disable=import-error +from PyQt5 import QtCore, QtGui, QtWidgets # pylint: disable=import-error import qubesadmin.firewall from . import ui_newfwruledlg # pylint: disable=no-name-in-module @@ -30,6 +30,7 @@ class FirewallModifiedOutsideError(ValueError): pass + class QIPAddressValidator(QtGui.QValidator): # pylint: disable=too-few-public-methods def __init__(self, parent=None): @@ -40,10 +41,10 @@ def validate(self, input_string, pos): hostname = str(input_string) if len(hostname) > 255 or not hostname: - return (QtGui.QValidator.Intermediate, input_string, pos) + return QtGui.QValidator.Intermediate, input_string, pos if hostname == "*": - return (QtGui.QValidator.Acceptable, input_string, pos) + return QtGui.QValidator.Acceptable, input_string, pos unmask = hostname.split("/", 1) if len(unmask) == 2: @@ -51,27 +52,28 @@ def validate(self, input_string, pos): mask = unmask[1] if mask.isdigit() or mask == "": if re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None: - return (QtGui.QValidator.Invalid, input_string, pos) + return QtGui.QValidator.Invalid, input_string, pos if mask != "": mask = int(unmask[1]) if mask < 0 or mask > 32: - return (QtGui.QValidator.Invalid, input_string, pos) + return QtGui.QValidator.Invalid, input_string, pos else: - return (QtGui.QValidator.Invalid, input_string, pos) + return QtGui.QValidator.Invalid, input_string, pos if hostname[-1:] == ".": hostname = hostname[:-1] if hostname[-1:] == "-": - return (QtGui.QValidator.Intermediate, input_string, pos) + return QtGui.QValidator.Intermediate, input_string, pos allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(? @@ -307,7 +303,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): filename = os.path.basename(filename) error = "%s: %s" % (exc_type.__name__, exc_value) - QtGui.QMessageBox.critical( + QtWidgets.QMessageBox.critical( None, "Houston, we have a problem...", "Whoops. A critical error has occured. This is most likely a bug " @@ -317,7 +313,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): def main(): - qtapp = QtGui.QApplication(sys.argv) + qtapp = QtWidgets.QApplication(sys.argv) qtapp.setOrganizationName("The Qubes Project") qtapp.setOrganizationDomain("http://qubes-os.org") qtapp.setApplicationName("Qubes Global Settings") @@ -333,5 +329,6 @@ def main(): qtapp.exec_() qtapp.exit() + if __name__ == "__main__": main() diff --git a/qubesmanager/informationnotes.py b/qubesmanager/informationnotes.py index b512d3f8..b5adc326 100644 --- a/qubesmanager/informationnotes.py +++ b/qubesmanager/informationnotes.py @@ -20,7 +20,7 @@ # with this program; if not, see . # # -from PyQt4.QtGui import QDialog # pylint: disable=import-error +from PyQt5.QtWidgets import QDialog # pylint: disable=import-error from . import ui_informationnotes # pylint: disable=no-name-in-module import subprocess diff --git a/qubesmanager/log_dialog.py b/qubesmanager/log_dialog.py index 98ca5d8a..e0e999c8 100644 --- a/qubesmanager/log_dialog.py +++ b/qubesmanager/log_dialog.py @@ -20,8 +20,7 @@ # # import sys -from PyQt4 import QtCore # pylint: disable=import-error -from PyQt4 import QtGui # pylint: disable=import-error +from PyQt5 import QtWidgets # pylint: disable=import-error from . import ui_logdlg # pylint: disable=no-name-in-module from . import clipboard @@ -33,7 +32,7 @@ LOG_DISPLAY_SIZE = 1024*1024 -class LogDialog(ui_logdlg.Ui_LogDialog, QtGui.QDialog): +class LogDialog(ui_logdlg.Ui_LogDialog, QtWidgets.QDialog): # pylint: disable=too-few-public-methods def __init__(self, app, log_path, parent=None): @@ -45,9 +44,10 @@ def __init__(self, app, log_path, parent=None): self.setupUi(self) self.setWindowTitle(log_path) - self.connect(self.copy_to_qubes_clipboard, - QtCore.SIGNAL("clicked()"), - self.copy_to_clipboard_triggered) + self.copy_to_qubes_clipboard.clicked.connect( + self.copy_to_clipboard_triggered) + self.copy_to_qubes_clipboard.clicked.connect( + self.copy_to_clipboard_triggered) self.__init_log_text__() @@ -64,6 +64,7 @@ def __init_log_text__(self): self.displayed_text += log.read() log.close() self.log_text.setPlainText(self.displayed_text) + self.log_text.show() def copy_to_clipboard_triggered(self): clipboard.copy_text_to_qubes_clipboard(self.displayed_text) @@ -71,7 +72,7 @@ def copy_to_clipboard_triggered(self): def main(): qubes_app = Qubes() - qt_app = QtGui.QApplication(sys.argv) + qt_app = QtWidgets.QApplication(sys.argv) log_window = LogDialog(qubes_app, sys.argv[1]) log_window.show() diff --git a/qubesmanager/multiselectwidget.py b/qubesmanager/multiselectwidget.py index c7338898..bad80053 100644 --- a/qubesmanager/multiselectwidget.py +++ b/qubesmanager/multiselectwidget.py @@ -1,12 +1,13 @@ -from PyQt4 import QtCore, QtGui # pylint: disable=import-error -from . import ui_multiselectwidget # pylint: disable=no-name-in-module +from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error +from . import ui_multiselectwidget # pylint: disable=no-name-in-module + class MultiSelectWidget( - ui_multiselectwidget.Ui_MultiSelectWidget, QtGui.QWidget): + ui_multiselectwidget.Ui_MultiSelectWidget, QtWidgets.QWidget): - __pyqtSignals__ = ("selected_changed()",) - __pyqtSignals__ = ("items_added(PyQt_PyObject)",) - __pyqtSignals__ = ("items_removed(PyQt_PyObject)",) + selectedChanged = QtCore.pyqtSignal() + itemsAdded = QtCore.pyqtSignal(list) + itemsRemoved = QtCore.pyqtSignal(list) def __init__(self, parent=None): super(MultiSelectWidget, self).__init__(parent) @@ -16,9 +17,9 @@ def __init__(self, parent=None): self.remove_selected_button.clicked.connect(self.remove_selected) self.remove_all_button.clicked.connect(self.remove_all) self.available_list.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) + QtWidgets.QAbstractItemView.ExtendedSelection) self.selected_list.setSelectionMode( - QtGui.QAbstractItemView.ExtendedSelection) + QtWidgets.QAbstractItemView.ExtendedSelection) def switch_selected(self, src, dst): selected = src.selectedItems() @@ -30,11 +31,11 @@ def switch_selected(self, src, dst): dst.addItem(item) items.append(item) dst.sortItems() - self.emit(QtCore.SIGNAL("selected_changed()")) + self.selectedChanged.emit() if src is self.selected_list: - self.emit(QtCore.SIGNAL("items_removed(PyQt_PyObject)"), items) + self.itemsRemoved.emit(items) else: - self.emit(QtCore.SIGNAL("items_added(PyQt_PyObject)"), items) + self.itemsAdded.emit(items) def add_selected(self): self.switch_selected(self.available_list, self.selected_list) @@ -49,12 +50,11 @@ def move_all(self, src, dst): dst.addItem(item) items.append(item) dst.sortItems() - self.emit(QtCore.SIGNAL("selected_changed()")) + self.selectedChanged.emit() if src is self.selected_list: - self.emit(QtCore.SIGNAL("items_removed(PyQt_PyObject)"), items) + self.itemsRemoved.emit(items) else: - self.emit(QtCore.SIGNAL("items_added(PyQt_PyObject)"), items) - + self.itemsAdded.emit(items) def add_all(self): self.move_all(self.available_list, self.selected_list) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 015bac8f..e957981c 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -37,8 +37,7 @@ from qubesadmin import utils from qubesadmin import events -from PyQt4 import QtGui # pylint: disable=import-error -from PyQt4 import QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error from qubesmanager.about import AboutDialog @@ -54,7 +53,7 @@ from . import common_threads -class SearchBox(QtGui.QLineEdit): +class SearchBox(QtWidgets.QLineEdit): def __init__(self, parent=None): super(SearchBox, self).__init__(parent) self.focusing = False @@ -184,7 +183,7 @@ def update(self, update_size_on_disk=False, event=None): # AdminAPI pass - #force re-sorting + # force re-sorting self.table.setSortingEnabled(True) @@ -229,16 +228,25 @@ def check_if_vm_has_shutdown(self): if vm_is_running and vm_start_time \ and vm_start_time < self.shutdown_started: if self.timeout_reached(): - reply = QtGui.QMessageBox.question( - None, self.tr("Qube Shutdown"), - self.tr( + + msgbox = QtWidgets.QMessageBox(self.caller) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + msgbox.setWindowTitle(self.tr("Qube Shutdown")) + msgbox.setText(self.tr( "The Qube '{0}' hasn't shutdown within the last " "{1} seconds, do you want to kill it?
").format( - vm.name, self.shutdown_time / 1000), - self.tr("Kill it!"), + vm.name, self.shutdown_time / 1000)) + kill_button = msgbox.addButton( + self.tr("Kill it!"), QtWidgets.QMessageBox.YesRole) + wait_button = msgbox.addButton( self.tr("Wait another {0} seconds...").format( - self.shutdown_time / 1000)) - if reply == 0: + self.shutdown_time / 1000), + QtWidgets.QMessageBox.NoRole) + msgbox.setDefaultButton(wait_button) + msgbox.exec_() + msgbox.deleteLater() + + if msgbox.clickedButton() is kill_button: try: vm.kill() except exc.QubesVMNotStartedError: @@ -290,8 +298,9 @@ def run(self): user="root", input=dsa4371update.read()) if stdout == b'changed=yes\n': - subprocess.call(['notify-send', '-i', 'dialog-information', - 'Debian DSA-4371 fix installed in {}'.format( + subprocess.call( + ['notify-send', '-i', 'dialog-information', + 'Debian DSA-4371 fix installed in {}'.format( self.vm.name)]) elif stdout == b'changed=no\n': pass @@ -299,8 +308,8 @@ def run(self): raise exc.QubesException( "Failed to apply DSA-4371 fix: {}".format( stderr.decode('ascii'))) - self.vm.run_service("qubes.InstallUpdatesGUI",\ - user="root", wait=False) + self.vm.run_service("qubes.InstallUpdatesGUI", + user="root", wait=False) except (ChildProcessError, exc.QubesException) as ex: self.msg = ("Error on qube update!", str(ex)) @@ -318,7 +327,7 @@ def run(self): self.msg = ("Error while running command!", str(ex)) -class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): +class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtWidgets.QMainWindow): # pylint: disable=too-many-instance-attributes row_height = 30 column_width = 200 @@ -337,8 +346,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow): "Include in backups": 9, "Last backup": 10, "Default DispVM": 11, - "Is DVM Template": 12 - } + "Is DVM Template": 12} def __init__(self, qt_app, qubes_app, dispatcher, parent=None): # pylint: disable=unused-argument @@ -355,8 +363,7 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): QtCore.QRegExp("[a-zA-Z0-9_-]*", QtCore.Qt.CaseInsensitive), None)) self.searchContainer.addWidget(self.searchbox) - self.connect(self.table, QtCore.SIGNAL("itemSelectionChanged()"), - self.table_selection_changed) + self.table.itemSelectionChanged.connect(self.table_selection_changed) self.table.setColumnWidth(0, self.column_width) @@ -393,12 +400,12 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.table.setColumnWidth(self.columns_indices["Label"], 40) self.table.setColumnWidth(self.columns_indices["Type"], 40) - self.table.horizontalHeader().setResizeMode( - QtGui.QHeaderView.Interactive) + self.table.horizontalHeader().setSectionResizeMode( + QtWidgets.QHeaderView.Interactive) self.table.horizontalHeader().setStretchLastSection(True) self.table.horizontalHeader().setMinimumSectionSize(40) - self.context_menu = QtGui.QMenu(self) + self.context_menu = QtWidgets.QMenu(self) self.context_menu.addAction(self.action_settings) self.context_menu.addAction(self.action_editfwrules) @@ -423,11 +430,11 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.context_menu.addMenu(self.logs_menu) self.context_menu.addSeparator() - self.tools_context_menu = QtGui.QMenu(self) + self.tools_context_menu = QtWidgets.QMenu(self) self.tools_context_menu.addAction(self.action_toolbar) self.tools_context_menu.addAction(self.action_menubar) - self.dom0_context_menu = QtGui.QMenu(self) + self.dom0_context_menu = QtWidgets.QMenu(self) self.dom0_context_menu.addAction(self.action_global_settings) self.dom0_context_menu.addAction(self.action_updatevm) self.dom0_context_menu.addSeparator() @@ -435,42 +442,29 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.dom0_context_menu.addMenu(self.logs_menu) self.dom0_context_menu.addSeparator() - self.connect( - self.table.horizontalHeader(), - QtCore.SIGNAL("sortIndicatorChanged(int, Qt::SortOrder)"), + self.table.horizontalHeader().sortIndicatorChanged.connect( self.sort_indicator_changed) - self.connect(self.table, - QtCore.SIGNAL("customContextMenuRequested(const QPoint&)"), - self.open_context_menu) - self.connect(self.menubar, - QtCore.SIGNAL("customContextMenuRequested(const QPoint&)"), - lambda pos: self.open_tools_context_menu(self.menubar, - pos)) - self.connect(self.toolbar, - QtCore.SIGNAL("customContextMenuRequested(const QPoint&)"), - lambda pos: self.open_tools_context_menu(self.toolbar, - pos)) - self.connect(self.logs_menu, QtCore.SIGNAL("triggered(QAction *)"), - self.show_log) - - self.connect(self.searchbox, - QtCore.SIGNAL("textChanged(const QString&)"), - self.do_search) + self.table.customContextMenuRequested.connect(self.open_context_menu) + self.menubar.customContextMenuRequested.connect( + lambda pos: self.open_tools_context_menu(self.menubar, pos)) + self.toolbar.customContextMenuRequested.connect( + lambda pos: self.open_tools_context_menu(self.toolbar, pos)) + self.logs_menu.triggered.connect(self.show_log) + + self.searchbox.textChanged.connect(self.do_search) self.table.setContentsMargins(0, 0, 0, 0) self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) self.layout().setContentsMargins(0, 0, 0, 0) - self.connect(self.action_menubar, QtCore.SIGNAL("toggled(bool)"), - self.showhide_menubar) - self.connect(self.action_toolbar, QtCore.SIGNAL("toggled(bool)"), - self.showhide_toolbar) + self.action_menubar.toggled.connect(self.showhide_menubar) + self.action_toolbar.toggled.connect(self.showhide_toolbar) try: self.load_manager_settings() except Exception as ex: # pylint: disable=broad-except - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Manager settings unreadable"), self.tr("Qube Manager settings cannot be parsed. Previously " "saved display settings may not be restored " @@ -492,7 +486,7 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.on_domain_status_changed) dispatcher.add_handler('domain-stopped', self.on_domain_status_changed) dispatcher.add_handler('domain-pre-shutdown', - self.on_domain_status_changed) + self.on_domain_status_changed) dispatcher.add_handler('domain-shutdown', self.on_domain_status_changed) dispatcher.add_handler('domain-paused', self.on_domain_status_changed) dispatcher.add_handler('domain-unpaused', self.on_domain_status_changed) @@ -517,6 +511,10 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): timer.start(1000 * 30) # 30s self.check_updates() + # select the first row of the table to make sure menu actions are + # correctly initialized + self.table.selectRow(0) + def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == QtCore.Qt.Key_Escape: self.searchbox.clear() @@ -532,13 +530,13 @@ def clear_threads(self): if thread.msg: (title, msg) = thread.msg if thread.msg_is_success: - QtGui.QMessageBox.information( - None, + QtWidgets.QMessageBox.information( + self, self.tr(title), self.tr(msg)) else: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr(title), self.tr(msg)) @@ -586,7 +584,7 @@ def on_domain_removed(self, _submitter, _event, **kwargs): row_to_delete = row qid_to_delete = qid if not row_to_delete: - return # for some reason, the VM was removed in some other way + return # for some reason, the VM was removed in some other way del self.vms_in_table[qid_to_delete] self.table.removeRow(row_to_delete.name_widget.row()) @@ -663,7 +661,7 @@ def fill_table(self): self.table.setRowCount(len(vms_list)) - progress = QtGui.QProgressDialog( + progress = QtWidgets.QProgressDialog( self.tr( "Loading Qube Manager..."), "", 0, len(vms_list)) progress.setWindowTitle(self.tr("Qube Manager")) @@ -806,7 +804,7 @@ def action_removevm_triggered(self): manager_utils.format_dependencies_list(dependencies) + \ "
" - info_dialog = QtGui.QMessageBox(self) + info_dialog = QtWidgets.QMessageBox(self) info_dialog.setWindowTitle(self.tr("Warning!")) info_dialog.setText( self.tr("This qube cannot be removed. It is used as:" @@ -818,8 +816,8 @@ def action_removevm_triggered(self): return - (requested_name, ok) = QtGui.QInputDialog.getText( - None, self.tr("Qube Removal Confirmation"), + (requested_name, ok) = QtWidgets.QInputDialog.getText( + self, self.tr("Qube Removal Confirmation"), self.tr("Are you sure you want to remove the Qube '{0}'" "?
All data on this Qube's private storage will be " "lost!

Type the name of the Qube ({1}) below " @@ -831,8 +829,8 @@ def action_removevm_triggered(self): if requested_name != vm.name: # name did not match - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Qube removal confirmation failed"), self.tr( "Entered name did not match! Not removing " @@ -856,7 +854,7 @@ def action_clonevm_triggered(self): while name_format % name_number in self.qubes_app.domains.keys(): name_number += 1 - (clone_name, ok) = QtGui.QInputDialog.getText( + (clone_name, ok) = QtWidgets.QInputDialog.getText( self, self.tr('Qubes clone Qube'), self.tr('Enter name for Qube {} clone:').format(vm.name), text=(name_format % name_number)) @@ -866,13 +864,13 @@ def action_clonevm_triggered(self): name_in_use = clone_name in self.qubes_app.domains if name_in_use: - QtGui.QMessageBox.warning( - None, self.tr("Name already in use!"), + QtWidgets.QMessageBox.warning( + self, self.tr("Name already in use!"), self.tr("There already exists a qube called '{}'. " "Cloning aborted.").format(clone_name)) return - self.progress = QtGui.QProgressDialog( + self.progress = QtWidgets.QProgressDialog( self.tr( "Cloning Qube..."), "", 0, 0) self.progress.setCancelButton(None) @@ -894,8 +892,8 @@ def action_resumevm_triggered(self): try: vm.unpause() except exc.QubesException as ex: - QtGui.QMessageBox.warning( - None, self.tr("Error unpausing Qube!"), + QtWidgets.QMessageBox.warning( + self, self.tr("Error unpausing Qube!"), self.tr("ERROR: {0}").format(ex)) return @@ -923,8 +921,8 @@ def action_pausevm_triggered(self): try: vm.pause() except exc.QubesException as ex: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Error pausing Qube!"), self.tr("ERROR: {0}").format(ex)) return @@ -934,14 +932,15 @@ def action_pausevm_triggered(self): def action_shutdownvm_triggered(self): vm = self.get_selected_vm() - reply = QtGui.QMessageBox.question( - None, self.tr("Qube Shutdown Confirmation"), + reply = QtWidgets.QMessageBox.question( + self, self.tr("Qube Shutdown Confirmation"), self.tr("Are you sure you want to power down the Qube" " '{0}'?
This will shutdown all the " "running applications within this Qube.").format( - vm.name), QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) + vm.name), + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) - if reply == QtGui.QMessageBox.Yes: + if reply == QtWidgets.QMessageBox.Yes: self.shutdown_vm(vm) def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, @@ -949,8 +948,8 @@ def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, try: vm.shutdown() except exc.QubesException as ex: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Error shutting down Qube!"), self.tr("ERROR: {0}").format(ex)) return @@ -967,14 +966,14 @@ def shutdown_vm(self, vm, shutdown_time=vm_shutdown_timeout, def action_restartvm_triggered(self): vm = self.get_selected_vm() - reply = QtGui.QMessageBox.question( - None, self.tr("Qube Restart Confirmation"), + reply = QtWidgets.QMessageBox.question( + self, self.tr("Qube Restart Confirmation"), self.tr("Are you sure you want to restart the Qube '{0}'?" "
This will shutdown all the running " "applications within this Qube.").format(vm.name), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) - if reply == QtGui.QMessageBox.Yes: + if reply == QtWidgets.QMessageBox.Yes: # in case the user shut down the VM in the meantime if vm.is_running(): self.shutdown_vm(vm, and_restart=True) @@ -997,17 +996,17 @@ def action_killvm_triggered(self): "shutdown!) all the running applications within " "this Qube.").format(vm.name) - reply = QtGui.QMessageBox.question( - None, self.tr("Qube Kill Confirmation"), info, - QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel, - QtGui.QMessageBox.Cancel) + reply = QtWidgets.QMessageBox.question( + self, self.tr("Qube Kill Confirmation"), info, + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel, + QtWidgets.QMessageBox.Cancel) - if reply == QtGui.QMessageBox.Yes: + if reply == QtWidgets.QMessageBox.Yes: try: vm.kill() except exc.QubesException as ex: - QtGui.QMessageBox.critical( - None, self.tr("Error while killing Qube!"), + QtWidgets.QMessageBox.critical( + self, self.tr("Error while killing Qube!"), self.tr( "An exception ocurred while killing {0}.
" "ERROR: {1}").format(vm.name, ex)) @@ -1057,13 +1056,13 @@ def action_updatevm_triggered(self): vm = self.get_selected_vm() if not vm.is_running(): - reply = QtGui.QMessageBox.question( - None, self.tr("Qube Update Confirmation"), + reply = QtWidgets.QMessageBox.question( + self, self.tr("Qube Update Confirmation"), self.tr( "{0}
The Qube has to be running to be updated." "
Do you want to start it?
").format(vm.name), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel) - if reply != QtGui.QMessageBox.Yes: + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) + if reply != QtWidgets.QMessageBox.Yes: return thread = UpdateVMThread(vm) @@ -1071,14 +1070,13 @@ def action_updatevm_triggered(self): thread.finished.connect(self.clear_threads) thread.start() - # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_run_command_in_vm_triggered') def action_run_command_in_vm_triggered(self): # pylint: disable=invalid-name vm = self.get_selected_vm() - (command_to_run, ok) = QtGui.QInputDialog.getText( + (command_to_run, ok) = QtWidgets.QInputDialog.getText( self, self.tr('Qubes command entry'), self.tr('Run command in {}:').format(vm.name)) if not ok or command_to_run == "": @@ -1101,8 +1099,8 @@ def action_set_keyboard_layout_triggered(self): def action_editfwrules_triggered(self): with common_threads.busy_cursor(): vm = self.get_selected_vm() - settings_window = settings.VMSettingsWindow(vm, self.qt_app,\ - "firewall") + settings_window = settings.VMSettingsWindow(vm, self.qt_app, + "firewall") settings_window.exec_() # noinspection PyArgumentList @@ -1132,16 +1130,16 @@ def action_show_network_triggered(self): @QtCore.pyqtSlot(name='on_action_restore_triggered') def action_restore_triggered(self): with common_threads.busy_cursor(): - restore_window = restore.RestoreVMsWindow(self.qt_app,\ - self.qubes_app) + restore_window = restore.RestoreVMsWindow(self.qt_app, + self.qubes_app) restore_window.exec_() # noinspection PyArgumentList @QtCore.pyqtSlot(name='on_action_backup_triggered') def action_backup_triggered(self): with common_threads.busy_cursor(): - backup_window = backup.BackupVMsWindow(self.qt_app, self.qubes_app, - self.dispatcher, self) + backup_window = backup.BackupVMsWindow( + self.qt_app, self.qubes_app, self.dispatcher, self) backup_window.show() # noinspection PyArgumentList @@ -1227,7 +1225,7 @@ def action_about_qubes_triggered(self): # pylint: disable=no-self-use about.exec_() def createPopupMenu(self): # pylint: disable=invalid-name - menu = QtGui.QMenu() + menu = QtWidgets.QMenu() menu.addAction(self.action_toolbar) menu.addAction(self.action_menubar) return menu @@ -1302,9 +1300,9 @@ def handle_exception(exc_type, exc_value, exc_traceback): strace += "line no.: %d\n" % line strace += "file: %s\n" % filename - msg_box = QtGui.QMessageBox() + msg_box = QtWidgets.QMessageBox() msg_box.setDetailedText(strace) - msg_box.setIcon(QtGui.QMessageBox.Critical) + msg_box.setIcon(QtWidgets.QMessageBox.Critical) msg_box.setWindowTitle("Houston, we have a problem...") msg_box.setText("Whoops. A critical error has occured. " "This is most likely a bug in Qubes Manager.

" @@ -1323,7 +1321,7 @@ def loop_shutdown(): def main(): - qt_app = QtGui.QApplication(sys.argv) + qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName("Qube Manager") diff --git a/qubesmanager/releasenotes.py b/qubesmanager/releasenotes.py index fd781d5d..7ac3a7c1 100644 --- a/qubesmanager/releasenotes.py +++ b/qubesmanager/releasenotes.py @@ -20,7 +20,7 @@ # with this program; if not, see . # # -from PyQt4.QtGui import QDialog # pylint: disable=import-error +from PyQt5.QtWidgets import QDialog # pylint: disable=import-error from . import ui_releasenotes # pylint: disable=no-name-in-module diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index d8a4a276..a38f46f2 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python3 # # The Qubes OS Project, http://www.qubes-os.org # @@ -21,8 +21,7 @@ # import sys -from PyQt4 import QtCore # pylint: disable=import-error -from PyQt4 import QtGui # pylint: disable=import-error +from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error import os import os.path import traceback @@ -36,7 +35,7 @@ from . import backup_utils from multiprocessing import Queue -from multiprocessing.queues import Empty +from queue import Empty from qubesadmin import Qubes, exc from qubesadmin.backup import restore @@ -72,7 +71,7 @@ def run(self): self.tr("Finished successfully!")) -class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard): +class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtWidgets.QWizard): def __init__(self, qt_app, qubes_app, parent=None): super(RestoreVMsWindow, self).__init__(parent) @@ -101,22 +100,16 @@ def __init__(self, qt_app, qubes_app, parent=None): self.select_vms_widget = multiselectwidget.MultiSelectWidget(self) self.select_vms_layout.insertWidget(1, self.select_vms_widget) - self.connect(self, - QtCore.SIGNAL("currentIdChanged(int)"), - self.current_page_changed) - self.dir_line_edit.connect(self.dir_line_edit, - QtCore.SIGNAL("textChanged(QString)"), - self.backup_location_changed) + self.currentIdChanged.connect(self.current_page_changed) + self.dir_line_edit.textChanged.connect(self.backup_location_changed) self.select_dir_page.isComplete = self.has_selected_dir self.select_vms_page.isComplete = self.has_selected_vms self.confirm_page.isComplete = self.all_vms_good # FIXME # this causes to run isComplete() twice, I don't know why - self.select_vms_page.connect( - self.select_vms_widget, - QtCore.SIGNAL("selected_changed()"), - QtCore.SIGNAL("completeChanged()")) + self.select_vms_widget.selectedChanged.connect( + self.select_vms_page.completeChanged.emit) backup_utils.fill_appvms_list(self) @@ -169,7 +162,8 @@ def __fill_vms_list__(self): continue self.select_vms_widget.available_list.addItem(vmname) except exc.QubesException as ex: - QtGui.QMessageBox.warning(None, self.tr("Restore error!"), str(ex)) + QtWidgets.QMessageBox.warning( + self, self.tr("Restore error!"), str(ex)) self.restart() def append_output(self, text): @@ -201,7 +195,7 @@ def current_page_changed(self, page_id): # pylint: disable=unused-argument self.confirm_text_edit.setFontFamily("Monospace") self.confirm_text_edit.setText(self.func_output) - self.confirm_page.emit(QtCore.SIGNAL("completeChanged()")) + self.confirm_page.completeChanged.emit() elif self.currentPage() is self.commit_page: self.button(self.FinishButton).setDisabled(True) @@ -226,8 +220,8 @@ def thread_finished(self): self.progress_bar.setValue(100) if self.thread.msg: - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr("Restore qubes"), self.tr(self.thread.msg)) @@ -260,7 +254,6 @@ def update_log(self): except Empty: pass - def all_vms_good(self): for vm_info in self.vms_to_restore.values(): if not vm_info.vm: @@ -296,7 +289,7 @@ def has_selected_vms(self): def backup_location_changed(self, new_dir=None): # pylint: disable=unused-argument - self.select_dir_page.emit(QtCore.SIGNAL("completeChanged()")) + self.select_dir_page.completeChanged.emit() # Bases on the original code by: @@ -308,18 +301,16 @@ def handle_exception(exc_type, exc_value, exc_traceback): filename = os.path.basename(filename) error = "%s: %s" % (exc_type.__name__, exc_value) - QtGui.QMessageBox.critical(None, "Houston, we have a problem...", - "Whoops. A critical error has occured. " - "This is most likely a bug " - "in Qubes Restore VMs application.

" - "%s" % error + - "at line %d of file %s.

" - % (line, filename)) + QtWidgets.QMessageBox.critical( + None, "Houston, we have a problem...", + "Whoops. A critical error has occured. This is most likely a bug " + "in Qubes Restore VMs application.

%s" % error + + "at line %d of file %s.

" % (line, filename)) def main(): - qt_app = QtGui.QApplication(sys.argv) + qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName("Qubes Restore VMs") diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 40ec22b3..df83cc41 100644 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -41,10 +41,11 @@ from .appmenu_select import AppmenuSelectManager from . import firewall -from PyQt4 import QtCore, QtGui # pylint: disable=import-error +from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error from . import ui_settingsdlg # pylint: disable=no-name-in-module + # pylint: disable=too-few-public-methods class RenameVMThread(QtCore.QThread): def __init__(self, vm, new_vm_name, dependencies): @@ -121,7 +122,7 @@ def run(self): # pylint: disable=too-many-instance-attributes -class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog): +class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog): tabs_indices = collections.OrderedDict(( ('basic', 0), ('advanced', 1), @@ -151,7 +152,7 @@ def __init__(self, vm, qapp, init_page="basic", parent=None): assert idx in range(self.tabWidget.count()) self.tabWidget.setCurrentIndex(idx) - self.buttonBox.button(QtGui.QDialogButtonBox.Apply).clicked.connect( + self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect( self.apply) self.tabWidget.currentChanged.connect(self.current_tab_changed) @@ -177,12 +178,8 @@ def __init__(self, vm, qapp, init_page="basic", parent=None): self.__init_advanced_tab__() self.include_in_balancing.stateChanged.connect( self.include_in_balancing_changed) - self.connect(self.init_mem, - QtCore.SIGNAL("editingFinished()"), - self.check_mem_changes) - self.connect(self.max_mem_size, - QtCore.SIGNAL("editingFinished()"), - self.check_mem_changes) + self.init_mem.editingFinished.connect(self.check_mem_changes) + self.max_mem_size.editingFinished.connect(self.check_mem_changes) self.boot_from_device_button.clicked.connect( self.boot_from_cdrom_button_pressed) @@ -207,9 +204,7 @@ def __init__(self, vm, qapp, init_page="basic", parent=None): ####### devices tab self.__init_devices_tab__() - self.connect(self.dev_list, - QtCore.SIGNAL("selected_changed()"), - self.devices_selection_changed) + self.dev_list.selectedChanged.connect(self.devices_selection_changed) self.no_strict_reset_button.clicked.connect( self.strict_reset_button_pressed) self.current_strict_reset_list = [] @@ -240,8 +235,8 @@ def clear_threads(self): if thread.msg: (title, msg) = thread.msg - QtGui.QMessageBox.warning( - None, + QtWidgets.QMessageBox.warning( + self, self.tr(title), self.tr(msg)) @@ -268,10 +263,10 @@ def save_changes(self): error = self.__save_changes__() if error: - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, - self.tr("Error while changing settings for {0}!"\ - ).format(self.vm.name),\ + self.tr("Error while changing settings for {0}!" + ).format(self.vm.name), self.tr("ERROR: {0}").format('\n'.join(error))) def apply(self): @@ -331,7 +326,7 @@ def check_network_availability(self): netvm is not None and not netvm.features.check_with_template('qubes-firewall', False)) if netvm is None: - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Qube configuration problem!"), self.tr('This qube has networking disabled ' @@ -340,17 +335,17 @@ def check_network_availability(self): 'please enable networking.') ) if netvm is not None and \ - not netvm.features.check_with_template(\ + not netvm.features.check_with_template( 'qubes-firewall', False): - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Qube configuration problem!"), - self.tr("The '{vm}' qube is network connected to "\ - "'{netvm}', which does not support firewall!
"\ - "You may edit the '{vm}' qube firewall rules, but "\ - "these will not take any effect until you connect it "\ - "to a working Firewall qube.").format(\ - vm=self.vm.name, netvm=netvm.name)) + self.tr("The '{vm}' qube is network connected to " + "'{netvm}', which does not support firewall!
" + "You may edit the '{vm}' qube firewall rules, but " + "these will not take any effect until you connect it " + "to a working Firewall qube.").format( + vm=self.vm.name, netvm=netvm.name)) def current_tab_changed(self, idx): if idx == self.tabs_indices["firewall"]: @@ -536,7 +531,7 @@ def __apply_basic_tab__(self): def check_mem_changes(self): if self.max_mem_size.value() < self.init_mem.value(): - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Warning!"), self.tr("Max memory can not be less than initial memory.
" @@ -546,7 +541,7 @@ def check_mem_changes(self): # max_mem_size/10.79 in order to allow scaling up to # max_mem_size (or else "add_memory() failed: -17" problem) if self.init_mem.value() * 10 < self.max_mem_size.value(): - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Warning!"), self.tr("Initial memory can not be less than one tenth " @@ -579,7 +574,6 @@ def check_warn_dispvmnetvm(self): else: self.warn_netvm_dispvm.setVisible(False) - def rename_vm(self): dependencies = admin_utils.vm_dependencies(self.vm.app, self.vm) @@ -589,7 +583,7 @@ def rename_vm(self): and vm.is_running()] if running_dependencies: - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Qube cannot be renamed!"), self.tr( @@ -599,7 +593,7 @@ def rename_vm(self): ", ".join(running_dependencies))) return - new_vm_name, ok = QtGui.QInputDialog.getText( + new_vm_name, ok = QtWidgets.QInputDialog.getText( self, self.tr('Rename qube'), self.tr('New name: (WARNING: all other changes will be discarded)')) @@ -609,7 +603,7 @@ def rename_vm(self): self.threads_list.append(thread) thread.finished.connect(self.clear_threads) - self.progress = QtGui.QProgressDialog( + self.progress = QtWidgets.QProgressDialog( self.tr( "Renaming Qube..."), "", 0, 0) self.progress.setCancelButton(None) @@ -625,7 +619,7 @@ def remove_vm(self): if dependencies: list_text = utils.format_dependencies_list(dependencies) - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Qube cannot be removed!"), self.tr("This qube cannot be removed. It is used as:" @@ -635,8 +629,7 @@ def remove_vm(self): return - - answer, ok = QtGui.QInputDialog.getText( + answer, ok = QtWidgets.QInputDialog.getText( self, self.tr('Delete qube'), self.tr('Are you absolutely sure you want to delete this qube? ' @@ -650,14 +643,14 @@ def remove_vm(self): self.done(0) elif ok: - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Removal cancelled"), self.tr("The qube will not be removed.")) def clone_vm(self): - cloned_vm_name, ok = QtGui.QInputDialog.getText( + cloned_vm_name, ok = QtWidgets.QInputDialog.getText( self, self.tr('Clone qube'), self.tr('Name for the cloned qube:')) @@ -667,7 +660,7 @@ def clone_vm(self): thread.finished.connect(self.clear_threads) self.threads_list.append(thread) - self.progress = QtGui.QProgressDialog( + self.progress = QtWidgets.QProgressDialog( self.tr( "Cloning Qube..."), "", 0, 0) self.progress.setCancelButton(None) @@ -867,9 +860,9 @@ def update_virt_mode_list(self): self.virt_mode.clear() - self.virt_mode_list, self.virt_mode_idx = utils.prepare_choice(\ - self.virt_mode, self.vm, 'virt_mode', choices, None,\ - allow_default=True, transform=(lambda x: str(x).upper())) + self.virt_mode_list, self.virt_mode_idx = utils.prepare_choice( + self.virt_mode, self.vm, 'virt_mode', choices, None, + allow_default=True, transform=(lambda x: str(x).upper())) if old_mode is not None: self.virt_mode.setCurrentIndex(self.virt_mode_list.index(old_mode)) @@ -924,7 +917,7 @@ def __init_devices_tab__(self): attached_devs = list(self.vm.devices['pci'].persistent()) # pylint: disable=too-few-public-methods - class DevListWidgetItem(QtGui.QListWidgetItem): + class DevListWidgetItem(QtWidgets.QListWidgetItem): def __init__(self, dev, unknown=False, parent=None): super(DevListWidgetItem, self).__init__(parent) name = dev.ident.replace('_', ":") + ' ' + dev.description @@ -1070,7 +1063,7 @@ def __init_services_tab__(self): if not feature.startswith('service.'): continue service = feature[len('service.'):] - item = QtGui.QListWidgetItem(service) + item = QtWidgets.QListWidgetItem(service) item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked if self.vm.features[feature] else ui_settingsdlg.QtCore.Qt.Unchecked) @@ -1097,12 +1090,12 @@ def __add_service__(self): srv = str(self.service_line_edit.currentText()).strip() if srv != "": if srv in self.new_srv_dict: - QtGui.QMessageBox.information( + QtWidgets.QMessageBox.information( self, '', self.tr('Service already on the list!')) else: - item = QtGui.QListWidgetItem(srv) + item = QtWidgets.QListWidgetItem(srv) item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked) self.services_list.addItem(item) self.new_srv_dict[srv] = True @@ -1146,9 +1139,10 @@ def __apply_services_tab__(self): def set_fw_model(self, model): self.fw_model = model self.rulesTreeView.setModel(model) - self.rulesTreeView.header().setResizeMode( - QtGui.QHeaderView.ResizeToContents) - self.rulesTreeView.header().setResizeMode(0, QtGui.QHeaderView.Stretch) + self.rulesTreeView.header().setSectionResizeMode( + QtWidgets.QHeaderView.ResizeToContents) + self.rulesTreeView.header().setSectionResizeMode( + 0, QtWidgets.QHeaderView.Stretch) self.set_allow(model.allow) if model.temp_full_access_expire_time: self.temp_full_access.setChecked(True) @@ -1226,9 +1220,9 @@ def handle_exception(exc_type, exc_value, exc_traceback): strace += "line no.: %d\n" % line strace += "file: %s\n" % filename - msg_box = QtGui.QMessageBox() + msg_box = QtWidgets.QMessageBox() msg_box.setDetailedText(strace) - msg_box.setIcon(QtGui.QMessageBox.Critical) + msg_box.setIcon(QtWidgets.QMessageBox.Critical) msg_box.setWindowTitle("Houston, we have a problem...") msg_box.setText("Whoops. A critical error has occured. " "This is most likely a bug in Qubes Manager.

" @@ -1254,7 +1248,7 @@ def main(args=None): args = parser.parse_args(args) vm = args.domains.pop() - qapp = QtGui.QApplication(sys.argv) + qapp = QtWidgets.QApplication(sys.argv) qapp.setOrganizationName('Invisible Things Lab') qapp.setOrganizationDomain("https://www.qubes-os.org/") qapp.setApplicationName("Qube Settings") diff --git a/qubesmanager/table_widgets.py b/qubesmanager/table_widgets.py index 438dba62..9ec9c1d0 100644 --- a/qubesmanager/table_widgets.py +++ b/qubesmanager/table_widgets.py @@ -20,8 +20,7 @@ # with this program; if not, see . import datetime -from PyQt4 import QtGui # pylint: disable=import-error -from PyQt4 import QtCore # pylint: disable=import-error +from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error # pylint: disable=too-few-public-methods power_order = QtCore.Qt.DescendingOrder @@ -31,7 +30,7 @@ row_height = 30 -class VmIconWidget(QtGui.QWidget): +class VmIconWidget(QtWidgets.QWidget): def __init__(self, icon_path, enabled=True, size_multiplier=0.7, tooltip=None, parent=None, icon_sz=(32, 32)): # pylint: disable=unused-argument @@ -39,13 +38,13 @@ def __init__(self, icon_path, enabled=True, size_multiplier=0.7, self.enabled = enabled self.size_multiplier = size_multiplier - self.label_icon = QtGui.QLabel() + self.label_icon = QtWidgets.QLabel() self.set_icon(icon_path) if tooltip is not None: self.label_icon.setToolTip(tooltip) - layout = QtGui.QHBoxLayout() + layout = QtWidgets.QHBoxLayout() layout.addWidget(self.label_icon) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) @@ -72,7 +71,7 @@ def set_icon(self, icon_path): class VmTypeWidget(VmIconWidget): - class VmTypeItem(QtGui.QTableWidgetItem): + class VmTypeItem(QtWidgets.QTableWidgetItem): def __init__(self, value, vm): super(VmTypeWidget.VmTypeItem, self).__init__() self.value = value @@ -82,7 +81,7 @@ def __init__(self, value, vm): def set_value(self, value): self.value = value - #pylint: disable=too-many-return-statements + # pylint: disable=too-many-return-statements def __lt__(self, other): if self.qid == 0: return True @@ -120,7 +119,7 @@ def get_vm_icon(self, vm): class VmLabelWidget(VmIconWidget): - class VmLabelItem(QtGui.QTableWidgetItem): + class VmLabelItem(QtWidgets.QTableWidgetItem): def __init__(self, value, vm): super(VmLabelWidget.VmLabelItem, self).__init__() self.value = value @@ -130,7 +129,7 @@ def __init__(self, value, vm): def set_value(self, value): self.value = value - #pylint: disable=too-many-return-statements + # pylint: disable=too-many-return-statements def __lt__(self, other): if self.qid == 0: return True @@ -159,10 +158,11 @@ def update(self): self.set_icon(icon_path) -class VmStatusIcon(QtGui.QLabel): +class VmStatusIcon(QtWidgets.QLabel): def __init__(self, vm, parent=None): super(VmStatusIcon, self).__init__(parent) self.vm = vm + self.status = None self.set_on_icon() self.previous_power_state = self.vm.get_power_state() @@ -191,8 +191,8 @@ def set_on_icon(self): self.setFixedSize(icon_sz) -class VmInfoWidget(QtGui.QWidget): - class VmInfoItem(QtGui.QTableWidgetItem): +class VmInfoWidget(QtWidgets.QWidget): + class VmInfoItem(QtWidgets.QTableWidgetItem): def __init__(self, on_icon, upd_info_item, vm): super(VmInfoWidget.VmInfoItem, self).__init__() self.on_icon = on_icon @@ -232,7 +232,7 @@ def __lt__(self, other): def __init__(self, vm, parent=None): super(VmInfoWidget, self).__init__(parent) self.vm = vm - layout = QtGui.QHBoxLayout() + layout = QtWidgets.QHBoxLayout() self.on_icon = VmStatusIcon(vm) self.upd_info = VmUpdateInfoWidget(vm, show_text=False) @@ -243,9 +243,9 @@ def __init__(self, vm, parent=None): layout.addWidget(self.on_icon) layout.addWidget(self.upd_info) layout.addWidget(self.error_icon) - layout.addItem(QtGui.QSpacerItem(0, 10, - QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Expanding)) + layout.addItem(QtWidgets.QSpacerItem(0, 10, + QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding)) layout.addWidget(self.blk_icon) layout.addWidget(self.rec_icon) @@ -256,15 +256,15 @@ def __init__(self, vm, parent=None): self.blk_icon.setVisible(False) self.error_icon.setVisible(False) - self.table_item = self.VmInfoItem(self.on_icon,\ - self.upd_info.table_item, vm) + self.table_item = self.VmInfoItem(self.on_icon, + self.upd_info.table_item, vm) def update_vm_state(self): self.on_icon.update() self.upd_info.update_outdated() -class VMPropertyItem(QtGui.QTableWidgetItem): +class VMPropertyItem(QtWidgets.QTableWidgetItem): def __init__(self, vm, property_name, empty_function=(lambda x: False), check_default=False): """ @@ -327,7 +327,7 @@ def update(self): font = QtGui.QFont() font.setStyle(QtGui.QFont.StyleItalic) self.setFont(font) - self.setTextColor(QtGui.QColor("gray")) + self.setForeground(QtGui.QBrush(QtGui.QColor("gray"))) self.setText(self.vm.klass) @@ -342,8 +342,8 @@ def update(self): # features man qvm-features -class VmUpdateInfoWidget(QtGui.QWidget): - class VmUpdateInfoItem(QtGui.QTableWidgetItem): +class VmUpdateInfoWidget(QtWidgets.QWidget): + class VmUpdateInfoItem(QtWidgets.QTableWidgetItem): def __init__(self, value, vm): super(VmUpdateInfoWidget.VmUpdateInfoItem, self).__init__() self.value = 0 @@ -371,13 +371,13 @@ def __lt__(self, other): def __init__(self, vm, show_text=True, parent=None): super(VmUpdateInfoWidget, self).__init__(parent) - layout = QtGui.QHBoxLayout() + layout = QtWidgets.QHBoxLayout() self.show_text = show_text if self.show_text: - self.label = QtGui.QLabel("") + self.label = QtWidgets.QLabel("") layout.addWidget(self.label, alignment=QtCore.Qt.AlignCenter) else: - self.icon = QtGui.QLabel("") + self.icon = QtWidgets.QLabel("") layout.addWidget(self.icon, alignment=QtCore.Qt.AlignCenter) self.setLayout(layout) @@ -432,6 +432,8 @@ def update_status_widget(self, state): "The Template must be stopped before changes from its " "current session can be picked up by this qube.") else: + label_text = None + tooltip_text = None icon_path = None if hasattr(self, 'icon'): @@ -445,12 +447,12 @@ def update_status_widget(self, state): if icon_path is not None: self.icon = VmIconWidget(icon_path, True, 0.7) self.icon.setToolTip(tooltip_text) - self.layout().addWidget(self.icon,\ - alignment=QtCore.Qt.AlignCenter) + self.layout().addWidget(self.icon, + alignment=QtCore.Qt.AlignCenter) self.icon.setVisible(True) -class VmSizeOnDiskItem(QtGui.QTableWidgetItem): +class VmSizeOnDiskItem(QtWidgets.QTableWidgetItem): def __init__(self, vm): super(VmSizeOnDiskItem, self).__init__() self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 88e1a082..31ff0a98 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -32,10 +32,7 @@ from qubesadmin import exc from qubesadmin import events -from PyQt4 import QtGui # pylint: disable=import-error -from PyQt4 import QtCore # pylint: disable=import-error -from PyQt4 import Qt # pylint: disable=import-error - +from PyQt5 import QtWidgets, QtGui, QtCore # pylint: disable=import-error from . import ui_templatemanager # pylint: disable=no-name-in-module @@ -43,7 +40,7 @@ class TemplateManagerWindow( - ui_templatemanager.Ui_MainWindow, QtGui.QMainWindow): + ui_templatemanager.Ui_MainWindow, QtWidgets.QMainWindow): def __init__(self, qt_app, qubes_app, dispatcher, parent=None): # pylint: disable=unused-argument @@ -61,11 +58,11 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.prepare_lists() self.initialize_table_events() - self.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect( + self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect( self.apply) - self.buttonBox.button(QtGui.QDialogButtonBox.Cancel).clicked.connect( - self.cancel) - self.buttonBox.button(QtGui.QDialogButtonBox.Reset).clicked.connect( + self.buttonBox.button( + QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.cancel) + self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect( self.reset) self.change_all_combobox.currentIndexChanged.connect( @@ -118,7 +115,7 @@ def initialize_table_events(self): def vm_added(self, _submitter, _event, vm, **_kwargs): # unfortunately, a VM just in the moment of creation may not have # a template it will have in a second - e.g., when cloning - timer = Qt.QTimer() + timer = QtCore.QTimer() timer.setSingleShot(True) timer.timeout.connect(lambda: self._vm_added(vm, timer)) self.timers.append(timer) @@ -239,7 +236,7 @@ def apply(self): errors[vm] = str(ex) if errors: error_messages = [vm + ": " + errors[vm] for vm in errors] - QtGui.QMessageBox.warning( + QtWidgets.QMessageBox.warning( self, self.tr("Errors encountered!"), self.tr( @@ -248,7 +245,7 @@ def apply(self): self.close() -class VMNameItem(QtGui.QTableWidgetItem): +class VMNameItem(QtWidgets.QTableWidgetItem): # pylint: disable=too-few-public-methods def __init__(self, vm): super(VMNameItem, self).__init__() @@ -258,7 +255,7 @@ def __init__(self, vm): self.setIcon(QtGui.QIcon.fromTheme(vm.label.icon)) -class StatusItem(QtGui.QTableWidgetItem): +class StatusItem(QtWidgets.QTableWidgetItem): def __init__(self, vm): super(StatusItem, self).__init__() self.vm = vm @@ -281,7 +278,7 @@ def __lt__(self, other): return self.state < other.state -class CurrentTemplateItem(QtGui.QTableWidgetItem): +class CurrentTemplateItem(QtWidgets.QTableWidgetItem): # pylint: disable=too-few-public-methods def __init__(self, vm): super(CurrentTemplateItem, self).__init__() @@ -295,7 +292,7 @@ def __lt__(self, other): return self.text() < other.text() -class NewTemplateItem(QtGui.QComboBox): +class NewTemplateItem(QtWidgets.QComboBox): def __init__(self, vm, templates, table_widget): super(NewTemplateItem, self).__init__() self.vm = vm @@ -331,7 +328,7 @@ def __init__(self, vm, row_no, table_widget, columns, templates): # state self.state_item = StatusItem(self.vm) table_widget.setItem(row_no, columns.index('State'), self.state_item) - self.checkbox = QtGui.QCheckBox() + self.checkbox = QtWidgets.QCheckBox() # icon and name self.name_item = VMNameItem(self.vm) @@ -343,7 +340,7 @@ def __init__(self, vm, row_no, table_widget, columns, templates): self.current_item) # new template - self.dummy_new_item = QtGui.QTableWidgetItem("qube is running") + self.dummy_new_item = QtWidgets.QTableWidgetItem("qube is running") self.new_item = NewTemplateItem(self.vm, templates, table_widget) table_widget.setItem(row_no, columns.index('New template'), @@ -367,7 +364,7 @@ def vm_state_change(self, is_running, row=None): if not is_running: self.new_item = NewTemplateItem(self.vm, self.templates, self.table_widget) - self.checkbox = QtGui.QCheckBox() + self.checkbox = QtWidgets.QCheckBox() self.table_widget.setCellWidget( row, column_names.index('New template'), self.new_item) @@ -408,9 +405,9 @@ def handle_exception(exc_type, exc_value, exc_traceback): strace += "line no.: %d\n" % line strace += "file: %s\n" % filename - msg_box = QtGui.QMessageBox() + msg_box = QtWidgets.QMessageBox() msg_box.setDetailedText(strace) - msg_box.setIcon(QtGui.QMessageBox.Critical) + msg_box.setIcon(QtWidgets.QMessageBox.Critical) msg_box.setWindowTitle("Houston, we have a problem...") msg_box.setText("Whoops. A critical error has occured. " "This is most likely a bug in Qubes Manager.

" @@ -429,7 +426,7 @@ def loop_shutdown(): def main(): - qt_app = QtGui.QApplication(sys.argv) + qt_app = QtWidgets.QApplication(sys.argv) qt_app.setOrganizationName("The Qubes Project") qt_app.setOrganizationDomain("http://qubes-os.org") qt_app.setApplicationName("Qube Manager") @@ -450,7 +447,7 @@ def main(): asyncio.ensure_future(dispatcher.listen_for_events())) except asyncio.CancelledError: pass - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except loop_shutdown() exc_type, exc_value, exc_traceback = sys.exc_info()[:3] handle_exception(exc_type, exc_value, exc_traceback) diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py index a6a1351f..d3ae1d38 100644 --- a/qubesmanager/utils.py +++ b/qubesmanager/utils.py @@ -24,16 +24,18 @@ import re import qubesadmin -from PyQt4.QtGui import QIcon # pylint: disable=import-error +from PyQt5.QtGui import QIcon # pylint: disable=import-error + def _filter_internal(vm): return (not vm.klass == 'AdminVM' - and not vm.features.get('internal', False)) + and not vm.features.get('internal', False)) + def prepare_choice(widget, holder, propname, choice, default, - filter_function=None, *, - icon_getter=None, allow_internal=None, allow_default=False, - allow_none=False, transform=None): + filter_function=None, *, + icon_getter=None, allow_internal=None, allow_default=False, + allow_none=False, transform=None): # for newly created vms, set propname to None @@ -112,10 +114,13 @@ def prepare_choice(widget, holder, propname, choice, default, return choice_list, idx + def prepare_kernel_choice(widget, holder, propname, default, *args, **kwargs): # TODO get from storage API (pool 'linux-kernel') (suggested by @marmarta) return prepare_choice(widget, holder, propname, - os.listdir('/var/lib/qubes/vm-kernels'), default, *args, **kwargs) + os.listdir('/var/lib/qubes/vm-kernels'), + default, *args, **kwargs) + def prepare_label_choice(widget, holder, propname, default, *args, **kwargs): try: @@ -124,10 +129,12 @@ def prepare_label_choice(widget, holder, propname, default, *args, **kwargs): app = holder return prepare_choice(widget, holder, propname, - sorted(app.labels.values(), key=lambda l: l.index), - default, *args, - icon_getter=(lambda label: QIcon.fromTheme(label.icon)), - **kwargs) + sorted(app.labels.values(), key=lambda l: l.index), + default, *args, + icon_getter=(lambda label: + QIcon.fromTheme(label.icon)), + **kwargs) + def prepare_vm_choice(widget, holder, propname, default, *args, **kwargs): try: @@ -136,11 +143,13 @@ def prepare_vm_choice(widget, holder, propname, default, *args, **kwargs): app = holder return prepare_choice(widget, holder, propname, app.domains, default, - *args, **kwargs) + *args, **kwargs) + def is_debug(): return os.getenv('QUBES_MANAGER_DEBUG', '') not in ('', '0') + def debug(*args, **kwargs): if not is_debug(): return diff --git a/rpm_spec/qmgr.spec.in b/rpm_spec/qmgr.spec.in index 49a3d59a..5b363c46 100644 --- a/rpm_spec/qmgr.spec.in +++ b/rpm_spec/qmgr.spec.in @@ -8,7 +8,7 @@ Vendor: Invisible Things Lab License: GPL URL: http://fixme Requires: python3 -Requires: python3-PyQt4 +Requires: python3-PyQt5 Requires: python3-inotify Requires: python3-qubesadmin >= 4.0.19 Requires: python3-Quamash @@ -17,9 +17,10 @@ Requires: qubes-artwork Requires: pmount Requires: cryptsetup Requires: wmctrl -BuildRequires: python3-PyQt4-devel +BuildRequires: python3-PyQt5-devel BuildRequires: python3-devel -BuildRequires: qt-devel +BuildRequires: qt5-devel +BuildRequires: qt5-linguist AutoReq: 0 Source0: %{name}-%{version}.tar.gz diff --git a/ui/restoredlg.ui b/ui/restoredlg.ui index bb3df0a5..517df942 100644 --- a/ui/restoredlg.ui +++ b/ui/restoredlg.ui @@ -6,8 +6,8 @@ 0 0 - 700 - 399 + 756 + 440 @@ -214,7 +214,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html>