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