Improved template manager UI
changed it to be more intuitive and less annoying.
This commit is contained in:
parent
21224a6cbd
commit
c0eb8b55ab
@ -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>
|
||||||
|
@ -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><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></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/>
|
||||||
|
Loading…
Reference in New Issue
Block a user