Browse Source

Update Qt version used in qubesmanager to Qt5

Fixed dependencies, places where obsolete functions stopped working,
code fragments that started throwing warnings and an .ui file that
stopped being readable after the update.
Marta Marczykowska-Górecka 4 years ago
parent
commit
e79724f9db

+ 4 - 4
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/

+ 1 - 1
build-deps.list

@@ -1 +1 @@
-PyQt4-devel
+PyQt5-devel

+ 4 - 1
qubesmanager/about.py

@@ -20,7 +20,8 @@
 # with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #
-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 @@ class AboutDialog(ui_about.Ui_AboutDialog, QDialog):
         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_()

+ 7 - 6
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 @@ class AppmenuSelectManager:
         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 @@ class AppmenuSelectManager:
 
     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

+ 33 - 45
qubesmanager/backup.py

@@ -30,8 +30,8 @@ from qubesadmin import utils as admin_utils
 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 os
 import asyncio
 from contextlib import suppress
 
+
 # pylint: disable=too-few-public-methods
 class BackupThread(QtCore.QThread):
     def __init__(self, vm):
@@ -67,7 +68,7 @@ class BackupThread(QtCore.QThread):
             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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
         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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
         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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
 
         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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
         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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
         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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
     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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
                 '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 @@ class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
 
     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 <b>line %d</b> of file <b>%s</b>.<br/><br/>"
         % (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)

+ 5 - 6
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):

+ 12 - 13
qubesmanager/bootfromdevice.py

@@ -21,12 +21,13 @@ import sys
 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 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog):
         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 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog):
                 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 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog):
 
     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 @@ class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog):
             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()

+ 30 - 19
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 os
 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'

+ 3 - 3
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 @@ from qubesadmin import exc
 @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

+ 20 - 15
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 . import utils
 
 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 @@ class CreateVMThread(QtCore.QThread):
                 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 @@ class CreateVMThread(QtCore.QThread):
             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 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         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 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         except LookupError:
             pass
         else:
-            QtGui.QMessageBox.warning(None,
+            QtWidgets.QMessageBox.warning(
+                self,
                 self.tr('Incorrect qube name!'),
                 self.tr('A qube with the name <b>{}</b> already exists in the '
                         'system!').format(name))
@@ -153,20 +157,19 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         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 <b>{}</b>...").format(name), "", 0, 0)
         self.progress.setCancelButton(None)
         self.progress.setModal(True)
@@ -176,7 +179,8 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         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 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         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 @@ class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
         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')

+ 4 - 6
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 @@ class PCIDeviceListWindow(ui_devicelist.Ui_Dialog, QtGui.QDialog):
 
         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()

+ 44 - 39
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 @@ from . import ui_newfwruledlg  # pylint: disable=no-name-in-module
 class FirewallModifiedOutsideError(ValueError):
     pass
 
+
 class QIPAddressValidator(QtGui.QValidator):
     # pylint: disable=too-few-public-methods
     def __init__(self, parent=None):
@@ -40,10 +41,10 @@ class QIPAddressValidator(QtGui.QValidator):
         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 @@ class QIPAddressValidator(QtGui.QValidator):
             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}(?<!-)$", re.IGNORECASE)
         if all(allowed.match(x) for x in hostname.split(".")):
-            return (QtGui.QValidator.Acceptable, input_string, pos)
+            return QtGui.QValidator.Acceptable, input_string, pos
+
+        return QtGui.QValidator.Invalid, input_string, pos
 
-        return (QtGui.QValidator.Invalid, input_string, pos)
 
-class NewFwRuleDlg(QtGui.QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
+class NewFwRuleDlg(QtWidgets.QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
     def __init__(self, parent=None):
         super(NewFwRuleDlg, self).__init__(parent)
         self.setupUi(self)
@@ -84,19 +86,20 @@ class NewFwRuleDlg(QtGui.QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
             QtCore.QRegExp("[a-z][a-z0-9-]+|[0-9]+(-[0-9]+)?",
                            QtCore.Qt.CaseInsensitive), None))
         self.serviceComboBox.setEnabled(False)
-        self.serviceComboBox.setInsertPolicy(QtGui.QComboBox.InsertAtBottom)
+        self.serviceComboBox.setInsertPolicy(QtWidgets.QComboBox.InsertAtBottom)
         self.populate_combos()
-        self.serviceComboBox.setInsertPolicy(QtGui.QComboBox.InsertAtTop)
+        self.serviceComboBox.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
 
     def accept(self):
         if self.tcp_radio.isChecked() or self.udp_radio.isChecked():
             if not self.serviceComboBox.currentText():
-                msg = QtGui.QMessageBox()
-                msg.warning(self, self.tr("Firewall rule"),
+                msg = QtWidgets.QMessageBox()
+                msg.warning(
+                    self, self.tr("Firewall rule"),
                     self.tr("You need to fill service "
                             "name/port for TCP/UDP rule"))
                 return
-        QtGui.QDialog.accept(self)
+        super().accept()
 
     def populate_combos(self):
         example_addresses = [
@@ -122,7 +125,7 @@ class NewFwRuleDlg(QtGui.QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
         self.set_ok_state(True)
 
     def set_ok_state(self, ok_state):
-        ok_button = self.buttonBox.button(QtGui.QDialogButtonBox.Ok)
+        ok_button = self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok)
         if ok_button is not None:
             ok_button.setEnabled(ok_state)
 
@@ -158,10 +161,10 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
                         (service["name"], int(service["port"]),))
 
         self.fw_changed = False
-        self.allow = None # is the default policy allow or deny
-        self.temp_full_access_expire_time = None # temporary full access time
-        self.__vm = None # VM that the model applies to
-        self.__children = None # list of rules in the FW
+        self.allow = None  # is the default policy allow or deny
+        self.temp_full_access_expire_time = None  # temporary full access time
+        self.__vm = None  # VM that the model applies to
+        self.__children = None  # list of rules in the FW
 
     def sort(self, idx, order):
         rev = (order == QtCore.Qt.AscendingOrder)
@@ -172,7 +175,6 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
         index2 = self.createIndex(len(self) - 1, len(self.__column_names) - 1)
         self.dataChanged.emit(index1, index2)
 
-
     def get_service_name(self, port):
         for service in self.__services:
             if str(service[1]) == str(port):
@@ -286,19 +288,19 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
             rules.append(rule)
 
         if not conf['allow']:
-            rules.append(qubesadmin.firewall.Rule(None,
-                action='accept', specialtarget='dns'))
+            rules.append(qubesadmin.firewall.Rule(
+                None, action='accept', specialtarget='dns'))
 
         if not conf['allow']:
-            rules.append(qubesadmin.firewall.Rule(None,
-                action='accept', proto='icmp'))
+            rules.append(qubesadmin.firewall.Rule(
+                None, action='accept', proto='icmp'))
 
         if conf['allow']:
-            rules.append(qubesadmin.firewall.Rule(None,
-                action='accept'))
+            rules.append(qubesadmin.firewall.Rule(
+                None, action='accept'))
         else:
-            rules.append(qubesadmin.firewall.Rule(None,
-                action='drop'))
+            rules.append(qubesadmin.firewall.Rule(
+                None, action='drop'))
 
         vm.firewall.rules = rules
 
@@ -329,7 +331,7 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
 
         conf = {"allow": allow,
                 "rules": list()
-            }
+                }
 
         conf['rules'].extend(self.children)
 
@@ -372,7 +374,8 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
                 try:
                     rule.dsthost = address
                 except ValueError:
-                    QtGui.QMessageBox.warning(None, self.tr("Invalid address"),
+                    QtWidgets.QMessageBox.warning(
+                        dialog, self.tr("Invalid address"),
                         self.tr("Address '{0}' is invalid.").format(address))
                     return
 
@@ -385,11 +388,11 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
                 try:
                     rule.dstports = service
                 except ValueError:
-                    QtGui.QMessageBox.warning(
-                        None,
+                    QtWidgets.QMessageBox.warning(
+                        dialog,
                         self.tr("Invalid port or service"),
-                        self.tr("Port number or service '{0}' is invalid.")
-                                        .format(service))
+                        self.tr("Port number or service '{0}' is "
+                                "invalid.").format(service))
                     return
             elif service:
                 try:
@@ -398,10 +401,12 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
                     if self.get_service_port(service) is not None:
                         rule.dstports = self.get_service_port(service)
                     else:
-                        QtGui.QMessageBox.warning(None,
+                        QtWidgets.QMessageBox.warning(
+                            dialog,
                             self.tr("Invalid port or service"),
-                            self.tr("Port number or service '{0}' is invalid.")
-                                            .format(service))
+                            self.tr(
+                                "Port number or service '{0}' is "
+                                "invalid.".format(service)))
                         return
 
             if row is not None:
@@ -415,7 +420,7 @@ class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
 
         return self.createIndex(row, column, self.children[row])
 
-    def parent(self, child): # pylint: disable=unused-argument,no-self-use
+    def parent(self, child):  # pylint: disable=unused-argument,no-self-use
         return QtCore.QModelIndex()
 
     # pylint: disable=invalid-name,unused-argument

+ 21 - 24
qubesmanager/global_settings.py

@@ -24,7 +24,7 @@ import sys
 import os
 import os.path
 import traceback
-from PyQt4 import QtCore, QtGui  # pylint: disable=import-error
+from PyQt5 import QtWidgets  # pylint: disable=import-error
 
 from qubesadmin import Qubes
 from qubesadmin.utils import parse_size
@@ -36,9 +36,10 @@ from configparser import ConfigParser
 
 qmemman_config_path = '/etc/qubes/qmemman.conf'
 
+
 # pylint: disable=too-many-instance-attributes
 class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
-                           QtGui.QDialog):
+                           QtWidgets.QDialog):
 
     def __init__(self, app, qvm_collection, parent=None):
         super(GlobalSettingsWindow, self).__init__(parent)
@@ -48,11 +49,8 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
 
         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.__init_system_defaults__()
         self.__init_kernel_defaults__()
@@ -154,10 +152,10 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
                 self.kernels_list[self.default_kernel_combo.currentIndex()]
 
     def __init_mem_defaults__(self):
-        #qmemman settings
+        # qmemman settings
         self.qmemman_config = ConfigParser()
-        self.vm_min_mem_val = '200MiB'  #str(qmemman_algo.MIN_PREFMEM)
-        self.dom0_mem_boost_val = '350MiB' #str(qmemman_algo.DOM0_MEM_BOOST)
+        self.vm_min_mem_val = '200MiB'  # str(qmemman_algo.MIN_PREFMEM)
+        self.dom0_mem_boost_val = '350MiB'  # str(qmemman_algo.DOM0_MEM_BOOST)
 
         self.qmemman_config.read(qmemman_config_path)
         if self.qmemman_config.has_section('global'):
@@ -172,10 +170,9 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
         self.min_vm_mem.setValue(self.vm_min_mem_val/1024/1024)
         self.dom0_mem_boost.setValue(self.dom0_mem_boost_val/1024/1024)
 
-
     def __apply_mem_defaults__(self):
 
-        #qmemman settings
+        # qmemman settings
         current_min_vm_mem = self.min_vm_mem.value()
         current_dom0_mem_boost = self.dom0_mem_boost.value()
 
@@ -186,7 +183,7 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
             current_dom0_mem_boost = str(current_dom0_mem_boost)+'MiB'
 
             if not self.qmemman_config.has_section('global'):
-                #add the whole section
+                # add the whole section
                 self.qmemman_config.add_section('global')
                 self.qmemman_config.set(
                     'global', 'vm-min-mem', current_min_vm_mem)
@@ -201,7 +198,7 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
                 qmemman_config_file.close()
 
             else:
-                #If there already is a 'global' section, we don't use
+                # If there already is a 'global' section, we don't use
                 # SafeConfigParser.write() - it would get rid of
                 # all the comments...
 
@@ -209,7 +206,7 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
                 lines_to_add['vm-min-mem'] = \
                     "vm-min-mem = " + current_min_vm_mem + "\n"
                 lines_to_add['dom0-mem-boost'] = \
-                    "dom0-mem-boost = " + current_dom0_mem_boost +"\n"
+                    "dom0-mem-boost = " + current_dom0_mem_boost + "\n"
 
                 config_lines = []
 
@@ -252,23 +249,23 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
         self.disable_updates_all.clicked.connect(self.__disable_updates_all)
 
     def __enable_updates_all(self):
-        reply = QtGui.QMessageBox.question(
+        reply = QtWidgets.QMessageBox.question(
             self, self.tr("Change state of all qubes"),
             self.tr("Are you sure you want to set all qubes to check "
                     "for updates?"),
-            QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel)
-        if reply == QtGui.QMessageBox.Cancel:
+            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
+        if reply == QtWidgets.QMessageBox.Cancel:
             return
 
         self.__set_updates_all(True)
 
     def __disable_updates_all(self):
-        reply = QtGui.QMessageBox.question(
+        reply = QtWidgets.QMessageBox.question(
             self, self.tr("Change state of all qubes"),
             self.tr("Are you sure you want to set all qubes to not check "
                     "for updates?"),
-            QtGui.QMessageBox.Yes | QtGui.QMessageBox.Cancel)
-        if reply == QtGui.QMessageBox.Cancel:
+            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
+        if reply == QtWidgets.QMessageBox.Cancel:
             return
 
         self.__set_updates_all(False)
@@ -298,7 +295,6 @@ class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
         self.__apply_updates__()
 
 
-
 # Bases on the original code by:
 # Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>
 
@@ -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()

+ 1 - 1
qubesmanager/informationnotes.py

@@ -20,7 +20,7 @@
 # with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #
-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

+ 8 - 7
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 @@ from qubesadmin import Qubes
 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 @@ class LogDialog(ui_logdlg.Ui_LogDialog, QtGui.QDialog):
         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 @@ class LogDialog(ui_logdlg.Ui_LogDialog, QtGui.QDialog):
         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 @@ class LogDialog(ui_logdlg.Ui_LogDialog, QtGui.QDialog):
 
 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()

+ 15 - 15
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 @@ class MultiSelectWidget(
         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 @@ class MultiSelectWidget(
             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 @@ class MultiSelectWidget(
             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)

+ 102 - 104
qubesmanager/qube_manager.py

@@ -37,8 +37,7 @@ from qubesadmin import exc
 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 utils as manager_utils
 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 @@ class VmRowInTable:
             # AdminAPI
             pass
 
-        #force re-sorting
+        # force re-sorting
         self.table.setSortingEnabled(True)
 
 
@@ -229,16 +228,25 @@ class VmShutdownMonitor(QtCore.QObject):
         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 <b>'{0}'</b> hasn't shutdown within the last "
                         "{1} seconds, do you want to kill it?<br>").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 @@ class UpdateVMThread(common_threads.QubesThread):
                             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 @@ class UpdateVMThread(common_threads.QubesThread):
                     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 @@ class RunCommandThread(common_threads.QubesThread):
             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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
             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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
                                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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
                 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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
                 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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
 
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
                         manager_utils.format_dependencies_list(dependencies) + \
                         "<br>"
 
-            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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
 
             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 <b>'{0}'</b>"
                     "?<br> All data on this Qube's private storage will be "
                     "lost!<br><br>Type the name of the Qube (<b>{1}</b>) below "
@@ -831,8 +829,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
 
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 <b>{}</b> clone:').format(vm.name),
             text=(name_format % name_number))
@@ -866,13 +864,13 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
             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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
     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"
                     " <b>'{0}'</b>?<br><small>This will shutdown all the "
                     "running applications within this Qube.</small>").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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
     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 <b>'{0}'</b>?"
                     "<br><small>This will shutdown all the running "
                     "applications within this Qube.</small>").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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
                            "shutdown!)</b> all the running applications within "
                            "this Qube.</small>").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(
                         "<b>An exception ocurred while killing {0}.</b><br>"
                         "ERROR: {1}").format(vm.name, ex))
@@ -1057,13 +1056,13 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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(
                     "<b>{0}</b><br>The Qube has to be running to be updated."
                     "<br>Do you want to start it?<br>").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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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 <b>{}</b>:').format(vm.name))
         if not ok or command_to_run == "":
@@ -1101,8 +1099,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
     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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
     @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 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
         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.<br><br>"
@@ -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")

+ 1 - 1
qubesmanager/releasenotes.py

@@ -20,7 +20,7 @@
 # with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #
-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
 

+ 20 - 29
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 multiselectwidget
 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 @@ class RestoreThread(QtCore.QThread):
                 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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
         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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
                     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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
             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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
         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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
         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 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
 
     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.<br><br>"
-                         "<b><i>%s</i></b>" % error +
-                         "at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
-                                      % (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.<br><br><b><i>%s</i></b>" % error +
+        "at <b>line %d</b> of file <b>%s</b>.<br/><br/>" % (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")

+ 45 - 51
qubesmanager/settings.py

@@ -41,10 +41,11 @@ from . import device_list
 
 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 @@ class RefreshAppsVMThread(QtCore.QThread):
 
 
 # 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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
         ####### 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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
                 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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
                         '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!<br/>"\
-                        "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!<br/>"
+                        "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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
     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.<br>"
@@ -546,7 +541,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
         # 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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
                                 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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
                         ", ".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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
 
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
             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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
         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 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
     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.<br><br>"
@@ -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")

+ 30 - 28
qubesmanager/table_widgets.py

@@ -20,8 +20,7 @@
 # with this program; if not, see <http://www.gnu.org/licenses/>.
 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 @@ update_order = QtCore.Qt.AscendingOrder
 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 @@ class VmIconWidget(QtGui.QWidget):
 
         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 @@ class VmIconWidget(QtGui.QWidget):
 
 
 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 @@ class VmTypeWidget(VmIconWidget):
         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 @@ class VmTypeWidget(VmIconWidget):
 
 
 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 @@ class VmLabelWidget(VmIconWidget):
         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 @@ class VmLabelWidget(VmIconWidget):
             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 @@ class VmStatusIcon(QtGui.QLabel):
         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 @@ class VmInfoWidget(QtGui.QWidget):
     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 @@ class VmInfoWidget(QtGui.QWidget):
         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 @@ class VmInfoWidget(QtGui.QWidget):
         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 @@ class VmTemplateItem(VMPropertyItem):
             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 @@ class VmInternalItem(VMPropertyItem):
 
 
 # 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 @@ class VmUpdateInfoWidget(QtGui.QWidget):
 
     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 @@ class VmUpdateInfoWidget(QtGui.QWidget):
                 "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 @@ class VmUpdateInfoWidget(QtGui.QWidget):
             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)

+ 19 - 22
qubesmanager/template_manager.py

@@ -32,10 +32,7 @@ from qubesadmin import Qubes
 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 @@ column_names = ['State', 'Qube', 'Current template', 'New template']
 
 
 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 @@ class TemplateManagerWindow(
         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 @@ class TemplateManagerWindow(
     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 @@ class TemplateManagerWindow(
                     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 @@ class TemplateManagerWindow(
         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 @@ class VMNameItem(QtGui.QTableWidgetItem):
         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 @@ class StatusItem(QtGui.QTableWidgetItem):
         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 @@ class CurrentTemplateItem(QtGui.QTableWidgetItem):
         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 @@ class VMRow:
         # 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 @@ class VMRow:
                              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 @@ class VMRow:
         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.<br><br>"
@@ -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)

+ 20 - 11
qubesmanager/utils.py

@@ -24,16 +24,18 @@ import os
 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

+ 4 - 3
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

+ 3 - 3
ui/restoredlg.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>700</width>
-    <height>399</height>
+    <width>756</width>
+    <height>440</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -214,7 +214,7 @@
        <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
 &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
 p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
 &lt;p style=&quot;-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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
       </property>
      </widget>