From fd23b57b625f74f38e0960e12294a7147542efa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Marczykowska-G=C3=B3recka?= Date: Wed, 11 Sep 2024 15:38:08 +0200 Subject: [PATCH] Fixes to Template Switcher - improved behavior on double-click - improved behavior on OK (progress dialog) - no auto-close on OK fixes QubesOS/qubes-issues#7639 --- qubesmanager/common_threads.py | 20 ++++++++++ qubesmanager/template_manager.py | 66 ++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/qubesmanager/common_threads.py b/qubesmanager/common_threads.py index d3a0ebcf..5f94a06e 100644 --- a/qubesmanager/common_threads.py +++ b/qubesmanager/common_threads.py @@ -72,3 +72,23 @@ def run(self): self.msg_is_success = True except exc.QubesException as ex: self.msg = (self.tr("Error while cloning qube!"), str(ex)) + + +class ChangeTemplatesThread(QtCore.QThread): + def __init__(self, progress_dialog, items_to_change, qubes_app): + super().__init__() + self.dialog = progress_dialog + self.items = items_to_change + self.qubes_app = qubes_app + self.errors = {} + + def run(self): + i = 0 + for vm, row in self.items: + i += 1 + try: + setattr(self.qubes_app.domains[vm], + 'template', row.new_item.currentText()) + except Exception as ex: # pylint: disable=broad-except + self.errors[vm] = str(ex) + self.dialog.setValue(i) diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 85066171..70e6aec9 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -26,6 +26,7 @@ from . import ui_templatemanager # pylint: disable=no-name-in-module from . import utils +from . import common_threads column_names = ['State', 'Qube', 'Current template', 'New template'] @@ -45,14 +46,20 @@ def __init__(self, qt_app, qubes_app, dispatcher, parent=None): self.rows_in_table = {} self.templates = [] self.timers = [] + self.dialog = None + self.thread = None self.prepare_lists() self.initialize_table_events() self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect( self.apply) + self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Ok).setText('Apply') self.buttonBox.button( QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.cancel) + self.buttonBox.button( + QtWidgets.QDialogButtonBox.StandardButton.Cancel).setText('Close') self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect( self.reset) @@ -179,9 +186,12 @@ def change_all_changed(self): row.new_item.findText(selected_template)) self.change_all_combobox.setCurrentIndex(0) + self.clear_selection() def table_double_click(self, row, column): template_column = column_names.index('Current template') + current_state = self.vm_list.cellWidget( + row, column_names.index('State')).isChecked() if column != template_column: return @@ -194,16 +204,13 @@ def table_double_click(self, row, column): checkbox = self.vm_list.cellWidget( row_number, column_names.index('State')) if checkbox: - if row_number == row: - # this is because double click registers as a - # single click and a double click - checkbox.setChecked(False) - else: - checkbox.setChecked(True) + checkbox.setChecked(not current_state) def table_click(self, row, column): if column == column_names.index('New template'): return + if column == column_names.index('Current template'): + return checkbox = self.vm_list.cellWidget(row, column_names.index('State')) if not checkbox: @@ -222,14 +229,28 @@ def cancel(self): self.close() def apply(self): - errors = {} - for vm, row in self.rows_in_table.items(): - if row.new_item and row.new_item.changed: - try: - setattr(self.qubes_app.domains[vm], - 'template', row.new_item.currentText()) - except Exception as ex: # pylint: disable=broad-except - errors[vm] = str(ex) + items_to_change = [ + (vm, row) for vm, row in self.rows_in_table.items() + if row.new_item and row.new_item.changed] + + # show a "in progress" dialog + self.dialog = QtWidgets.QProgressDialog( + "Changing templates...", None, 0, len(items_to_change), self) + self.dialog.setCancelButton(None) + self.dialog.setModal(True) + self.dialog.show() + + self.thread = common_threads.ChangeTemplatesThread(self.dialog, + items_to_change, + self.qubes_app) + self.thread.finished.connect(self.finish_changes) + self.thread.start() + + def finish_changes(self): + self.dialog.hide() + + errors = self.thread.errors + if errors: error_messages = [vm + ": " + error for vm, error in errors.items()] QtWidgets.QMessageBox.warning( @@ -238,7 +259,14 @@ def apply(self): self.tr( "Errors encountered on template change in the following " "qubes:
{}.").format("
".join(error_messages))) - self.close() + + for vm, row in self.rows_in_table.items(): + if row.new_item and row.new_item.changed: + vm_object = self.qubes_app.domains[vm] + + if vm_object.template.name == row.new_item.currentText(): + row.new_item.reset_start_value() + row.current_item.reset_template_name() class VMNameItem(QtWidgets.QTableWidgetItem): @@ -288,6 +316,9 @@ def __lt__(self, other): return self.vm.name < other.vm.name return self.text() < other.text() + def reset_template_name(self): + self.setText(self.vm.template.name) + class NewTemplateItem(QtWidgets.QComboBox): def __init__(self, vm, templates, table_widget): @@ -314,6 +345,11 @@ def choice_changed(self): def reset_choice(self): self.setCurrentIndex(self.findText(self.start_value)) + def reset_start_value(self): + self.start_value = self.currentText() + self.changed = False + self.setStyleSheet('font-weight: normal') + class VMRow: # pylint: disable=too-few-public-methods