Browse Source

Improved template manager UI

changed it to be more intuitive and less annoying.
Marta Marczykowska-Górecka 5 years ago
parent
commit
c0eb8b55ab
2 changed files with 148 additions and 51 deletions
  1. 96 40
      qubesmanager/template_manager.py
  2. 52 11
      ui/templatemanager.ui

+ 96 - 40
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: <br> {}.").format("<br> ".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]
-
-        for item in items:
-            if is_running:
-                item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable)
-            else:
-                item.setFlags(item.flags() | QtCore.Qt.ItemIsSelectable)
+        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
+
+        # 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 <p.varet@gmail.com>

+ 52 - 11
ui/templatemanager.ui

@@ -14,6 +14,12 @@
    <string>Template Manager</string>
   </property>
   <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">
     <item row="0" column="0">
      <layout class="QVBoxLayout" name="verticalLayout">
@@ -26,7 +32,7 @@
          <set>QAbstractItemView::NoEditTriggers</set>
         </property>
         <property name="selectionMode">
-         <enum>QAbstractItemView::MultiSelection</enum>
+         <enum>QAbstractItemView::NoSelection</enum>
         </property>
         <property name="selectionBehavior">
          <enum>QAbstractItemView::SelectRows</enum>
@@ -47,18 +53,44 @@
          <number>4</number>
         </attribute>
         <attribute name="horizontalHeaderStretchLastSection">
-         <bool>false</bool>
+         <bool>true</bool>
         </attribute>
         <attribute name="verticalHeaderVisible">
          <bool>false</bool>
         </attribute>
        </widget>
       </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>
        <widget class="QLabel" name="label">
         <property name="text">
-         <string>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.</string>
+         <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>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
@@ -66,17 +98,26 @@ To select all qubes with a given template, double-click the template name in any
        </widget>
       </item>
       <item>
-       <widget class="QDialogButtonBox" name="buttonBox">
-        <property name="standardButtons">
-         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
-        </property>
-       </widget>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QPushButton" name="clear_selection_button">
+          <property name="text">
+           <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>
      </layout>
     </item>
    </layout>
-   <zorder>pushButton_2</zorder>
-   <zorder>verticalLayoutWidget</zorder>
   </widget>
  </widget>
  <resources/>