Эх сурвалжийг харах

Merge branch 'master' of https://github.com/QubesOS/qubes-manager into template_menu

# Conflicts:
#	qubesmanager/qube_manager.py
donoban 3 жил өмнө
parent
commit
f90ab86a73

+ 14 - 0
.gitlab-ci.yml

@@ -0,0 +1,14 @@
+include:
+  - project: 'QubesOS/qubes-continuous-integration'
+    file: '/r4.1/gitlab-base.yml'
+  - project: 'QubesOS/qubes-continuous-integration'
+    file: '/r4.1/gitlab-dom0.yml'
+  - project: 'QubesOS/qubes-continuous-integration'
+    file: '/r4.1/gitlab-vm.yml'
+
+checks:pylint:
+  stage: checks
+  before_script:
+    - pip3 install --quiet -r ci/requirements.txt
+  script:
+    - PYTHONPATH="test-packages:$PYTHONPATH" python3 -m pylint qubesmanager

+ 0 - 0
ci/pylintrc → .pylintrc


+ 1 - 1
debian/control

@@ -7,7 +7,7 @@ Build-Depends:
   dh-python,
   python3-all,
   python3-setuptools,
-  qt5-default,
+  qtbase5-dev,
   qttools5-dev-tools,
   pyqt5-dev-tools
 Standards-Version: 4.3.0

+ 1 - 0
debian/rules

@@ -2,6 +2,7 @@
 
 include /usr/share/dpkg/default.mk
 export DESTDIR=$(shell pwd)/debian/tmp
+export QT_SELECT=qt5
 
 %:
 	dh $@ --with python3 --buildsystem=pybuild

+ 113 - 32
qubesmanager/qube_manager.py

@@ -37,7 +37,7 @@ from PyQt5.QtCore import (Qt, QAbstractTableModel, QObject, pyqtSlot, QEvent,
 # pylint: disable=import-error
 from PyQt5.QtWidgets import (QLineEdit, QStyledItemDelegate, QToolTip,
     QMenu, QInputDialog, QMainWindow, QProgressDialog, QStyleOptionViewItem,
-    QAbstractItemView, QMessageBox, QAction)
+    QMessageBox)
 
 # pylint: disable=import-error
 from PyQt5.QtGui import (QIcon, QPixmap, QRegExpValidator, QFont, QColor)
@@ -630,8 +630,11 @@ class RunCommandThread(common_threads.QubesThread):
         except (ChildProcessError, exc.QubesException) as ex:
             self.msg = (self.tr("Error while running command!"), str(ex))
 
-
 class QubesProxyModel(QSortFilterProxyModel):
+    def __init__(self, window):
+        super().__init__()
+        self.window = window
+
     def lessThan(self, left, right):
         if left.data(self.sortRole()) != right.data(self.sortRole()):
             return super().lessThan(left, right)
@@ -641,6 +644,31 @@ class QubesProxyModel(QSortFilterProxyModel):
 
         return left_vm.name.lower() < right_vm.name.lower()
 
+    # pylint: disable=too-many-return-statements
+    def filterAcceptsRow(self, sourceRow, sourceParent):
+        if self.window.show_all.isChecked():
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+
+        index = self.sourceModel().index(sourceRow, 0, sourceParent)
+        vm = self.sourceModel().data(index, Qt.UserRole)
+
+        if self.window.show_running.isChecked() and \
+                vm.state['power'] == 'Running':
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+        if self.window.show_halted.isChecked() and \
+                vm.state['power'] == 'Halted':
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+        if self.window.show_network.isChecked() and \
+                getattr(vm.vm, 'provides_network', False):
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+        if self.window.show_templates.isChecked() and vm.klass == 'TemplateVM':
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+        if self.window.show_standalone.isChecked() \
+                and vm.klass == 'StandaloneVM':
+            return super().filterAcceptsRow(sourceRow, sourceParent)
+
+        return False
+
 
 class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
     # suppress saving settings while initializing widgets
@@ -659,7 +687,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
         self.searchbox.setValidator(QRegExpValidator(
             QRegExp("[a-zA-Z0-9_-]*", Qt.CaseInsensitive), None))
         self.searchbox.textChanged.connect(self.do_search)
-        self.searchContainer.addWidget(self.searchbox)
+        self.searchContainer.insertWidget(1, self.searchbox)
 
         self.settings_windows = {}
 
@@ -668,33 +696,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
 
         self.init_template_menu()
         self.init_network_menu()
-        self.context_menu = QMenu(self)
-
-        self.context_menu.addAction(self.action_settings)
-        self.context_menu.addMenu(self.template_menu)
-        self.context_menu.addMenu(self.network_menu)
-        self.context_menu.addAction(self.action_editfwrules)
-        self.context_menu.addAction(self.action_appmenus)
-        self.context_menu.addAction(self.action_set_keyboard_layout)
-        self.context_menu.addSeparator()
-
-        self.context_menu.addAction(self.action_updatevm)
-        self.context_menu.addAction(self.action_run_command_in_vm)
-        self.context_menu.addAction(self.action_open_console)
-        self.context_menu.addAction(self.action_resumevm)
-        self.context_menu.addAction(self.action_startvm_tools_install)
-        self.context_menu.addAction(self.action_pausevm)
-        self.context_menu.addAction(self.action_shutdownvm)
-        self.context_menu.addAction(self.action_restartvm)
-        self.context_menu.addAction(self.action_killvm)
-        self.context_menu.addSeparator()
-
-        self.context_menu.addAction(self.action_clonevm)
-        self.context_menu.addAction(self.action_removevm)
-        self.context_menu.addSeparator()
-
-        self.context_menu.addMenu(self.logs_menu)
-        self.context_menu.addSeparator()
+        self.__init_context_menu()
 
         self.tools_context_menu = QMenu(self)
         self.tools_context_menu.addAction(self.action_toolbar)
@@ -706,6 +708,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
                 lambda pos: self.open_tools_context_menu(self.toolbar, pos))
         self.action_menubar.toggled.connect(self.showhide_menubar)
         self.action_toolbar.toggled.connect(self.showhide_toolbar)
+        self.action_compact_view.toggled.connect(self.set_compactview)
         self.logs_menu.triggered.connect(self.show_log)
 
         self.table.resizeColumnsToContents()
@@ -717,7 +720,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
         self.fill_cache()
         self.qubes_model = QubesTableModel(self.qubes_cache)
 
-        self.proxy = QubesProxyModel()
+        self.proxy = QubesProxyModel(self)
         self.proxy.setSourceModel(self.qubes_model)
         self.proxy.setSortRole(Qt.UserRole + 1)
         self.proxy.setSortCaseSensitivity(Qt.CaseInsensitive)
@@ -727,10 +730,16 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
         self.proxy.layoutChanged.connect(self.update_template_menu)
         self.proxy.layoutChanged.connect(self.update_network_menu)
 
+        self.show_running.stateChanged.connect(self.invalidate)
+        self.show_halted.stateChanged.connect(self.invalidate)
+        self.show_network.stateChanged.connect(self.invalidate)
+        self.show_templates.stateChanged.connect(self.invalidate)
+        self.show_standalone.stateChanged.connect(self.invalidate)
+        self.show_all.stateChanged.connect(self.invalidate)
+
         self.table.setModel(self.proxy)
         self.table.setItemDelegateForColumn(3, StateIconDelegate())
         self.table.resizeColumnsToContents()
-        self.table.setSelectionMode(QAbstractItemView.ExtendedSelection)
         selection_model = self.table.selectionModel()
         selection_model.selectionChanged.connect(self.table_selection_changed)
 
@@ -747,6 +756,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
         self.menu_view.addSeparator()
         self.menu_view.addAction(self.action_toolbar)
         self.menu_view.addAction(self.action_menubar)
+        self.menu_view.addSeparator()
+        self.menu_view.addAction(self.action_compact_view)
 
         try:
             self.load_manager_settings()
@@ -850,12 +861,54 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
                 self.tr("Change Network Error"),
                 self.tr((str(ex))))
 
+    def __init_context_menu(self):
+        self.context_menu = QMenu(self)
+        self.context_menu.addAction(self.action_settings)
+        self.context_menu.addAction(self.template_menu.menuAction())
+        self.context_menu.addAction(self.network_menu.menuAction())
+        self.context_menu.addAction(self.action_editfwrules)
+        self.context_menu.addAction(self.action_appmenus)
+        self.context_menu.addAction(self.action_set_keyboard_layout)
+        self.context_menu.addSeparator()
+        self.context_menu.addAction(self.action_updatevm)
+        self.context_menu.addAction(self.action_run_command_in_vm)
+        self.context_menu.addAction(self.action_open_console)
+        self.context_menu.addAction(self.action_resumevm)
+        self.context_menu.addAction(self.action_startvm_tools_install)
+        self.context_menu.addAction(self.action_pausevm)
+        self.context_menu.addAction(self.action_shutdownvm)
+        self.context_menu.addAction(self.action_restartvm)
+        self.context_menu.addAction(self.action_killvm)
+        self.context_menu.addSeparator()
+        self.context_menu.addAction(self.action_clonevm)
+        self.context_menu.addAction(self.action_removevm)
+        self.context_menu.addSeparator()
+        self.context_menu.addMenu(self.logs_menu)
+        self.context_menu.addSeparator()
+
+    def save_showing(self):
+        self.manager_settings.setValue('show/running',
+                self.show_running.isChecked())
+        self.manager_settings.setValue('show/halted',
+                self.show_halted.isChecked())
+        self.manager_settings.setValue('show/network',
+                self.show_network.isChecked())
+        self.manager_settings.setValue('show/templates',
+                self.show_templates.isChecked())
+        self.manager_settings.setValue('show/standalone',
+                self.show_standalone.isChecked())
+        self.manager_settings.setValue('show/all', self.show_all.isChecked())
+
     def save_sorting(self):
         self.manager_settings.setValue('view/sort_column',
                 self.proxy.sortColumn())
         self.manager_settings.setValue('view/sort_order',
                 self.proxy.sortOrder())
 
+    def invalidate(self):
+        self.proxy.invalidate()
+        self.table.resizeColumnsToContents()
+
     def fill_cache(self):
         progress = QProgressDialog(
             self.tr(
@@ -1024,6 +1077,23 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
         if not self.manager_settings.value("view/toolbar_visible",
                                            defaultValue=True):
             self.action_toolbar.setChecked(False)
+        if self.manager_settings.value("view/compactview",
+                                           defaultValue="false") != "false":
+            self.action_compact_view.setChecked(True)
+
+        # Restore show checkboxes
+        self.show_running.setChecked(self.manager_settings.value(
+            'show/running', "true") == "true")
+        self.show_halted.setChecked(self.manager_settings.value(
+            'show/halted', "true") == "true")
+        self.show_network.setChecked(self.manager_settings.value(
+            'show/network', "true") == "true")
+        self.show_templates.setChecked(self.manager_settings.value(
+            'show/templates', "true") == "true")
+        self.show_standalone.setChecked(self.manager_settings.value(
+            'show/standalone', "true") == "true")
+        self.show_all.setChecked(self.manager_settings.value(
+            'show/all', "true") == "true")
 
         # load last window size
         self.resize(self.manager_settings.value("window_size",
@@ -1389,6 +1459,9 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
                     "\nError: {}".format(str(ex))))
             return
 
+    def closeEvent(self, _):
+        self.save_showing()
+
     # noinspection PyArgumentList
     @pyqtSlot(name='on_action_settings_triggered')
     def action_settings_triggered(self):
@@ -1515,6 +1588,14 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
     def action_exit_triggered(self):
         self.close()
 
+    def set_compactview(self, checked):
+        if checked:
+            self.toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)
+        else:
+            self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
+        if self.settings_loaded:
+            self.manager_settings.setValue('view/compactview', checked)
+
     def showhide_menubar(self, checked):
         self.menubar.setVisible(checked)
         if not checked:

+ 1 - 1
qubesmanager/utils.py

@@ -459,7 +459,7 @@ def format_dependencies_list(dependencies):
 
 
 def loop_shutdown():
-    pending = asyncio.Task.all_tasks()
+    pending = asyncio.all_tasks()
     for task in pending:
         with suppress(asyncio.CancelledError):
             task.cancel()

+ 106 - 18
ui/qubemanager.ui

@@ -52,23 +52,6 @@
     <property name="sizeConstraint">
      <enum>QLayout::SetDefaultConstraint</enum>
     </property>
-    <item row="0" column="0">
-     <layout class="QHBoxLayout" name="searchContainer">
-      <property name="spacing">
-       <number>6</number>
-      </property>
-      <property name="leftMargin">
-       <number>6</number>
-      </property>
-      <item>
-       <widget class="QLabel" name="label">
-        <property name="text">
-         <string>Search:</string>
-        </property>
-       </widget>
-      </item>
-     </layout>
-    </item>
     <item row="1" column="0">
      <widget class="QTableView" name="table">
       <property name="minimumSize">
@@ -108,7 +91,7 @@
        <bool>true</bool>
       </property>
       <property name="selectionMode">
-       <enum>QAbstractItemView::SingleSelection</enum>
+       <enum>QAbstractItemView::ExtendedSelection</enum>
       </property>
       <property name="selectionBehavior">
        <enum>QAbstractItemView::SelectRows</enum>
@@ -236,6 +219,103 @@ Template</string>
       </column>
      </widget>
     </item>
+    <item row="0" column="0">
+     <layout class="QHBoxLayout" name="searchContainer">
+      <property name="spacing">
+       <number>6</number>
+      </property>
+      <property name="leftMargin">
+       <number>6</number>
+      </property>
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Search:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="show_label">
+        <property name="text">
+         <string>Show:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_running">
+        <property name="text">
+         <string>Running</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_halted">
+        <property name="text">
+         <string>Halted</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_network">
+        <property name="text">
+         <string>Network</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_templates">
+        <property name="text">
+         <string>Templates</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_standalone">
+        <property name="text">
+         <string>Standalone</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="show_all">
+        <property name="text">
+         <string>All</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </item>
    </layout>
   </widget>
   <widget class="QMenuBar" name="menubar">
@@ -889,6 +969,14 @@ Template</string>
     <string>Open a secure Xen console in the qube. Useful chiefly for debugging purposes: for normal operation, use &quot;Run Terminal&quot; from the Domains widget. </string>
    </property>
   </action>
+  <action name="action_compact_view">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Compact View</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="../resources.qrc"/>