From d601794e987cd8ede9f8b18e3ca109d5ea405e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Marczykowska-G=C3=B3recka?= Date: Wed, 25 Sep 2024 13:22:53 +0200 Subject: [PATCH] Update qt to PyQt6 Updated all files to Qt version 6. Based on the template manager redesign. fixes QubesOS/qubes-issues#9476 --- Makefile | 11 +- qubesmanager/about.py | 8 +- qubesmanager/appmenu_select.py | 2 +- qubesmanager/backup.py | 32 ++-- qubesmanager/backup_utils.py | 2 +- qubesmanager/bootfromdevice.py | 6 +- qubesmanager/clipboard.py | 4 +- qubesmanager/clone_vm.py | 4 +- qubesmanager/common_threads.py | 5 +- qubesmanager/create_new_vm.py | 13 +- qubesmanager/device_list.py | 2 +- qubesmanager/firewall.py | 32 ++-- qubesmanager/informationnotes.py | 2 +- qubesmanager/log_dialog.py | 8 +- qubesmanager/multiselectwidget.py | 6 +- qubesmanager/qube_manager.py | 177 ++++++++++++----------- qubesmanager/qvm_template_gui.py | 116 +++++++-------- qubesmanager/releasenotes.py | 2 +- qubesmanager/restore.py | 20 +-- qubesmanager/settings.py | 49 ++++--- qubesmanager/template_manager.py | 13 +- qubesmanager/tests/__init__.py | 2 +- qubesmanager/tests/test_backup.py | 14 +- qubesmanager/tests/test_backup_utils.py | 2 +- qubesmanager/tests/test_clone_vm.py | 10 +- qubesmanager/tests/test_create_new_vm.py | 4 +- qubesmanager/tests/test_qube_manager.py | 60 ++++---- qubesmanager/tests/test_vm_settings.py | 14 +- qubesmanager/utils.py | 21 +-- rpm_spec/qmgr.spec.in | 14 +- ui/backupdlg.ui | 11 +- ui/clonevmdlg.ui | 3 +- ui/newappvmdlg.ui | 3 +- ui/qubemanager.ui | 10 +- ui/qvmtemplate.ui | 6 +- ui/restoredlg.ui | 11 +- ui/templatemanager.ui | 6 +- ui/templatemanger2.ui | 6 +- 38 files changed, 373 insertions(+), 338 deletions(-) diff --git a/Makefile b/Makefile index 164a11e0..4cc1e0bd 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ VERSION := $(shell cat version) PYTHON ?= python3 -LRELEASE_QT5 ?= $(if $(wildcard /etc/debian_version),lrelease,lrelease-qt5) +LRELEASE_QT6 ?= $(if $(wildcard /etc/debian_version),lrelease,lrelease-qt6) +RCC ?= /usr/lib64/qt6/libexec/rcc SETUPTOOLS_OPTS = SETUPTOOLS_OPTS += $(if $(wildcard /etc/debian_version),--install-layout=deb,) @@ -11,17 +12,17 @@ export QT_HASH_SEED=0 export PYTHONHASHSEED=0 qubesmanager/ui_%.py: ui/%.ui - pyuic5 --from-imports -o $@ $< + pyuic6 -o $@ $< touch --reference=$< $@ ui: $(patsubst ui/%.ui,qubesmanager/ui_%.py,$(wildcard ui/*.ui)) res: - pyrcc5 -o qubesmanager/resources_rc.py resources.qrc + $(RCC) -g python resources.qrc | sed '0,/PySide6/s//PyQt6/' > qubesmanager/resources_rc.py touch --reference=resources.qrc qubesmanager/resources_rc.py translations: - $(LRELEASE_QT5) qubesmanager.pro + $(LRELEASE_QT6) qubesmanager.pro python: $(PYTHON) ./setup.py build @@ -30,7 +31,7 @@ python_install: $(PYTHON) ./setup.py install -O1 --skip-build --root $(DESTDIR) $(SETUPTOOLS_OPTS) update_ts: res - pylupdate5 qubesmanager.pro + pylupdate6 qubesmanager.pro install: mkdir -p $(DESTDIR)/usr/libexec/qubes-manager/ diff --git a/qubesmanager/about.py b/qubesmanager/about.py index 1f8d9fd1..8dfde976 100644 --- a/qubesmanager/about.py +++ b/qubesmanager/about.py @@ -20,8 +20,8 @@ # with this program; if not, see . # # -from PyQt5.QtWidgets import QDialog # pylint: disable=import-error -from PyQt5.QtGui import QIcon # pylint: disable=import-error +from PyQt6.QtWidgets import QDialog # pylint: disable=import-error +from PyQt6.QtGui import QIcon # pylint: disable=import-error from qubesmanager.releasenotes import ReleaseNotesDialog from qubesmanager.informationnotes import InformationNotesDialog @@ -60,8 +60,8 @@ def __init__(self, parent=None): def on_release_notes_clicked(self): release_notes_dialog = ReleaseNotesDialog(self) - release_notes_dialog.exec_() + release_notes_dialog.exec() def on_information_notes_clicked(self): information_notes_dialog = InformationNotesDialog(self) - information_notes_dialog.exec_() + information_notes_dialog.exec() diff --git a/qubesmanager/appmenu_select.py b/qubesmanager/appmenu_select.py index 965e65b3..178ba92e 100755 --- a/qubesmanager/appmenu_select.py +++ b/qubesmanager/appmenu_select.py @@ -20,7 +20,7 @@ # import subprocess -from PyQt5 import QtWidgets, QtCore # pylint: disable=import-error +from PyQt6 import QtWidgets, QtCore # pylint: disable=import-error from qubesadmin import exc # TODO description in tooltip diff --git a/qubesmanager/backup.py b/qubesmanager/backup.py index d35c24be..04b61ee7 100644 --- a/qubesmanager/backup.py +++ b/qubesmanager/backup.py @@ -24,12 +24,12 @@ from qubesadmin import exc from qubesadmin import utils as admin_utils -from PyQt5 import QtCore, QtWidgets, QtGui, Qt # pylint: disable=import-error -from . import ui_backupdlg # pylint: disable=no-name-in-module -from . import multiselectwidget +from PyQt6 import QtCore, QtWidgets, QtGui # pylint: disable=import-error +from qubesmanager import ui_backupdlg # pylint: disable=no-name-in-module +from qubesmanager import multiselectwidget -from . import backup_utils -from . import utils +from qubesmanager import backup_utils +from qubesmanager import utils import grp import pwd @@ -81,8 +81,8 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.setupUi(self) self.setWindowFlags(self.windowFlags() | - Qt.Qt.WindowMaximizeButtonHint | - Qt.Qt.WindowMinimizeButtonHint) + QtCore.Qt.WindowType.WindowMaximizeButtonHint | + QtCore.Qt.WindowType.WindowMinimizeButtonHint) self.progress_status.text = self.tr("Backup in progress...") self.dir_line_edit.setReadOnly(False) @@ -146,10 +146,12 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): def show_hide_password(self): if self.show_passwd_button.isChecked(): - self.passphrase_line_edit.setEchoMode(QtWidgets.QLineEdit.Password) + self.passphrase_line_edit.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Password) self.show_passwd_button.setIcon(QtGui.QIcon(':/eye-off.svg')) else: - self.passphrase_line_edit.setEchoMode(QtWidgets.QLineEdit.Normal) + self.passphrase_line_edit.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Normal) self.show_passwd_button.setIcon(QtGui.QIcon(':/eye.svg')) def save_profile_changed(self): @@ -376,7 +378,7 @@ def current_page_changed(self, page_id): # pylint: disable=unused-argument self.save_settings(use_temp=False, save_passphrase=save_passphrase) - self.button(self.FinishButton).setDisabled(True) + self.button(self.WizardButton.FinishButton).setDisabled(True) self.showFileDialog.setEnabled( self.appvm_combobox.currentIndex() != 0) self.showFileDialog.setChecked(self.showFileDialog.isEnabled() @@ -399,8 +401,8 @@ def backup_finished(self): self, self.tr("Backup error"), self.tr("ERROR: {}").format( self.thread.msg)) - self.button(self.CancelButton).setEnabled(False) - self.button(self.FinishButton).setEnabled(True) + self.button(self.WizardButton.CancelButton).setEnabled(False) + self.button(self.WizardButton.FinishButton).setEnabled(True) self.cleanup_temporary_files() else: @@ -415,8 +417,8 @@ def backup_finished(self): "the file selection dialog.")) backup_utils.select_path_button_clicked(self, False, True) - self.button(self.CancelButton).setEnabled(False) - self.button(self.FinishButton).setEnabled(True) + self.button(self.WizardButton.CancelButton).setEnabled(False) + self.button(self.WizardButton.FinishButton).setEnabled(True) self.showFileDialog.setEnabled(False) self.cleanup_temporary_files() @@ -426,7 +428,7 @@ def backup_finished(self): def reject(self): if (self.currentPage() is self.commit_page) and \ - self.button(self.CancelButton).isEnabled(): + self.button(self.WizardButton.CancelButton).isEnabled(): try: self.qubes_app.qubesd_call( 'dom0', 'admin.backup.Cancel', diff --git a/qubesmanager/backup_utils.py b/qubesmanager/backup_utils.py index 1e41d1d7..e72ff013 100644 --- a/qubesmanager/backup_utils.py +++ b/qubesmanager/backup_utils.py @@ -21,7 +21,7 @@ import re import socket -from PyQt5 import QtWidgets # pylint: disable=import-error +from PyQt6 import QtWidgets # pylint: disable=import-error import subprocess from . import utils diff --git a/qubesmanager/bootfromdevice.py b/qubesmanager/bootfromdevice.py index 6fc60454..f4311a3c 100644 --- a/qubesmanager/bootfromdevice.py +++ b/qubesmanager/bootfromdevice.py @@ -21,7 +21,7 @@ import subprocess from . import utils from . import ui_bootfromdevice # pylint: disable=no-name-in-module -from PyQt5 import QtWidgets, QtGui, Qt # pylint: disable=import-error +from PyQt6 import QtWidgets, QtGui, QtCore # pylint: disable=import-error from qubesadmin import tools from qubesadmin import exc from qubesadmin.tools import qvm_start @@ -42,8 +42,8 @@ def __init__(self, vm, qapp, qubesapp=None, parent=None, new_vm=False): self.setWindowTitle( self.tr("Boot {vm} from device").format(vm=self.vm)) self.setWindowFlags(self.windowFlags() | - Qt.Qt.WindowMaximizeButtonHint | - Qt.Qt.WindowMinimizeButtonHint) + QtCore.Qt.WindowType.WindowMaximizeButtonHint | + QtCore.Qt.WindowType.WindowMinimizeButtonHint) self.buttonBox.accepted.connect(self.save_and_apply) self.buttonBox.rejected.connect(self.reject) diff --git a/qubesmanager/clipboard.py b/qubesmanager/clipboard.py index 8bf8f1eb..8c81671e 100644 --- a/qubesmanager/clipboard.py +++ b/qubesmanager/clipboard.py @@ -26,8 +26,8 @@ from math import log # pylint: disable=import-error -from PyQt5.QtWidgets import QApplication, QMessageBox -from PyQt5.QtCore import QCoreApplication +from PyQt6.QtWidgets import QApplication, QMessageBox +from PyQt6.QtCore import QCoreApplication APPVIEWER_LOCK = "/var/run/qubes/appviewer.lock" CLIPBOARD_CONTENTS = "/var/run/qubes/qubes-clipboard.bin" diff --git a/qubesmanager/clone_vm.py b/qubesmanager/clone_vm.py index 05fba0e7..d4b1ef34 100644 --- a/qubesmanager/clone_vm.py +++ b/qubesmanager/clone_vm.py @@ -24,7 +24,7 @@ import sys import subprocess -from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error +from PyQt6 import QtCore, QtWidgets, QtGui # pylint: disable=import-error import qubesadmin import qubesadmin.tools @@ -183,4 +183,4 @@ def main(args=None): "appname", 'Clone qube')) dialog = CloneVMDlg(qtapp, args.app, src_vm=src_vm) - dialog.exec_() + dialog.exec() diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index d3a0ebcf..a95163cb 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -20,7 +20,7 @@ # -from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error +from PyQt6 import QtCore, QtWidgets # pylint: disable=import-error from contextlib import contextmanager from qubesadmin import exc @@ -29,7 +29,8 @@ @contextmanager def busy_cursor(): try: - QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BusyCursor) + QtWidgets.QApplication.setOverrideCursor( + QtCore.Qt.CursorShape.BusyCursor) yield finally: QtWidgets.QApplication.restoreOverrideCursor() diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py index e8038146..32e6c29e 100644 --- a/qubesmanager/create_new_vm.py +++ b/qubesmanager/create_new_vm.py @@ -25,7 +25,7 @@ import sys import subprocess -from PyQt5 import QtCore, QtWidgets, QtGui # pylint: disable=import-error +from PyQt6 import QtCore, QtWidgets, QtGui # pylint: disable=import-error import qubesadmin import qubesadmin.tools @@ -145,8 +145,11 @@ def __init__(self, qtapp, app, parent=None): self.storage_pool.clear() self.storage_pool.addItem("(default)", qubesadmin.DEFAULT) - self.name.setValidator(QtGui.QRegExpValidator( - QtCore.QRegExp("[a-zA-Z0-9_-]*", QtCore.Qt.CaseInsensitive), None)) + self.name.setValidator(QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression( + "[a-zA-Z0-9_-]*", + QtCore.QRegularExpression.PatternOption.CaseInsensitiveOption), + None)) self.name.selectAll() self.name.setFocus() @@ -182,7 +185,7 @@ def accept(self): if self.install_system.isChecked(): self.boot_dialog = bootfromdevice.VMBootFromDeviceWindow( name, self.qtapp, self.app, self, True) - if not self.boot_dialog.exec_(): + if not self.boot_dialog.exec(): return if name in self.app.domains: @@ -336,4 +339,4 @@ def main(args=None): "appname", 'Create qube')) dialog = NewVmDlg(qtapp, args.app) - dialog.exec_() + dialog.exec() diff --git a/qubesmanager/device_list.py b/qubesmanager/device_list.py index c32fd249..aacc1013 100644 --- a/qubesmanager/device_list.py +++ b/qubesmanager/device_list.py @@ -18,7 +18,7 @@ # from . import ui_devicelist # pylint: disable=no-name-in-module -from PyQt5 import QtWidgets # pylint: disable=import-error +from PyQt6 import QtWidgets # pylint: disable=import-error class PCIDeviceListWindow(ui_devicelist.Ui_Dialog, QtWidgets.QDialog): diff --git a/qubesmanager/firewall.py b/qubesmanager/firewall.py index 4564504d..93193944 100644 --- a/qubesmanager/firewall.py +++ b/qubesmanager/firewall.py @@ -21,7 +21,7 @@ import datetime import re -from PyQt5 import QtCore, QtGui, QtWidgets # pylint: disable=import-error +from PyQt6 import QtCore, QtGui, QtWidgets # pylint: disable=import-error import qubesadmin.firewall from . import ui_newfwruledlg # pylint: disable=no-name-in-module @@ -39,13 +39,17 @@ def __init__(self, parent=None): self.set_ok_state(False) self.addressComboBox.editTextChanged.connect( self.address_editing_finished) - self.serviceComboBox.setValidator(QtGui.QRegExpValidator( - QtCore.QRegExp("[a-z][a-z0-9-]+|[0-9]+(-[0-9]+)?", - QtCore.Qt.CaseInsensitive), None)) + self.serviceComboBox.setValidator(QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression( + "[a-z][a-z0-9-]+|[0-9]+(-[0-9]+)?", + QtCore.QRegularExpression.PatternOption.CaseInsensitiveOption), + None)) self.serviceComboBox.setEnabled(False) - self.serviceComboBox.setInsertPolicy(QtWidgets.QComboBox.InsertAtBottom) + self.serviceComboBox.setInsertPolicy( + QtWidgets.QComboBox.InsertPolicy.InsertAtBottom) self.populate_combos() - self.serviceComboBox.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop) + self.serviceComboBox.setInsertPolicy( + QtWidgets.QComboBox.InsertPolicy.InsertAtTop) self.model = None @@ -142,7 +146,8 @@ def address_editing_finished(self): self.set_ok_state(True) def set_ok_state(self, ok_state): - ok_button = self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok) + ok_button = self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Ok) if ok_button is not None: ok_button.setEnabled(ok_state) @@ -190,7 +195,7 @@ def __init__(self, parent=None): self.__children = None # list of rules in the FW def sort(self, idx, order): - rev = order == QtCore.Qt.AscendingOrder + rev = order == QtCore.Qt.SortOrder.AscendingOrder self.children.sort(key=lambda x: self.get_column_string(idx, x), reverse=rev) @@ -413,16 +418,17 @@ def hasChildren(self, index=QtCore.QModelIndex()): parent_item = index.internalPointer() return parent_item is None - def data(self, index, role=QtCore.Qt.DisplayRole): - if index.isValid() and role == QtCore.Qt.DisplayRole: + def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole): + if index.isValid() and role == QtCore.Qt.ItemDataRole.DisplayRole: return self.get_column_string(index.column(), self.children[index.row()]) # pylint: disable=invalid-name - def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole): + def headerData(self, section, orientation, + role=QtCore.Qt.ItemDataRole.DisplayRole): if section < len(self.__column_names) \ - and orientation == QtCore.Qt.Horizontal \ - and role == QtCore.Qt.DisplayRole: + and orientation == QtCore.Qt.Orientation.Horizontal \ + and role == QtCore.Qt.ItemDataRole.DisplayRole: return self.__column_names[section] @property diff --git a/qubesmanager/informationnotes.py b/qubesmanager/informationnotes.py index 755be5b7..70e21abb 100644 --- a/qubesmanager/informationnotes.py +++ b/qubesmanager/informationnotes.py @@ -20,7 +20,7 @@ # with this program; if not, see . # # -from PyQt5.QtWidgets import QDialog # pylint: disable=import-error +from PyQt6.QtWidgets import QDialog # pylint: disable=import-error from . import ui_informationnotes # pylint: disable=no-name-in-module import subprocess diff --git a/qubesmanager/log_dialog.py b/qubesmanager/log_dialog.py index d5ac0031..d62bbe6a 100644 --- a/qubesmanager/log_dialog.py +++ b/qubesmanager/log_dialog.py @@ -22,7 +22,7 @@ import sys import os from functools import partial -from PyQt5 import QtWidgets, Qt # pylint: disable=import-error +from PyQt6 import QtWidgets, QtCore # pylint: disable=import-error from qubesadmin import Qubes from . import ui_logdlg # pylint: disable=no-name-in-module from . import clipboard @@ -43,8 +43,8 @@ def __init__(self, app, logfiles, parent=None): self.setupUi(self) self.setWindowFlags(self.windowFlags() | - Qt.Qt.WindowMaximizeButtonHint | - Qt.Qt.WindowMinimizeButtonHint) + QtCore.Qt.WindowType.WindowMaximizeButtonHint | + QtCore.Qt.WindowType.WindowMinimizeButtonHint) self.copy_to_qubes_clipboard.clicked.connect( self.copy_to_clipboard_triggered) @@ -89,7 +89,7 @@ def main(): log_window = LogDialog(qubes_app, sys.argv[1:]) log_window.show() - qt_app.exec_() + qt_app.exec() qt_app.exit() diff --git a/qubesmanager/multiselectwidget.py b/qubesmanager/multiselectwidget.py index 53455514..45f13122 100644 --- a/qubesmanager/multiselectwidget.py +++ b/qubesmanager/multiselectwidget.py @@ -1,4 +1,4 @@ -from PyQt5 import QtCore, QtWidgets # pylint: disable=import-error +from PyQt6 import QtCore, QtWidgets # pylint: disable=import-error from . import ui_multiselectwidget # pylint: disable=no-name-in-module @@ -17,9 +17,9 @@ def __init__(self, parent=None): self.remove_selected_button.clicked.connect(self.remove_selected) self.remove_all_button.clicked.connect(self.remove_all) self.available_list.setSelectionMode( - QtWidgets.QAbstractItemView.ExtendedSelection) + QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) self.selected_list.setSelectionMode( - QtWidgets.QAbstractItemView.ExtendedSelection) + QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection) self.available_list.itemDoubleClicked.connect(self.add_selected) self.selected_list.itemDoubleClicked.connect(self.remove_selected) diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index dbc13b87..33713f66 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -33,17 +33,18 @@ from qubesadmin.tools import qvm_start # pylint: disable=import-error -from PyQt5.QtCore import (Qt, QAbstractTableModel, QObject, pyqtSlot, QEvent, - QSettings, QRegExp, QSortFilterProxyModel, QSize, QPoint, QTimer) +from PyQt6.QtCore import (Qt, QAbstractTableModel, QObject, pyqtSlot, QEvent, + QSettings, QRegularExpression, QSortFilterProxyModel, + QSize, QPoint, QTimer) # pylint: disable=import-error -from PyQt5.QtWidgets import (QLineEdit, QStyledItemDelegate, QToolTip, - QMenu, QInputDialog, QMainWindow, QProgressDialog, QStyleOptionViewItem, - QMessageBox, QShortcut) +from PyQt6.QtWidgets import (QLineEdit, QStyledItemDelegate, QToolTip, + QMenu, QInputDialog, QMainWindow, QProgressDialog, + QStyleOptionViewItem, QMessageBox) # pylint: disable=import-error -from PyQt5.QtGui import (QIcon, QPixmap, QRegExpValidator, QFont, QColor, - QKeySequence) +from PyQt6.QtGui import (QIcon, QPixmap, QRegularExpressionValidator, QFont, + QColor, QShortcut, QKeySequence) from qubesmanager.about import AboutDialog @@ -120,12 +121,12 @@ def __init__(self): def sizeHint(self, option, index): hint = super().sizeHint(option, index) option = QStyleOptionViewItem(option) - option.features |= option.HasDecoration + option.features |= option.ViewItemFeature.HasDecoration widget = option.widget style = widget.style() - iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, - option, widget) - width = iconRect.width() * 3 # Nº of possible icons + iconRect = style.subElementRect( + style.SubElement.SE_ItemViewItemDecoration, option, widget) + width = iconRect.width() * 3 # Nº of possible icons hint.setWidth(width) return hint @@ -137,13 +138,14 @@ def paint(self, qp, option, index): style = widget.style() # paint the base item (borders, gradients, selection colors, etc) - style.drawControl(style.CE_ItemViewItem, option, qp, widget) + style.drawControl(style.ControlElement.CE_ItemViewItem, + option, qp, widget) # "lie" about the decoration, to get a valid icon rectangle (even if we # don't have any "real" icon set for the item) - option.features |= option.HasDecoration - iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, - option, widget) + option.features |= option.ViewItemFeature.HasDecoration + iconRect = style.subElementRect( + style.SubElement.SE_ItemViewItemDecoration, option, widget) iconSize = iconRect.size() margin = iconRect.left() - option.rect.left() @@ -166,16 +168,16 @@ def paint(self, qp, option, index): qp.restore() def helpEvent(self, event, view, option, index): - if event.type() != QEvent.ToolTip: + if event.type() != QEvent.Type.ToolTip: return super().helpEvent(event, view, option, index) option = QStyleOptionViewItem(option) widget = option.widget style = widget.style() - option.features |= option.HasDecoration + option.features |= option.ViewItemFeature.HasDecoration - iconRect = style.subElementRect(style.SE_ItemViewItemDecoration, - option, widget) + iconRect = style.subElementRect( + style.SubElement.SE_ItemViewItemDecoration, option, widget) iconRect.setTop(option.rect.y()) iconRect.setHeight(option.rect.height()) @@ -415,7 +417,7 @@ def data(self, index, role): col_name = self.columns_indices[col] vm = self.qubes_cache.get_vm(row) - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: if col in [0, 1]: return None if col_name == "Name": @@ -442,7 +444,7 @@ def data(self, index, role): return "Yes" if vm.dvm_template else "" if col_name == "Virt Mode": return vm.virt_mode - if role == Qt.DecorationRole: + if role == Qt.ItemDataRole.DecorationRole: if col_name == "Type": try: return self.klass_pixmap[vm.klass] @@ -463,24 +465,25 @@ def data(self, index, role): icon = QIcon.fromTheme(vm.icon) self.label_pixmap[vm.icon] = icon.pixmap(icon_size) return self.label_pixmap[vm.icon] - if role == Qt.CheckStateRole: + if role == Qt.ItemDataRole.CheckStateRole: if col_name == "Backup": - return Qt.Checked if vm.inc_backup else Qt.Unchecked - if role == Qt.FontRole: + return Qt.CheckState.Checked if vm.inc_backup else ( + Qt.CheckState.Unchecked) + if role == Qt.ItemDataRole.FontRole: if col_name == "Template": if vm.template is None: font = QFont() font.setItalic(True) return font - if role == Qt.ForegroundRole: + if role == Qt.ItemDataRole.ForegroundRole: if col_name == "Template": if vm.template is None: return QColor("gray") # Used for get VM Object - if role == Qt.UserRole: + if role == Qt.ItemDataRole.UserRole: return vm # Used for sorting - if role == Qt.UserRole + 1: + if role == Qt.ItemDataRole.UserRole + 1: if vm.klass == 'AdminVM': return "" if col_name == "Type": @@ -510,36 +513,37 @@ def data(self, index, role): if col_name == "Backup": # sort True before False, hence the not return not vm.inc_backup - return self.data(index, Qt.DisplayRole) + return self.data(index, Qt.ItemDataRole.DisplayRole) # pylint: disable=invalid-name def headerData(self, col, orientation, role): if col < 2: return None - if orientation == Qt.Horizontal and role == Qt.DisplayRole: + if (orientation == Qt.Orientation.Horizontal and role == + Qt.ItemDataRole.DisplayRole): return self.columns_indices[col] return None - def setData(self, index, value, role=Qt.EditRole): + def setData(self, index, value, role=Qt.ItemDataRole.EditRole): if not index.isValid(): return False - if role == Qt.CheckStateRole: + if role == Qt.ItemDataRole.CheckStateRole: col_name = self.columns_indices[index.column()] if col_name == "Backup": vm = self.qubes_cache.get_vm(index.row()) - vm.vm.include_in_backups = value == Qt.Checked - vm.inc_backup = value == Qt.Checked + vm.vm.include_in_backups = value == Qt.CheckState.Checked + vm.inc_backup = value == Qt.CheckState.Checked return True return False def flags(self, index): if not index.isValid(): - return Qt.NoItemFlags + return Qt.ItemFlag.NoItemFlags def_flags = QAbstractTableModel.flags(self, index) if self.columns_indices[index.column()] == "Backup": - return def_flags | Qt.ItemIsUserCheckable + return def_flags | Qt.ItemFlag.ItemIsUserCheckable return def_flags vm_restart_check_timeout = 1000 # in msec @@ -583,27 +587,28 @@ def check_if_vm_has_shutdown(self): if self.timeout_reached(): msgbox = QMessageBox(self.caller) - msgbox.setIcon(QMessageBox.Question) + msgbox.setIcon(QMessageBox.Icon.Question) msgbox.setWindowTitle(self.tr("Qube Shutdown")) msgbox.setText(self.tr( "The Qube '{0}' hasn't shutdown within the last " "{1} seconds, do you want to kill it?
").format( vm.name, self.shutdown_timeout)) kill_button = msgbox.addButton( - self.tr("Kill it!"), QMessageBox.YesRole) + self.tr("Kill it!"), QMessageBox.ButtonRole.YesRole) wait_button = msgbox.addButton( self.tr("Wait another {0} seconds...").format( self.shutdown_timeout), - QMessageBox.NoRole) - ignore_button = msgbox.addButton(self.tr("Don't ask again"), - QMessageBox.RejectRole) + QMessageBox.ButtonRole.NoRole) + ignore_button = msgbox.addButton( + self.tr("Don't ask again"), + QMessageBox.ButtonRole.RejectRole) msgbox.setDefaultButton(wait_button) msgbox.setEscapeButton(ignore_button) msgbox.setWindowFlags( - msgbox.windowFlags() | Qt.CustomizeWindowHint) + msgbox.windowFlags() | Qt.WindowType.CustomizeWindowHint) msgbox.setWindowFlags( - msgbox.windowFlags() & ~Qt.WindowCloseButtonHint) - msgbox.exec_() + msgbox.windowFlags() & ~Qt.WindowType.WindowCloseButtonHint) + msgbox.exec() msgbox.deleteLater() if msgbox.clickedButton() is kill_button: @@ -676,15 +681,15 @@ def lessThan(self, left, right): if left.data(self.sortRole()) != right.data(self.sortRole()): return super().lessThan(left, right) - left_vm = left.data(Qt.UserRole) - right_vm = right.data(Qt.UserRole) + left_vm = left.data(Qt.ItemDataRole.UserRole) + right_vm = right.data(Qt.ItemDataRole.UserRole) return left_vm.name.lower() < right_vm.name.lower() # pylint: disable=too-many-return-statements def filterAcceptsRow(self, sourceRow, sourceParent): index = self.sourceModel().index(sourceRow, 0, sourceParent) - vm = self.sourceModel().data(index, Qt.UserRole) + vm = self.sourceModel().data(index, Qt.ItemDataRole.UserRole) # if hide internal is true, ignore all other filters if not self.window.show_internal_action.isChecked() and vm.internal: @@ -724,8 +729,11 @@ def __init__(self, qt_app, qubes_app, dispatcher, _parent=None): self.qt_app = qt_app self.searchbox = SearchBox() - self.searchbox.setValidator(QRegExpValidator( - QRegExp("[a-zA-Z0-9_-]*", Qt.CaseInsensitive), None)) + self.searchbox.setValidator(QRegularExpressionValidator( + QRegularExpression( + "[a-zA-Z0-9_-]*", + QRegularExpression.PatternOption.CaseInsensitiveOption), + None)) self.searchbox.textChanged.connect(self.do_search) self.searchContainer.insertWidget(1, self.searchbox) @@ -792,10 +800,10 @@ def __init__(self, qt_app, qubes_app, dispatcher, _parent=None): self.proxy = QubesProxyModel(self) self.proxy.setSourceModel(self.qubes_model) - self.proxy.setSortRole(Qt.UserRole + 1) - self.proxy.setSortCaseSensitivity(Qt.CaseInsensitive) + self.proxy.setSortRole(Qt.ItemDataRole.UserRole + 1) + self.proxy.setSortCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) self.proxy.setFilterKeyColumn(2) - self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) + self.proxy.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) self.proxy.layoutChanged.connect(self.save_sorting) self.proxy.layoutChanged.connect(self.update_template_menu) self.proxy.layoutChanged.connect(self.update_network_menu) @@ -806,7 +814,7 @@ def __init__(self, qt_app, qubes_app, dispatcher, _parent=None): selection_model = self.table.selectionModel() selection_model.selectionChanged.connect(self.table_selection_changed) - self.table.setContextMenuPolicy(Qt.CustomContextMenu) + self.table.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.table.customContextMenuRequested.connect(self.open_context_menu) try: @@ -873,9 +881,9 @@ def change_template(self, template): self.tr("Do you want to change '{0}'
" "to Template '{1}'?").format( ', '.join(vm.name for vm in selected_vms), template), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: errors = [] for info in selected_vms: try: @@ -894,9 +902,9 @@ def change_network(self, netvm_name): self.tr("Do you want to change '{0}'
" "to Network '{1}'?").format( ', '.join(vm.name for vm in selected_vms), netvm_name), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel) - if reply != QMessageBox.Yes: + if reply != QMessageBox.StandardButton.Yes: return if netvm_name: @@ -913,9 +921,10 @@ def change_network(self, netvm_name): self.tr("
Can not change netvm to a halted Qube.
" "Do you want to start the Qube '{0}'?").format( netvm_name), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | + QMessageBox.StandardButton.Cancel) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: self.start_vm(netvm, True) else: return @@ -991,7 +1000,7 @@ def fill_cache(self): len(self.qubes_app.domains.keys())) progress.setWindowTitle(self.tr("Qube Manager")) progress.setMinimumDuration(1000) - progress.setWindowModality(Qt.WindowModal) + progress.setWindowModality(Qt.WindowModality.WindowModal) progress.setCancelButton(None) row_no = 0 @@ -1034,7 +1043,7 @@ def setup_application(self): self.qt_app.setWindowIcon(QIcon.fromTheme("qubes-manager")) def keyPressEvent(self, event): # pylint: disable=invalid-name - if event.key() == Qt.Key_Escape: + if event.key() == Qt.Key.Key_Escape: self.searchbox.clear() super().keyPressEvent(event) @@ -1159,10 +1168,10 @@ def load_manager_settings(self): sort_column = int(self.manager_settings.value("view/sort_column", defaultValue=2)) order = Qt.SortOrder(self.manager_settings.value("view/sort_order", - defaultValue=Qt.AscendingOrder)) + defaultValue=Qt.SortOrder.AscendingOrder)) if not sort_column: # Default sort by name - self.table.sortByColumn(2, Qt.AscendingOrder) + self.table.sortByColumn(2, Qt.SortOrder.AscendingOrder) else: self.table.sortByColumn(sort_column, order) @@ -1214,7 +1223,7 @@ def get_selected_vms(self): for index in indexes: if index.column() != 0: continue - vms.append(index.data(Qt.UserRole)) + vms.append(index.data(Qt.ItemDataRole.UserRole)) return vms @@ -1333,7 +1342,7 @@ def action_createvm_triggered(self): with common_threads.busy_cursor(): create_window = create_new_vm.NewVmDlg( self.qt_app, self.qubes_app, self) - create_window.exec_() + create_window.exec() # noinspection PyArgumentList @pyqtSlot(name='on_action_removevm_triggered') @@ -1398,7 +1407,7 @@ def action_clonevm_triggered(self): with common_threads.busy_cursor(): clone_window = clone_vm.CloneVMDlg( self.qt_app, self.qubes_app, src_vm=vm) - clone_window.exec_() + clone_window.exec() # noinspection PyArgumentList @pyqtSlot(name='on_action_resumevm_triggered') @@ -1467,9 +1476,10 @@ def action_shutdownvm_triggered(self): "?
This will shutdown all the running" " applications within this Qube.").format( vm.name), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | + QMessageBox.StandardButton.Cancel) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: self.shutdown_vm(vm) def get_connected_vms(self, vm, connected_vms): @@ -1493,9 +1503,10 @@ def shutdown_vm(self, vm, force=False, check_time=vm_restart_check_timeout, "
Do you want to shutdown: " "'{1}'?").format(vm.name, ", ".join([x.name for x in connected_vms])), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | + QMessageBox.StandardButton.Cancel) - if reply != QMessageBox.Yes: + if reply != QMessageBox.StandardButton.Yes: return False force = True @@ -1528,9 +1539,10 @@ def action_restartvm_triggered(self): self.tr("Are you sure you want to restart the Qube '{0}'" "?
This will shutdown all the running applica" "tions within this Qube.").format(vm.name), - QMessageBox.Yes | QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | + QMessageBox.StandardButton.Cancel) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: # in case the user shut down the VM in the meantime try: if manager_utils.is_running(vm, False): @@ -1568,10 +1580,11 @@ def action_killvm_triggered(self): reply = QMessageBox.question( self, self.tr("Qube Kill Confirmation"), info, - QMessageBox.Yes | QMessageBox.Cancel, - QMessageBox.Cancel) + QMessageBox.StandardButton.Yes | + QMessageBox.StandardButton.Cancel, + QMessageBox.StandardButton.Cancel) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: try: vm.kill() except exc.QubesException as ex: @@ -1697,7 +1710,7 @@ def action_restore_triggered(self): with common_threads.busy_cursor(): restore_window = restore.RestoreVMsWindow(self.qt_app, self.qubes_app, self) - restore_window.exec_() + restore_window.exec() # noinspection PyArgumentList @pyqtSlot(name='on_action_backup_triggered') @@ -1714,9 +1727,11 @@ def action_exit_triggered(self): def set_compactview(self, checked): if checked: - self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.toolbar.setToolButtonStyle( + Qt.ToolButtonStyle.ToolButtonIconOnly) else: - self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + self.toolbar.setToolButtonStyle( + Qt.ToolButtonStyle.ToolButtonTextUnderIcon) if self.settings_loaded: self.manager_settings.setValue('view/compactview', checked) @@ -1747,7 +1762,7 @@ def showhide_column(self, col_num, show): @pyqtSlot(name='on_action_about_qubes_triggered') def action_about_qubes_triggered(self): about = AboutDialog(self) - about.exec_() + about.exec() def createPopupMenu(self): # pylint: disable=invalid-name menu = QMenu() @@ -1756,11 +1771,11 @@ def createPopupMenu(self): # pylint: disable=invalid-name return menu def open_tools_context_menu(self, widget, point): - self.tools_context_menu.exec_(widget.mapToGlobal(point)) + self.tools_context_menu.exec(widget.mapToGlobal(point)) @pyqtSlot('const QPoint&') def open_context_menu(self, point): - self.context_menu.exec_(self.table.mapToGlobal( + self.context_menu.exec(self.table.mapToGlobal( point + QPoint(10, 0))) def show_log(self): @@ -1784,7 +1799,7 @@ def show_log(self): if len(logfiles) > 0: log_dlg = log_dialog.LogDialog(self.qt_app, logfiles) - log_dlg.exec_() + log_dlg.exec() else: QMessageBox.warning( self, diff --git a/qubesmanager/qvm_template_gui.py b/qubesmanager/qvm_template_gui.py index 91f1636f..4da1fdd6 100644 --- a/qubesmanager/qvm_template_gui.py +++ b/qubesmanager/qvm_template_gui.py @@ -35,10 +35,10 @@ import typing import shlex -import PyQt5 # pylint: disable=import-error -import PyQt5.QtWidgets # pylint: disable=import-error -import PyQt5.QtCore # pylint: disable=import-error -import PyQt5.QtGui # pylint: disable=import-error +import PyQt6 # pylint: disable=import-error +import PyQt6.QtWidgets # pylint: disable=import-error +import PyQt6.QtCore # pylint: disable=import-error +import PyQt6.QtGui # pylint: disable=import-error from . import ui_templateinstallconfirmdlg # pylint: disable=no-name-in-module from . import ui_templateinstallprogressdlg # pylint: disable=no-name-in-module @@ -50,7 +50,7 @@ # singleton for "no date" ZERO_DATE = datetime.fromtimestamp(0, UTC) -tr = functools.partial(PyQt5.QtCore.QCoreApplication.translate, "Template GUI") +tr = functools.partial(PyQt6.QtCore.QCoreApplication.translate, "Template GUI") HELP_TEXT = tr(""" This tool can be used to manage templates on your system. \ @@ -247,21 +247,21 @@ def get_upgradable(self) -> bool: def status(self, role): # pylint: disable=too-many-return-statements if self.obsolete(): - if role == PyQt5.QtCore.Qt.ToolTipRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.ToolTipRole: return tr("This template is obsolete and no longer receives " "updates") - if role == PyQt5.QtCore.Qt.DecorationRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.DecorationRole: return ":/obsolete.svg" if self.template_status == 'extra': - if role == PyQt5.QtCore.Qt.ToolTipRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.ToolTipRole: return tr("This template is a local template, not installed " "from a repository") - if role == PyQt5.QtCore.Qt.DecorationRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.DecorationRole: return ':/checkmark-with-plus.svg' if self.template_status in ['installed', 'upgradable']: - if role == PyQt5.QtCore.Qt.ToolTipRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.ToolTipRole: return tr("This template is installed") - if role == PyQt5.QtCore.Qt.DecorationRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.DecorationRole: return ':/checkmark.svg' return None @@ -311,7 +311,7 @@ class DescriptiveItem(TreeItem): def __init__(self, name): self._name = name self._children: typing.List[TreeItem] = [] - self._parent = PyQt5.QtCore.QModelIndex() + self._parent = PyQt6.QtCore.QModelIndex() @property def name(self) -> str: @@ -330,22 +330,22 @@ def parent(self) -> TreeItem: return self._parent -class TemplateModel(PyQt5.QtCore.QAbstractItemModel): +class TemplateModel(PyQt6.QtCore.QAbstractItemModel): def __init__(self): super().__init__() self.children = [] - def index(self, row, column, parent=PyQt5.QtCore.QModelIndex()): + def index(self, row, column, parent=PyQt6.QtCore.QModelIndex()): if not self.hasIndex(row, column, parent): - return PyQt5.QtCore.QModelIndex() + return PyQt6.QtCore.QModelIndex() if not parent.isValid(): child_item = self.children[row] else: child_item = parent.internalPointer().children[row] return self.createIndex(row, column, child_item) - def parent(self, child_index: PyQt5.QtCore.QModelIndex): - node = PyQt5.QtCore.QModelIndex() + def parent(self, child_index: PyQt6.QtCore.QModelIndex): + node = PyQt6.QtCore.QModelIndex() if child_index.isValid(): own_object = child_index.internalPointer() if own_object is not None: @@ -358,21 +358,21 @@ def parent(self, child_index: PyQt5.QtCore.QModelIndex): node = self.createIndex(row, 0, parent) return node - def rowCount(self, parent=PyQt5.QtCore.QModelIndex()): + def rowCount(self, parent=PyQt6.QtCore.QModelIndex()): if parent.internalPointer(): return len(parent.internalPointer().children) return len(self.children) - def columnCount(self, _parent=PyQt5.QtCore.QModelIndex()): + def columnCount(self, _parent=PyQt6.QtCore.QModelIndex()): return len(Template.COL_NAMES) - def data(self, index, role=PyQt5.QtCore.Qt.DisplayRole): + def data(self, index, role=PyQt6.QtCore.Qt.ItemDataRole.DisplayRole): # pylint: disable=too-many-return-statements if index.isValid(): data = index.internalPointer() - if role == PyQt5.QtCore.Qt.ItemDataRole: + if role == PyQt6.QtCore.Qt.ItemDataRole: return data.description - if role == PyQt5.QtCore.Qt.DisplayRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.DisplayRole: if index.column() == 0: return data.name if index.column() == 1: @@ -382,7 +382,7 @@ def data(self, index, role=PyQt5.QtCore.Qt.DisplayRole): if index.column() == 3: return data.repository() return data.name - if role == PyQt5.QtCore.Qt.ToolTipRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.ToolTipRole: if index.column() == 0: return "Template name" if index.column() == 1: @@ -391,29 +391,29 @@ def data(self, index, role=PyQt5.QtCore.Qt.DisplayRole): return "Template version" if index.column() == 3: return "Repository" - if role == PyQt5.QtCore.Qt.TextAlignmentRole: + if role == PyQt6.QtCore.Qt.ItemDataRole.TextAlignmentRole: if isinstance(data, int): - return PyQt5.QtCore.Qt.AlignRight - return PyQt5.QtCore.Qt.AlignLeft - if role == PyQt5.QtCore.Qt.DecorationRole: + return PyQt6.QtCore.Qt.AlignmentFlag.AlignRight + return PyQt6.QtCore.Qt.AlignmentFlag.AlignLeft + if role == PyQt6.QtCore.Qt.ItemDataRole.DecorationRole: if index.column() == 1: icon_name = data.status(role) if icon_name: - return PyQt5.QtGui.QIcon(icon_name) - if role == PyQt5.QtCore.Qt.UserRole: + return PyQt6.QtGui.QIcon(icon_name) + if role == PyQt6.QtCore.Qt.ItemDataRole.UserRole: return data return None def headerData(self, section, orientation, - role=PyQt5.QtCore.Qt.DisplayRole): + role=PyQt6.QtCore.Qt.ItemDataRole.DisplayRole): if section < len(Template.COL_NAMES) \ - and orientation == PyQt5.QtCore.Qt.Horizontal \ - and role == PyQt5.QtCore.Qt.DisplayRole: + and orientation == PyQt6.QtCore.Qt.Orientation.Horizontal \ + and role == PyQt6.QtCore.Qt.ItemDataRole.DisplayRole: return Template.COL_NAMES[section] return None - def removeRows(self, row, count, _parent=PyQt5.QtCore.QModelIndex()): - self.beginRemoveRows(PyQt5.QtCore.QModelIndex(), row, row + count) + def removeRows(self, row, count, _parent=PyQt6.QtCore.QModelIndex()): + self.beginRemoveRows(PyQt6.QtCore.QModelIndex(), row, row + count) del self.children[row:row+count] self.endRemoveRows() self.dataChanged.emit(*self.row_index(row, row + count)) @@ -440,7 +440,7 @@ async def refresh(self, refresh=True): return False, stderr # remove old rows rows_to_remove = len(self.children) - self.beginRemoveRows(PyQt5.QtCore.QModelIndex(), 0, + self.beginRemoveRows(PyQt6.QtCore.QModelIndex(), 0, rows_to_remove) self.children = [] self.endRemoveRows() @@ -494,7 +494,7 @@ async def refresh(self, refresh=True): # Convert back to list tpls = {k.title(): list(v.values()) for k, v in tpls.items()} - self.beginInsertRows(PyQt5.QtCore.QModelIndex(), 0, len(tpls) - 1) + self.beginInsertRows(PyQt6.QtCore.QModelIndex(), 0, len(tpls) - 1) for template_type, template_list in tpls.items(): if not template_list: continue @@ -511,10 +511,10 @@ async def refresh(self, refresh=True): class TemplateInstallConfirmDialog( ui_templateinstallconfirmdlg.Ui_TemplateInstallConfirmDlg, - PyQt5.QtWidgets.QDialog): + PyQt6.QtWidgets.QDialog): # pylint: disable=too-few-public-methods def __init__(self, question: str, operation_name: str, - palette: PyQt5.QtGui.QPalette, enable_warn: bool = False): + palette: PyQt6.QtGui.QPalette, enable_warn: bool = False): super().__init__() self.setupUi(self) @@ -523,19 +523,19 @@ def __init__(self, question: str, operation_name: str, ok_button = self.button_box.addButton( operation_name, - PyQt5.QtWidgets.QDialogButtonBox.ButtonRole.AcceptRole) + PyQt6.QtWidgets.QDialogButtonBox.ButtonRole.AcceptRole) ok_button.setPalette(palette) self.button_box.addButton( "Cancel", - PyQt5.QtWidgets.QDialogButtonBox.ButtonRole.RejectRole) + PyQt6.QtWidgets.QDialogButtonBox.ButtonRole.RejectRole) class TemplateInstallProgressDialog( ui_templateinstallprogressdlg.Ui_TemplateInstallProgressDlg, - PyQt5.QtWidgets.QDialog): + PyQt6.QtWidgets.QDialog): def __init__(self, command: typing.List[str], - palette: PyQt5.QtGui.QPalette, + palette: PyQt6.QtGui.QPalette, window_title: typing.Optional[str] = None): """ :param command: a list of strings containing the command to be used @@ -549,13 +549,13 @@ def __init__(self, command: typing.List[str], self.cancel_button = self.button_box.addButton( "Abort", - PyQt5.QtWidgets.QDialogButtonBox.ButtonRole.RejectRole) + PyQt6.QtWidgets.QDialogButtonBox.ButtonRole.RejectRole) def add_ok_button(self): self.button_box.removeButton(self.cancel_button) - ok_button: PyQt5.QtWidgets.QPushButton = self.button_box.addButton( + ok_button: PyQt6.QtWidgets.QPushButton = self.button_box.addButton( "OK", - PyQt5.QtWidgets.QDialogButtonBox.ButtonRole.AcceptRole) + PyQt6.QtWidgets.QDialogButtonBox.ButtonRole.AcceptRole) ok_button.setPalette(self.qubes_palette) @staticmethod @@ -606,16 +606,16 @@ async def coro(): class QvmTemplateWindow( ui_templatemanger2.Ui_MainWindow, - PyQt5.QtWidgets.QMainWindow): + PyQt6.QtWidgets.QMainWindow): def __init__(self, qt_app, qubes_app, dispatcher, _parent=None): super().__init__() self.setupUi(self) self.template_tree.header().setSectionResizeMode( - PyQt5.QtWidgets.QHeaderView.ResizeToContents) + PyQt6.QtWidgets.QHeaderView.ResizeMode.ResizeToContents) self.qubes_app = qubes_app - self.qt_app: PyQt5.QtWidgets.QApplication = qt_app - self.qt_app.setWindowIcon(PyQt5.QtGui.QIcon.fromTheme("qubes-manager")) + self.qt_app: PyQt6.QtWidgets.QApplication = qt_app + self.qt_app.setWindowIcon(PyQt6.QtGui.QIcon.fromTheme("qubes-manager")) self.dispatcher = dispatcher self.template_model = TemplateModel() @@ -651,10 +651,10 @@ def initialize_styles(self): qubes_style_buttons = [self.upgrade_button, self.install_button, self.reinstall_button, self.uninstall_button] palette = self.qt_app.palette() - palette.setColor(PyQt5.QtGui.QPalette.Button, PyQt5.QtGui.QColor( - "#4180c9")) - palette.setColor(PyQt5.QtGui.QPalette.ButtonText, PyQt5.QtGui.QColor( - "#ffffff")) + palette.setColor(PyQt6.QtGui.QPalette.ColorRole.Button, + PyQt6.QtGui.QColor("#4180c9")) + palette.setColor(PyQt6.QtGui.QPalette.ColorRole.ButtonText, + PyQt6.QtGui.QColor("#ffffff")) for button in qubes_style_buttons: button.setPalette(palette) @@ -688,10 +688,10 @@ def _get_selected_item(self) -> typing.Optional[TreeItem]: # and the selection model is single-row selected_item = selected_indexes[0] item = self.template_model.data(selected_item, - PyQt5.QtCore.Qt.UserRole) + PyQt6.QtCore.Qt.ItemDataRole.UserRole) return item - def template_selected(self, _selected: PyQt5.QtCore.QItemSelection): + def template_selected(self, _selected: PyQt6.QtCore.QItemSelection): item = self._get_selected_item() if not item: self._show_help() @@ -716,12 +716,12 @@ def _do_action(self, command: typing.List[str], operation_name: str, confirm = TemplateInstallConfirmDialog(question, operation_name, self.qubes_palette, enable_warn) - if confirm.exec_(): + if confirm.exec(): progress = TemplateInstallProgressDialog(command, self.qubes_palette, window_title) progress.install() - progress.exec_() + progress.exec() self.refresh() def do_uninstall(self): @@ -769,7 +769,7 @@ def refresh(self, refresh=True): async def coro(): ok, stderr = await self.template_model.refresh(refresh) if not ok: - PyQt5.QtWidgets.QMessageBox.warning( + PyQt6.QtWidgets.QMessageBox.warning( self, self.tr('Failed to fetch template list!'), self.tr('Failed to fetch template list: \n') + stderr diff --git a/qubesmanager/releasenotes.py b/qubesmanager/releasenotes.py index 827926fe..c4d1d8e3 100644 --- a/qubesmanager/releasenotes.py +++ b/qubesmanager/releasenotes.py @@ -20,7 +20,7 @@ # with this program; if not, see . # # -from PyQt5.QtWidgets import QDialog # pylint: disable=import-error +from PyQt6.QtWidgets import QDialog # pylint: disable=import-error from . import ui_releasenotes # pylint: disable=no-name-in-module diff --git a/qubesmanager/restore.py b/qubesmanager/restore.py index 90269a85..358f0948 100644 --- a/qubesmanager/restore.py +++ b/qubesmanager/restore.py @@ -20,8 +20,8 @@ # # -from PyQt5 import QtCore, QtWidgets, QtGui, Qt # pylint: disable=import-error import argparse +from PyQt6 import QtCore, QtWidgets, QtGui # pylint: disable=import-error import os import os.path import logging @@ -88,8 +88,8 @@ def __init__(self, qt_app, qubes_app, parent=None): self.qt_app = qt_app self.qubes_app = qubes_app self.setWindowFlags(self.windowFlags() | - Qt.Qt.WindowMaximizeButtonHint | - Qt.Qt.WindowMinimizeButtonHint) + QtCore.Qt.WindowType.WindowMaximizeButtonHint | + QtCore.Qt.WindowType.WindowMinimizeButtonHint) self.vms_to_restore = None self.func_output = [] @@ -132,10 +132,12 @@ def __init__(self, qt_app, qubes_app, parent=None): def show_hide_password(self): if self.passwd_show_button.isChecked(): - self.passphrase_line_edit.setEchoMode(QtWidgets.QLineEdit.Password) + self.passphrase_line_edit.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Password) self.passwd_show_button.setIcon(QtGui.QIcon(':/eye-off.svg')) else: - self.passphrase_line_edit.setEchoMode(QtWidgets.QLineEdit.Normal) + self.passphrase_line_edit.setEchoMode( + QtWidgets.QLineEdit.EchoMode.Normal) self.passwd_show_button.setIcon(QtGui.QIcon(':/eye.svg')) def setup_application(self): @@ -227,7 +229,7 @@ def current_page_changed(self, page_id): # pylint: disable=unused-argument self.confirm_page.completeChanged.emit() elif self.currentPage() is self.commit_page: - self.button(self.FinishButton).setDisabled(True) + self.button(self.WizardButton.FinishButton).setDisabled(True) self.showFileDialog.setEnabled(True) self.showFileDialog.setChecked(self.showFileDialog.isEnabled() and str(self.dir_line_edit.text()) @@ -264,8 +266,8 @@ def thread_finished(self): "the file selection dialog."))) backup_utils.select_path_button_clicked(self, False, True) - self.button(self.FinishButton).setEnabled(True) - self.button(self.CancelButton).setEnabled(False) + self.button(self.WizardButton.FinishButton).setEnabled(True) + self.button(self.WizardButton.CancelButton).setEnabled(False) self.showFileDialog.setEnabled(False) def update_log(self): @@ -295,7 +297,7 @@ def reject(self): self.backup_restore.canceled = True self.append_output('{0}'.format( self.tr("Aborting the operation..."))) - self.button(self.CancelButton).setDisabled(True) + self.button(self.WizardButton.CancelButton).setDisabled(True) else: self.done(0) diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 358d25ec..8dbd55da 100644 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -43,7 +43,7 @@ from .appmenu_select import AppmenuSelectManager from . import firewall -from PyQt5 import QtCore, QtWidgets, QtGui, Qt # pylint: disable=import-error +from PyQt6 import QtCore, QtWidgets, QtGui # pylint: disable=import-error from . import ui_settingsdlg # pylint: disable=no-name-in-module @@ -152,14 +152,15 @@ def __init__(self, vm, init_page="basic", qapp=None, qubesapp=None, self.setupUi(self) self.setWindowTitle(self.tr("Settings: {vm}").format(vm=self.vm.name)) self.setWindowFlags(self.windowFlags() | - Qt.Qt.WindowMaximizeButtonHint | - Qt.Qt.WindowMinimizeButtonHint) + QtCore.Qt.WindowType.WindowMaximizeButtonHint | + QtCore.Qt.WindowType.WindowMinimizeButtonHint) if init_page in self.tabs_indices: idx = self.tabs_indices[init_page] assert idx in range(self.tabWidget.count()) self.tabWidget.setCurrentIndex(idx) - self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect( + self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Apply).clicked.connect( self.apply) self.tabWidget.currentChanged.connect(self.current_tab_changed) @@ -282,8 +283,8 @@ def clear_threads(self): raise RuntimeError(self.tr('No finished thread found')) def keyPressEvent(self, event): # pylint: disable=invalid-name - if event.key() == QtCore.Qt.Key_Enter \ - or event.key() == QtCore.Qt.Key_Return: + if event.key() == QtCore.Qt.Key.Key_Enter \ + or event.key() == QtCore.Qt.Key.Key_Return: return super().keyPressEvent(event) @@ -397,9 +398,11 @@ def current_tab_changed(self, idx): def __init_basic_tab__(self): self.vmname.setText(self.vm.name) self.vmname.setValidator( - QtGui.QRegExpValidator( - QtCore.QRegExp("[a-zA-Z0-9_-]*", - QtCore.Qt.CaseInsensitive), None)) + QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression( + "[a-zA-Z0-9_-]*", + QtCore.QRegularExpression. + PatternOption.CaseInsensitiveOption), None)) self.vmname.setEnabled(False) self.rename_vm_button.setEnabled(not self.vm.is_running()) self.delete_vm_button.setEnabled(not self.vm.is_running()) @@ -590,10 +593,10 @@ def __apply_basic_tab__(self): "to a halted Qube.
" "Do you want to start the Qube" " '{0}'?").format(netvm.name), - QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.Cancel) + QtWidgets.QMessageBox.StandardButton.Yes | + QtWidgets.QMessageBox.StandardButton.Cancel) - if reply == QtWidgets.QMessageBox.Yes: + if reply == QtWidgets.QMessageBox.StandardButton.Yes: netvm.start() self.vm.netvm = self.netVM.currentData() else: @@ -816,7 +819,7 @@ def clone_vm(self): with common_threads.busy_cursor(): clone_window = clone_vm.CloneVMDlg( self.qapp, self.qubesapp, src_vm=self.vm) - clone_window.exec_() + clone_window.exec() ######### advanced tab @@ -1078,7 +1081,7 @@ def __apply_advanced_tab__(self): def include_in_balancing_changed(self, state): if self.dev_list.selected_list.count() > 0: - if state == ui_settingsdlg.QtCore.Qt.Checked: + if state == ui_settingsdlg.QtCore.Qt.CheckState.Checked: self.dmm_warning_adv.show() self.dmm_warning_dev.show() else: @@ -1091,7 +1094,7 @@ def include_in_balancing_changed(self, state): def boot_from_cdrom_button_pressed(self): boot_dialog = bootfromdevice.VMBootFromDeviceWindow( self.vm.name, self.qapp, self.qubesapp, self) - if boot_dialog.exec_(): + if boot_dialog.exec(): self.save_and_apply() qvm_start.main( ['--cdrom', boot_dialog.cdrom_location, self.vm.name]) @@ -1382,9 +1385,10 @@ def __init_services_tab__(self): continue service = feature[len(SERVICE_PREFIX):] item = QtWidgets.QListWidgetItem(service) - item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked - if self.vm.features[feature] - else ui_settingsdlg.QtCore.Qt.Unchecked) + item.setCheckState( + ui_settingsdlg.QtCore.Qt.CheckState.Checked + if self.vm.features[feature] + else ui_settingsdlg.QtCore.Qt.CheckState.Unchecked) self.services_list.addItem(item) self.new_srv_dict[service] = self.vm.features[feature] except qubesadmin.exc.QubesDaemonAccessError: @@ -1436,7 +1440,7 @@ def __add_service__(self): self.tr('Service already on the list!')) return item = QtWidgets.QListWidgetItem(srv) - item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked) + item.setCheckState(ui_settingsdlg.QtCore.Qt.CheckState.Checked) self.services_list.addItem(item) self.new_srv_dict[srv] = True @@ -1460,7 +1464,8 @@ def __apply_services_tab__(self): for i in range(self.services_list.count()): item = self.services_list.item(i) self.new_srv_dict[str(item.text())] = \ - item.checkState() == ui_settingsdlg.QtCore.Qt.Checked + (item.checkState() == + ui_settingsdlg.QtCore.Qt.CheckState.Checked) for service, v in self.new_srv_dict.items(): feature = SERVICE_PREFIX + service @@ -1484,9 +1489,9 @@ def set_fw_model(self, model): self.fw_model = model self.rulesTreeView.setModel(model) self.rulesTreeView.header().setSectionResizeMode( - QtWidgets.QHeaderView.ResizeToContents) + QtWidgets.QHeaderView.ResizeMode.ResizeToContents) self.rulesTreeView.header().setSectionResizeMode( - 0, QtWidgets.QHeaderView.Stretch) + 0, QtWidgets.QHeaderView.ResizeMode.Stretch) self.set_allow(model.allow) if model.temp_full_access_expire_time: self.temp_full_access.setChecked(True) diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 85066171..8d970831 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -22,7 +22,7 @@ from qubesadmin import exc -from PyQt5 import QtWidgets, QtGui, QtCore # pylint: disable=import-error +from PyQt6 import QtWidgets, QtGui, QtCore # pylint: disable=import-error from . import ui_templatemanager # pylint: disable=no-name-in-module from . import utils @@ -49,11 +49,14 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.prepare_lists() self.initialize_table_events() - self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect( + self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Ok).clicked.connect( self.apply) self.buttonBox.button( - QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.cancel) - self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect( + QtWidgets.QDialogButtonBox.StandardButton.Cancel).clicked.connect( + self.cancel) + self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Reset).clicked.connect( self.reset) self.change_all_combobox.currentIndexChanged.connect( @@ -161,7 +164,7 @@ def sorting_changed(self, index, _order): if index == column_names.index('New template') or \ index == column_names.index('State'): self.vm_list.horizontalHeader().setSortIndicator( - -1, QtCore.Qt.AscendingOrder) + -1, QtCore.Qt.SortOrder.AscendingOrder) def clear_selection(self): for row in self.rows_in_table.values(): diff --git a/qubesmanager/tests/__init__.py b/qubesmanager/tests/__init__.py index 5590f56c..059e7833 100644 --- a/qubesmanager/tests/__init__.py +++ b/qubesmanager/tests/__init__.py @@ -2,7 +2,7 @@ import sys import qasync -from PyQt5 import QtWidgets +from PyQt6 import QtWidgets qtapp = None loop = None diff --git a/qubesmanager/tests/test_backup.py b/qubesmanager/tests/test_backup.py index e6475c29..5e554086 100644 --- a/qubesmanager/tests/test_backup.py +++ b/qubesmanager/tests/test_backup.py @@ -23,7 +23,7 @@ import unittest import unittest.mock -from PyQt5 import QtTest, QtCore, QtWidgets +from PyQt6 import QtTest, QtCore, QtWidgets from qubesadmin import Qubes, events, utils, exc from qubesmanager import backup from qubesmanager.tests import init_qtapp @@ -394,7 +394,7 @@ def test_21_loading_settings_error(self, mock_load): self.assertIn('incorrect_vm', self.dialog.warning_running_label.text()) @unittest.mock.patch('qubesmanager.backup_utils.load_backup_profile') - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.information') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.information') def test_22_loading_settings_exc(self, mock_info, mock_load): mock_load.side_effect = exc.QubesException('Error') @@ -431,7 +431,7 @@ def test_23_cancel_confirm(self, *_args): mock_remove.assert_called_once_with( '/etc/qubes/backup/qubes-manager-backup-tmp.conf') - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile') @unittest.mock.patch('qubesadmin.Qubes.qubesd_call', return_value=b'backup output') @@ -456,7 +456,7 @@ def test_24_cancel_in_progress(self, mock_call, *_args): mock_remove.assert_called_once_with( '/etc/qubes/backup/qubes-manager-backup-tmp.conf') - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') @unittest.mock.patch('os.system') @unittest.mock.patch('os.remove') @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile') @@ -496,7 +496,7 @@ def test_25_successful_backup(self, _a, _b, mock_remove, self.assertEqual(mock_warning.call_count, 0, "Backup succeeded but received warning") - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') @unittest.mock.patch('os.system') @unittest.mock.patch('os.remove') @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile') @@ -535,7 +535,7 @@ def test_26_success_backup_poweroff( self.assertEqual(mock_warning.call_count, 0, "Backup succeeded but received warning") - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') @unittest.mock.patch('os.system') @unittest.mock.patch('os.remove') @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile') @@ -573,7 +573,7 @@ def test_27_failed_backup( "Attempted shutdown at failed backup") self.assertEqual(mock_warn.call_count, 1) - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') @unittest.mock.patch('os.system') @unittest.mock.patch('os.remove') @unittest.mock.patch('qubesmanager.backup_utils.write_backup_profile') diff --git a/qubesmanager/tests/test_backup_utils.py b/qubesmanager/tests/test_backup_utils.py index 615b9f41..cc9653e9 100644 --- a/qubesmanager/tests/test_backup_utils.py +++ b/qubesmanager/tests/test_backup_utils.py @@ -21,7 +21,7 @@ # import logging.handlers import unittest.mock -from PyQt5 import QtWidgets +from PyQt6 import QtWidgets from qubesadmin import Qubes from qubesmanager import backup_utils diff --git a/qubesmanager/tests/test_clone_vm.py b/qubesmanager/tests/test_clone_vm.py index 9cfe53ea..6b81a130 100644 --- a/qubesmanager/tests/test_clone_vm.py +++ b/qubesmanager/tests/test_clone_vm.py @@ -23,7 +23,7 @@ import unittest import unittest.mock -from PyQt5 import QtTest, QtCore +from PyQt6 import QtTest, QtCore from qubesadmin import Qubes from qubesmanager.tests import init_qtapp from qubesmanager import clone_vm @@ -46,13 +46,13 @@ def setUp(self): # mock the progress dialog to speed testing up self.patcher_progress = unittest.mock.patch( - 'PyQt5.QtWidgets.QProgressDialog') + 'PyQt6.QtWidgets.QProgressDialog') self.mock_progress = self.patcher_progress.start() self.addCleanup(self.patcher_progress.stop) # mock the message dialog to not hang on success self.patcher_warning = unittest.mock.patch( - 'PyQt5.QtWidgets.QMessageBox.warning') + 'PyQt6.QtWidgets.QMessageBox.warning') self.mock_warning = self.patcher_warning.start() self.addCleanup(self.patcher_warning.stop) self.patcher_information = unittest.mock.patch( @@ -229,13 +229,13 @@ def setUp(self): # mock the progress dialog to speed testing up self.patcher_progress = unittest.mock.patch( - 'PyQt5.QtWidgets.QProgressDialog') + 'PyQt6.QtWidgets.QProgressDialog') self.mock_progress = self.patcher_progress.start() self.addCleanup(self.patcher_progress.stop) # mock the message dialog to not hang on success self.patcher_warning = unittest.mock.patch( - 'PyQt5.QtWidgets.QMessageBox.warning') + 'PyQt6.QtWidgets.QMessageBox.warning') self.mock_warning = self.patcher_warning.start() self.addCleanup(self.patcher_warning.stop) self.patcher_information = unittest.mock.patch( diff --git a/qubesmanager/tests/test_create_new_vm.py b/qubesmanager/tests/test_create_new_vm.py index 19456d2e..c3bf9e25 100644 --- a/qubesmanager/tests/test_create_new_vm.py +++ b/qubesmanager/tests/test_create_new_vm.py @@ -23,7 +23,7 @@ import unittest import unittest.mock -from PyQt5 import QtTest, QtCore +from PyQt6 import QtTest, QtCore from qubesadmin import Qubes from qubesmanager.tests import init_qtapp from qubesmanager import create_new_vm @@ -44,7 +44,7 @@ def setUp(self): # mock the progress dialog to speed testing up self.patcher_progress = unittest.mock.patch( - 'PyQt5.QtWidgets.QProgressDialog') + 'PyQt6.QtWidgets.QProgressDialog') self.mock_progress = self.patcher_progress.start() self.addCleanup(self.patcher_progress.stop) diff --git a/qubesmanager/tests/test_qube_manager.py b/qubesmanager/tests/test_qube_manager.py index 3e36b1f0..cc75a98f 100644 --- a/qubesmanager/tests/test_qube_manager.py +++ b/qubesmanager/tests/test_qube_manager.py @@ -30,9 +30,9 @@ import datetime import time -from PyQt5 import QtTest, QtCore, QtWidgets -from PyQt5.QtCore import (Qt, QSize) -from PyQt5.QtGui import (QIcon) +from PyQt6 import QtTest, QtCore, QtWidgets +from PyQt6.QtCore import (Qt, QSize) +from PyQt6.QtGui import (QIcon) from qubesadmin import Qubes, events, exc import qubesmanager.qube_manager as qube_manager @@ -78,7 +78,7 @@ def setUp(self): self.qtapp, self.loop = init_qtapp() self.mock_qprogress = unittest.mock.patch( - 'PyQt5.QtWidgets.QProgressDialog') + 'PyQt6.QtWidgets.QProgressDialog') self.mock_qprogress.start() self.addCleanup(self.mock_qprogress.stop) @@ -268,9 +268,9 @@ def test_013_incorrect_settings_file(self): mock_settings.side_effect = ( lambda x, *args, **kwargs: settings_result_dict.get(x)) - with unittest.mock.patch('PyQt5.QtCore.QSettings.value', + with unittest.mock.patch('PyQt6.QtCore.QSettings.value', mock_settings),\ - unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning')\ + unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning')\ as mock_warning: self.dialog = qube_manager.VmManagerWindow( self.qtapp, self.qapp, self.dispatcher) @@ -338,7 +338,7 @@ def test_203_vm_open_apps(self, mock_window): mock_window.assert_called_once_with( selected_vm, "applications", self.qtapp, self.qapp, self.dialog) - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') def test_204_vm_keyboard(self, mock_message): selected_vm = self._select_non_admin_vm(running=True) self.assertIsNotNone(selected_vm, "No valid non-admin VM found") @@ -353,7 +353,7 @@ def test_204_vm_keyboard(self, mock_message): self.assertEqual(mock_message.call_count, 0, "VM does not support new layout change") - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') def test_205_vm_keyboard_not_running(self, mock_message): selected_vm = self._select_non_admin_vm(running=False) self.assertIsNotNone(selected_vm, "No valid non-admin VM found") @@ -383,7 +383,7 @@ def test_208_update_vm_admin(self): mock_update.assert_called_once_with([selected_vm.name]) mock_update().start.assert_called_once_with() - @unittest.mock.patch("PyQt5.QtWidgets.QInputDialog.getText", + @unittest.mock.patch("PyQt6.QtWidgets.QInputDialog.getText", return_value=("command to run", True)) def test_209_run_command_in_vm(self, _): selected_vm = self._select_non_admin_vm() @@ -404,7 +404,7 @@ def test_210_run_command_in_adminvm(self): self.assertFalse(self.dialog.action_run_command_in_vm.isEnabled(), "Should not be able to run commands for dom0") - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.warning") + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.warning") def test_211_pausevm(self, mock_warn): selected_vm = self._select_non_admin_vm(running=True) @@ -442,9 +442,9 @@ def test_213_resume_running_vm(self): self._select_non_admin_vm(running=True) self.assertFalse(self.dialog.action_resumevm.isEnabled()) - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes) - @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot') + @unittest.mock.patch('PyQt6.QtCore.QTimer.singleShot') @unittest.mock.patch('qubesmanager.qube_manager.VmShutdownMonitor') def test_214_shutdownvm(self, mock_monitor, mock_timer, _): selected_vm = self._select_non_admin_vm(running=True) @@ -492,8 +492,8 @@ def test_218_remove_vm_dependencies(self, mock_dependencies, mock_msgbox): mock_msgbox().show.assert_called_with() - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') - @unittest.mock.patch("PyQt5.QtWidgets.QInputDialog.getText") + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') + @unittest.mock.patch("PyQt6.QtWidgets.QInputDialog.getText") @unittest.mock.patch('qubesadmin.utils.vm_dependencies') def test_219_remove_vm_no_depencies( self, mock_dependencies, mock_input, mock_warning): @@ -527,9 +527,9 @@ def test_220_restartvm_halted_vm(self): self._select_non_admin_vm(running=False) self.assertFalse(self.dialog.action_restartvm.isEnabled()) - @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot') + @unittest.mock.patch('PyQt6.QtCore.QTimer.singleShot') @unittest.mock.patch('qubesmanager.qube_manager.VmShutdownMonitor') - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes) def test_221_restartvm_running_vm(self, _msgbox, mock_monitor, _qtimer): selected_vm = self._select_non_admin_vm(running=True) @@ -545,7 +545,7 @@ def test_221_restartvm_running_vm(self, _msgbox, mock_monitor, _qtimer): selected_vm, 1000, True, unittest.mock.ANY) @unittest.mock.patch('qubesmanager.qube_manager.StartVMThread') - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes) def test_222_restartvm_shutdown_meantime(self, _, mock_thread): selected_vm = self._select_non_admin_vm(running=True) @@ -572,7 +572,7 @@ def test_223_updatevm_running(self, mock_thread): self.dialog.clear_threads) mock_thread().start.assert_called_once_with() - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Yes) def test_224_killvm(self, _): selected_vm = self._select_non_admin_vm(running=True) @@ -582,7 +582,7 @@ def test_224_killvm(self, _): action.trigger() mock_kill.assert_called_once_with() - @unittest.mock.patch("PyQt5.QtWidgets.QMessageBox.question", + @unittest.mock.patch("PyQt6.QtWidgets.QMessageBox.question", return_value=QtWidgets.QMessageBox.Cancel) def test_225_killvm_cancel(self, _): selected_vm = self._select_non_admin_vm(running=True) @@ -686,7 +686,7 @@ def test_234_searchbox(self): "Incorrect number of vms shown for cleared search box") def test_235_hide_show_toolbars(self): - with unittest.mock.patch('PyQt5.QtCore.QSettings.setValue')\ + with unittest.mock.patch('PyQt6.QtCore.QSettings.setValue')\ as mock_setvalue: self.dialog.action_menubar.trigger() mock_setvalue.assert_called_with('view/menubar_visible', False) @@ -714,7 +714,7 @@ def test_236_clear_searchbox(self): self.assertEqual(expected_number, actual_number, "Incorrect number of vms shown for cleared search box") - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.question') @listen_for_events def test_240_network_menu_single(self, mock_question): mock_question.return_value = QtWidgets.QMessageBox.Yes @@ -777,7 +777,7 @@ def test_240_network_menu_single(self, mock_question): mock_question.assert_called() self.assertTrue(selected_vm.property_is_default('netvm')) - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.question') @listen_for_events def test_241_network_menu_multiple(self, mock_question): mock_question.return_value = QtWidgets.QMessageBox.Yes @@ -828,7 +828,7 @@ def test_241_network_menu_multiple(self, mock_question): self.assertEqual(str(vault.netvm), 'sys-net') mock_question.reset_mock() - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.question') @listen_for_events @skip_if_running('work') def test_250_template_menu_single(self, mock_question): @@ -876,7 +876,7 @@ def test_250_template_menu_single(self, mock_question): self.assertEqual(str(selected_vm.template), str(new_template)) mock_question.reset_mock() - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.question') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.question') @listen_for_events @skip_if_running('work', 'personal', 'untrusted') def test_251_template_menu_multiple(self, mock_question): @@ -946,8 +946,8 @@ def test_251_template_menu_multiple(self, mock_question): self.assertEqual(str(untrusted.template), str(new_template)) - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.information') - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.information') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') def test_300_clear_threads(self, mock_warning, mock_info): mock_thread_finished_ok = unittest.mock.Mock( spec=['isFinished', 'msg', 'msg_is_success'], @@ -1619,7 +1619,7 @@ def test_23_update_vm_thread_error(self, mock_call): class VMShutdownMonitorTest(unittest.TestCase): @unittest.mock.patch('qubesmanager.qube_manager.QMessageBox') - @unittest.mock.patch('PyQt5.QtCore.QTimer') + @unittest.mock.patch('PyQt6.QtCore.QTimer') def test_01_vm_shutdown_correct(self, mock_timer, mock_question): mock_vm = unittest.mock.Mock() mock_vm.is_running.return_value = False @@ -1634,7 +1634,7 @@ def test_01_vm_shutdown_correct(self, mock_timer, mock_question): monitor.restart_vm_if_needed.assert_called_once_with() @unittest.mock.patch('qubesmanager.qube_manager.QMessageBox') - @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot') + @unittest.mock.patch('PyQt6.QtCore.QTimer.singleShot') def test_02_vm_not_shutdown_wait(self, mock_timer, mock_question): mock_question().clickedButton.return_value = 1 mock_question().addButton.return_value = 0 @@ -1652,7 +1652,7 @@ def test_02_vm_not_shutdown_wait(self, mock_timer, mock_question): self.assertEqual(mock_timer.call_count, 1) @unittest.mock.patch('qubesmanager.qube_manager.QMessageBox') - @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot') + @unittest.mock.patch('PyQt6.QtCore.QTimer.singleShot') def test_03_vm_kill(self, mock_timer, mock_question): mock_question().clickedButton.return_value = 1 mock_question().addButton.return_value = 1 @@ -1673,7 +1673,7 @@ def test_03_vm_kill(self, mock_timer, mock_question): monitor.restart_vm_if_needed.assert_called_once_with() @unittest.mock.patch('qubesmanager.qube_manager.QMessageBox') - @unittest.mock.patch('PyQt5.QtCore.QTimer.singleShot') + @unittest.mock.patch('PyQt6.QtCore.QTimer.singleShot') def test_04_check_later(self, mock_timer, mock_question): mock_vm = unittest.mock.Mock() mock_vm.is_running.return_value = True diff --git a/qubesmanager/tests/test_vm_settings.py b/qubesmanager/tests/test_vm_settings.py index 35b20282..52128810 100644 --- a/qubesmanager/tests/test_vm_settings.py +++ b/qubesmanager/tests/test_vm_settings.py @@ -23,7 +23,7 @@ import unittest import unittest.mock -from PyQt5 import QtTest, QtCore +from PyQt6 import QtTest, QtCore from qubesadmin import Qubes import qubesmanager.settings as vm_settings from qubesmanager.tests import init_qtapp @@ -35,7 +35,7 @@ def setUp(self): self.qtapp, self.loop = init_qtapp() self.mock_qprogress = unittest.mock.patch( - 'PyQt5.QtWidgets.QProgressDialog') + 'PyQt6.QtWidgets.QProgressDialog') self.mock_qprogress.start() self.addCleanup(self.mock_qprogress.stop) @@ -295,8 +295,8 @@ def test_10_increase_private_storage(self): # TODO are dependencies correctly processed - @unittest.mock.patch('PyQt5.QtWidgets.QProgressDialog') - @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText') + @unittest.mock.patch('PyQt6.QtWidgets.QProgressDialog') + @unittest.mock.patch('PyQt6.QtWidgets.QInputDialog.getText') @unittest.mock.patch('qubesmanager.settings.RenameVMThread') def test_11_rename_vm(self, mock_thread, mock_input, _): self.vm = self.qapp.add_new_vm("AppVM", "test-vm", "blue") @@ -325,9 +325,9 @@ def test_12_clone_vm(self, mock_clone): src_vm=self.vm) - @unittest.mock.patch('PyQt5.QtWidgets.QMessageBox.warning') - @unittest.mock.patch('PyQt5.QtWidgets.QProgressDialog') - @unittest.mock.patch('PyQt5.QtWidgets.QInputDialog.getText') + @unittest.mock.patch('PyQt6.QtWidgets.QMessageBox.warning') + @unittest.mock.patch('PyQt6.QtWidgets.QProgressDialog') + @unittest.mock.patch('PyQt6.QtWidgets.QInputDialog.getText') @unittest.mock.patch('qubesmanager.common_threads.RemoveVMThread') def test_13_remove_vm(self, mock_thread, mock_input, _, mock_warning): self.vm = self.qapp.add_new_vm("AppVM", "test-vm", "blue") diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py index 45621017..3f246970 100644 --- a/qubesmanager/utils.py +++ b/qubesmanager/utils.py @@ -36,7 +36,7 @@ import pathlib import shutil -from PyQt5 import QtWidgets, QtCore, QtGui # pylint: disable=import-error +from PyQt6 import QtWidgets, QtCore, QtGui # pylint: disable=import-error # important usage note: which initialize_widget should I use? @@ -103,8 +103,8 @@ def __init__(self, *args, **kwargs): self.pattern = r'(\d+\.?\d?) ?(GiB|MiB)' self.regex = re.compile(self.pattern) - self.validator = QtGui.QRegExpValidator(QtCore.QRegExp( - self.pattern), self) + self.validator = QtGui.QRegularExpressionValidator( + QtCore.QRegularExpression(self.pattern), self) def textFromValue(self, v: int) -> str: if v > 1024: @@ -516,7 +516,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): msg_box = QtWidgets.QMessageBox() msg_box.setDetailedText(strace) - msg_box.setIcon(QtWidgets.QMessageBox.Critical) + msg_box.setIcon(QtWidgets.QMessageBox.Icon.Critical) msg_box.setWindowTitle(QtCore.QCoreApplication.translate( "ManagerUtils", "Houston, we have a problem...")) msg_box.setText(QtCore.QCoreApplication.translate( @@ -525,7 +525,7 @@ def handle_exception(exc_type, exc_value, exc_traceback): "{0}
at line {1}
of file " "{2}.

").format(error, line, filename)) - msg_box.exec_() + msg_box.exec() def run_asynchronous(window_class): @@ -543,13 +543,16 @@ def run_asynchronous(window_class): qt_app.lastWindowClosed.connect(loop_shutdown) - qubes_app = qubesadmin.Qubes() + # qubes_app = qubesadmin.Qubes() + import qubesadmin.tests.mock_app as mock_app + qapp = mock_app.MockQubesComplete() + dispatcher = mock_app.MockDispatcher(qapp) loop = qasync.QEventLoop(qt_app) asyncio.set_event_loop(loop) - dispatcher = events.EventsDispatcher(qubes_app) + # dispatcher = events.EventsDispatcher(qubes_app) - window = window_class(qt_app, qubes_app, dispatcher) + window = window_class(qt_app, qapp, dispatcher) if hasattr(window, "setup_application"): window.setup_application() @@ -591,7 +594,7 @@ def run_synchronous(window_class): window.show() - qt_app.exec_() + qt_app.exec() qt_app.exit() return window diff --git a/rpm_spec/qmgr.spec.in b/rpm_spec/qmgr.spec.in index 43d9e527..84cb4c2f 100644 --- a/rpm_spec/qmgr.spec.in +++ b/rpm_spec/qmgr.spec.in @@ -8,11 +8,7 @@ Vendor: Invisible Things Lab License: GPL URL: https://www.qubes-os.org Requires: python%{python3_pkgversion} -%if 0%{?fedora} >= 34 -Requires: python%{python3_pkgversion}-qt5 -%else -Requires: python%{python3_pkgversion}-PyQt5 -%endif +Requires: python%{python3_pkgversion}-pyqt6 Requires: python%{python3_pkgversion}-inotify Requires: python%{python3_pkgversion}-qubesadmin >= 4.3.0 Requires: python%{python3_pkgversion}-qasync @@ -21,16 +17,16 @@ Requires: qubes-artwork Requires: pmount Requires: cryptsetup Requires: wmctrl -BuildRequires: python%{python3_pkgversion}-PyQt5-devel +BuildRequires: python%{python3_pkgversion}-PyQt6-devel BuildRequires: python%{python3_pkgversion}-devel BuildRequires: python%{python3_pkgversion}-setuptools BuildRequires: make %if 0%{?fedora} >= 33 || 0%{?rhel} >= 7 -BuildRequires: qt5-qtbase-devel +BuildRequires: qt6-qtbase-devel %else -BuildRequires: qt5-devel +BuildRequires: qt6-devel %endif -BuildRequires: qt5-linguist +BuildRequires: qt6-linguist AutoReq: 0 Source0: %{name}-%{version}.tar.gz diff --git a/ui/backupdlg.ui b/ui/backupdlg.ui index d8d737fc..4b8402e8 100644 --- a/ui/backupdlg.ui +++ b/ui/backupdlg.ui @@ -378,11 +378,14 @@ - -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> diff --git a/ui/clonevmdlg.ui b/ui/clonevmdlg.ui index 6a9d64b3..38266b4c 100644 --- a/ui/clonevmdlg.ui +++ b/ui/clonevmdlg.ui @@ -14,8 +14,7 @@ Clone qube - - .. + diff --git a/ui/newappvmdlg.ui b/ui/newappvmdlg.ui index fc2c8b6f..6b2cd679 100644 --- a/ui/newappvmdlg.ui +++ b/ui/newappvmdlg.ui @@ -14,8 +14,7 @@ Create new qube - - .. + diff --git a/ui/qubemanager.ui b/ui/qubemanager.ui index 345c2533..1a061354 100644 --- a/ui/qubemanager.ui +++ b/ui/qubemanager.ui @@ -23,8 +23,7 @@ Qube Manager - - .. + @@ -388,7 +387,7 @@ template Network - + :/netvm.png:/netvm.png @@ -801,8 +800,7 @@ template - - .. + &Qubes OS @@ -986,7 +984,7 @@ template - + :/log.png:/log.png diff --git a/ui/qvmtemplate.ui b/ui/qvmtemplate.ui index 79554482..cc48c59d 100644 --- a/ui/qvmtemplate.ui +++ b/ui/qvmtemplate.ui @@ -92,8 +92,7 @@ - - .. + Refresh @@ -101,8 +100,7 @@ - - .. + Apply diff --git a/ui/restoredlg.ui b/ui/restoredlg.ui index bba923d7..03dcf3c6 100644 --- a/ui/restoredlg.ui +++ b/ui/restoredlg.ui @@ -225,11 +225,14 @@ - -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> diff --git a/ui/templatemanager.ui b/ui/templatemanager.ui index 16e5051b..46c0f7c4 100644 --- a/ui/templatemanager.ui +++ b/ui/templatemanager.ui @@ -46,12 +46,12 @@ false - - 20 - 4 + + 20 + true diff --git a/ui/templatemanger2.ui b/ui/templatemanger2.ui index ecd98f35..1625857a 100644 --- a/ui/templatemanger2.ui +++ b/ui/templatemanger2.ui @@ -180,8 +180,7 @@ - - .. + Repository settings @@ -204,8 +203,7 @@ - - .. + Help