Improved template manager UI

changed it to be more intuitive and less annoying.
This commit is contained in:
Marta Marczykowska-Górecka 2018-12-04 00:56:24 +01:00
parent 21224a6cbd
commit c0eb8b55ab
No known key found for this signature in database
GPG Key ID: 9A752C30B26FD04B
2 changed files with 147 additions and 50 deletions

View File

@ -37,9 +37,9 @@ from PyQt4 import QtCore # pylint: disable=import-error
from PyQt4 import Qt # 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( class TemplateManagerWindow(
@ -58,7 +58,7 @@ class TemplateManagerWindow(
self.templates = [] self.templates = []
self.timers = [] self.timers = []
self.prepare_vm_list() self.prepare_lists()
self.initialize_table_events() self.initialize_table_events()
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect( self.buttonBox.button(QtGui.QDialogButtonBox.Ok).clicked.connect(
@ -68,11 +68,20 @@ class TemplateManagerWindow(
self.buttonBox.button(QtGui.QDialogButtonBox.Reset).clicked.connect( self.buttonBox.button(QtGui.QDialogButtonBox.Reset).clicked.connect(
self.reset) 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() self.vm_list.show()
def prepare_vm_list(self): def prepare_lists(self):
self.templates = [vm.name for vm in self.qubes_app.domains 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 vms_with_templates = [vm for vm in self.qubes_app.domains
if getattr(vm, 'template', None)] if getattr(vm, 'template', None)]
@ -86,11 +95,13 @@ class TemplateManagerWindow(
self.rows_in_table[vm.name] = row self.rows_in_table[vm.name] = row
row_count += 1 row_count += 1
self.vm_list.setHorizontalHeaderLabels(['Qube', '', 'Current', 'New']) self.vm_list.setHorizontalHeaderLabels(['', 'Qube', 'Current', 'New'])
self.vm_list.resizeColumnsToContents() self.vm_list.resizeColumnsToContents()
def initialize_table_events(self): def initialize_table_events(self):
self.vm_list.cellDoubleClicked.connect(self.table_double_click) self.vm_list.cellDoubleClicked.connect(self.table_double_click)
self.vm_list.cellClicked.connect(self.table_click)
self.vm_list.horizontalHeader().sortIndicatorChanged.connect( self.vm_list.horizontalHeader().sortIndicatorChanged.connect(
self.sorting_changed) self.sorting_changed)
@ -153,11 +164,27 @@ class TemplateManagerWindow(
def sorting_changed(self, index, _order): def sorting_changed(self, index, _order):
# this is very much not perfect, but QTableWidget does not # this is very much not perfect, but QTableWidget does not
# want to be sorted on custom widgets # want to be sorted on custom widgets
# possible fix - try to set data of dummy items. if index == column_names.index('New template') or \
if index == column_names.index('New template'): index == column_names.index('State'):
self.vm_list.horizontalHeader().setSortIndicator( self.vm_list.horizontalHeader().setSortIndicator(
-1, QtCore.Qt.AscendingOrder) -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): def table_double_click(self, row, column):
template_column = column_names.index('Current template') template_column = column_names.index('Current template')
@ -166,16 +193,33 @@ class TemplateManagerWindow(
template_name = self.vm_list.item(row, column).text() template_name = self.vm_list.item(row, column).text()
self.vm_list.clearSelection()
for row_number in range(0, self.vm_list.rowCount()): for row_number in range(0, self.vm_list.rowCount()):
if self.vm_list.item( if self.vm_list.item(
row_number, template_column).text() == template_name: 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): def reset(self):
for row in self.rows_in_table.values(): for row in self.rows_in_table.values():
row.new_item.reset_choice() row.new_item.reset_choice()
row.checkbox.setChecked(False)
def cancel(self): def cancel(self):
self.close() self.close()
@ -197,7 +241,6 @@ class TemplateManagerWindow(
self.tr( self.tr(
"Errors encountered on template change in the following " "Errors encountered on template change in the following "
"qubes: <br> {}.").format("<br> ".join(error_messages))) "qubes: <br> {}.").format("<br> ".join(error_messages)))
self.close() self.close()
@ -270,15 +313,6 @@ class NewTemplateItem(QtGui.QComboBox):
self.changed = False self.changed = False
self.setStyleSheet('font-weight: normal') 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): def reset_choice(self):
self.setCurrentIndex(self.findText(self.start_value)) self.setCurrentIndex(self.findText(self.start_value))
@ -287,14 +321,17 @@ class VMRow:
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
def __init__(self, vm, row_no, table_widget, columns, templates): def __init__(self, vm, row_no, table_widget, columns, templates):
self.vm = vm self.vm = vm
self.table_widget = table_widget
# icon and name self.templates = templates
self.name_item = VMNameItem(self.vm)
table_widget.setItem(row_no, columns.index('Qube'), self.name_item)
# state # state
self.state_item = StatusItem(self.vm) self.state_item = StatusItem(self.vm)
table_widget.setItem(row_no, columns.index('State'), self.state_item) 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 # current template
self.current_item = CurrentTemplateItem(self.vm) self.current_item = CurrentTemplateItem(self.vm)
@ -302,29 +339,48 @@ class VMRow:
self.current_item) self.current_item)
# new template # new template
# this is needed to make the cell correctly selectable/non-selectable self.dummy_new_item = QtGui.QTableWidgetItem("qube is running")
self.dummy_new_item = QtGui.QTableWidgetItem()
self.new_item = NewTemplateItem(self.vm, templates, table_widget) 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'), table_widget.setItem(row_no, columns.index('New template'),
self.dummy_new_item) 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): def vm_state_change(self, is_running, row=None):
self.new_item.setEnabled(not is_running)
self.state_item.set_state(is_running) self.state_item.set_state(is_running)
items = [self.name_item, self.state_item, self.current_item, if not row:
self.dummy_new_item] 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: # hiding cellWidgets does not work in a qTableWidget
if is_running: if not is_running:
item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable) self.new_item = NewTemplateItem(self.vm, self.templates,
else: self.table_widget)
item.setFlags(item.flags() | QtCore.Qt.ItemIsSelectable) 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: # Bases on the original code by:
# Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com> # Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>

View File

@ -14,6 +14,12 @@
<string>Template Manager</string> <string>Template Manager</string>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
@ -26,7 +32,7 @@
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum> <enum>QAbstractItemView::NoSelection</enum>
</property> </property>
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectRows</enum>
@ -47,18 +53,44 @@
<number>4</number> <number>4</number>
</attribute> </attribute>
<attribute name="horizontalHeaderStretchLastSection"> <attribute name="horizontalHeaderStretchLastSection">
<bool>false</bool> <bool>true</bool>
</attribute> </attribute>
<attribute name="verticalHeaderVisible"> <attribute name="verticalHeaderVisible">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Change all selected to:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="change_all_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Select multiple qubes to change template in all of them at once. <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;To select all qubes with a given template, double-click the template name in any row.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;IMPORTANT&lt;/span&gt;: Changes will be applied only when you click OK.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
To select all qubes with a given template, double-click the template name in any row.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -66,17 +98,26 @@ To select all qubes with a given template, double-click the template name in any
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="standardButtons"> <item>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set> <widget class="QPushButton" name="clear_selection_button">
</property> <property name="text">
</widget> <string>Clear Selection</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
<zorder>pushButton_2</zorder>
<zorder>verticalLayoutWidget</zorder>
</widget> </widget>
</widget> </widget>
<resources/> <resources/>