From c0eb8b55ab3c5a4df8caa03ec4111cbce62a0b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Marczykowska-G=C3=B3recka?= Date: Tue, 4 Dec 2018 00:56:24 +0100 Subject: [PATCH] Improved template manager UI changed it to be more intuitive and less annoying. --- qubesmanager/template_manager.py | 134 ++++++++++++++++++++++--------- ui/templatemanager.ui | 63 ++++++++++++--- 2 files changed, 147 insertions(+), 50 deletions(-) diff --git a/qubesmanager/template_manager.py b/qubesmanager/template_manager.py index 92f8b8a..0401672 100644 --- a/qubesmanager/template_manager.py +++ b/qubesmanager/template_manager.py @@ -37,9 +37,9 @@ from PyQt4 import QtCore # pylint: disable=import-error from PyQt4 import Qt # pylint: disable=import-error -import ui_templatemanager # pylint: disable=no-name-in-module +from . import ui_templatemanager # pylint: disable=no-name-in-module -column_names = ['Qube', 'State', 'Current template', 'New template'] +column_names = ['State', 'Qube', 'Current template', 'New template'] class TemplateManagerWindow( @@ -58,7 +58,7 @@ class TemplateManagerWindow( self.templates = [] self.timers = [] - self.prepare_vm_list() + self.prepare_lists() self.initialize_table_events() self.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect( @@ -68,11 +68,20 @@ class TemplateManagerWindow( self.buttonBox.button(QtGui.QDialogButtonBox.Reset).clicked.connect( self.reset) + self.change_all_combobox.currentIndexChanged.connect( + self.change_all_changed) + self.clear_selection_button.clicked.connect(self.clear_selection) + self.vm_list.show() - def prepare_vm_list(self): + def prepare_lists(self): self.templates = [vm.name for vm in self.qubes_app.domains - if vm.klass == 'TemplateVM'] + if vm.klass == 'TemplateVM'] + + self.change_all_combobox.addItem('(select template)') + for template in self.templates: + self.change_all_combobox.addItem(template) + vms_with_templates = [vm for vm in self.qubes_app.domains if getattr(vm, 'template', None)] @@ -86,11 +95,13 @@ class TemplateManagerWindow( self.rows_in_table[vm.name] = row row_count += 1 - self.vm_list.setHorizontalHeaderLabels(['Qube', '', 'Current', 'New']) + self.vm_list.setHorizontalHeaderLabels(['', 'Qube', 'Current', 'New']) self.vm_list.resizeColumnsToContents() def initialize_table_events(self): self.vm_list.cellDoubleClicked.connect(self.table_double_click) + self.vm_list.cellClicked.connect(self.table_click) + self.vm_list.horizontalHeader().sortIndicatorChanged.connect( self.sorting_changed) @@ -153,11 +164,27 @@ class TemplateManagerWindow( def sorting_changed(self, index, _order): # this is very much not perfect, but QTableWidget does not # want to be sorted on custom widgets - # possible fix - try to set data of dummy items. - if index == column_names.index('New template'): + if index == column_names.index('New template') or \ + index == column_names.index('State'): self.vm_list.horizontalHeader().setSortIndicator( -1, QtCore.Qt.AscendingOrder) + def clear_selection(self): + for row in self.rows_in_table.values(): + row.checkbox.setChecked(False) + + def change_all_changed(self): + if self.change_all_combobox.currentIndex() == 0: + return + selected_template = self.change_all_combobox.currentText() + + for row in self.rows_in_table.values(): + if row.checkbox.isChecked(): + row.new_item.setCurrentIndex( + row.new_item.findText(selected_template)) + + self.change_all_combobox.setCurrentIndex(0) + def table_double_click(self, row, column): template_column = column_names.index('Current template') @@ -166,16 +193,33 @@ class TemplateManagerWindow( template_name = self.vm_list.item(row, column).text() - self.vm_list.clearSelection() - for row_number in range(0, self.vm_list.rowCount()): if self.vm_list.item( row_number, template_column).text() == template_name: - self.vm_list.selectRow(row_number) + 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) + + def table_click(self, row, column): + if column == column_names.index('New template'): + return + + checkbox = self.vm_list.cellWidget(row, column_names.index('State')) + if not checkbox: + return + + checkbox.setChecked(not checkbox.isChecked()) def reset(self): for row in self.rows_in_table.values(): row.new_item.reset_choice() + row.checkbox.setChecked(False) def cancel(self): self.close() @@ -197,7 +241,6 @@ class TemplateManagerWindow( self.tr( "Errors encountered on template change in the following " "qubes:
{}.").format("
".join(error_messages))) - self.close() @@ -270,15 +313,6 @@ class NewTemplateItem(QtGui.QComboBox): self.changed = False self.setStyleSheet('font-weight: normal') - for row_index in self.table_widget.selectionModel().selectedRows(): - widget = self.table_widget.cellWidget( - row_index.row(), column_names.index('New template')) - if widget.isEnabled() and widget.currentText() !=\ - self.currentText(): - widget.setCurrentIndex(widget.findText(self.currentText())) - - self.table_widget.clearSelection() - def reset_choice(self): self.setCurrentIndex(self.findText(self.start_value)) @@ -287,14 +321,17 @@ class VMRow: # pylint: disable=too-few-public-methods def __init__(self, vm, row_no, table_widget, columns, templates): self.vm = vm - - # icon and name - self.name_item = VMNameItem(self.vm) - table_widget.setItem(row_no, columns.index('Qube'), self.name_item) + self.table_widget = table_widget + self.templates = templates # state self.state_item = StatusItem(self.vm) table_widget.setItem(row_no, columns.index('State'), self.state_item) + self.checkbox = QtGui.QCheckBox() + + # icon and name + self.name_item = VMNameItem(self.vm) + table_widget.setItem(row_no, columns.index('Qube'), self.name_item) # current template self.current_item = CurrentTemplateItem(self.vm) @@ -302,29 +339,48 @@ class VMRow: self.current_item) # new template - # this is needed to make the cell correctly selectable/non-selectable - self.dummy_new_item = QtGui.QTableWidgetItem() + self.dummy_new_item = QtGui.QTableWidgetItem("qube is running") self.new_item = NewTemplateItem(self.vm, templates, table_widget) - table_widget.setCellWidget(row_no, columns.index('New template'), - self.new_item) table_widget.setItem(row_no, columns.index('New template'), self.dummy_new_item) - self.vm_state_change(self.vm.is_running()) + self.vm_state_change(self.vm.is_running(), row_no) - def vm_state_change(self, is_running): - self.new_item.setEnabled(not is_running) + def vm_state_change(self, is_running, row=None): self.state_item.set_state(is_running) - items = [self.name_item, self.state_item, self.current_item, - self.dummy_new_item] + if not row: + row = 0 + while row < self.table_widget.rowCount(): + if self.table_widget.item( + row, column_names.index('Qube')).text() == \ + self.name_item.text(): + break + row += 1 - for item in items: - if is_running: - item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable) - else: - item.setFlags(item.flags() | QtCore.Qt.ItemIsSelectable) + # hiding cellWidgets does not work in a qTableWidget + if not is_running: + self.new_item = NewTemplateItem(self.vm, self.templates, + self.table_widget) + self.checkbox = QtGui.QCheckBox() + + self.table_widget.setCellWidget( + row, column_names.index('New template'), self.new_item) + self.table_widget.setCellWidget( + row, column_names.index('State'), self.checkbox) + else: + new_template = self.table_widget.cellWidget( + row, column_names.index('New template')) + if new_template: + self.table_widget.removeCellWidget( + row, column_names.index('New template')) + + checkbox = self.table_widget.cellWidget( + row, column_names.index('State')) + if checkbox: + self.table_widget.removeCellWidget( + row, column_names.index('State')) # Bases on the original code by: # Copyright (c) 2002-2007 Pascal Varet diff --git a/ui/templatemanager.ui b/ui/templatemanager.ui index 7a69ba8..c100446 100644 --- a/ui/templatemanager.ui +++ b/ui/templatemanager.ui @@ -14,6 +14,12 @@ Template Manager + + + 0 + 0 + + @@ -26,7 +32,7 @@ QAbstractItemView::NoEditTriggers - QAbstractItemView::MultiSelection + QAbstractItemView::NoSelection QAbstractItemView::SelectRows @@ -47,18 +53,44 @@ 4 - false + true false + + + + + + + 0 + 0 + + + + Change all selected to: + + + + + + + + 0 + 0 + + + + + + - Select multiple qubes to change template in all of them at once. -To select all qubes with a given template, double-click the template name in any row. + <html><head/><body><p>To select all qubes with a given template, double-click the template name in any row.</p><p><span style=" font-weight:600;">IMPORTANT</span>: Changes will be applied only when you click OK.</p></body></html> true @@ -66,17 +98,26 @@ To select all qubes with a given template, double-click the template name in any - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset - - + + + + + Clear Selection + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + + - pushButton_2 - verticalLayoutWidget