diff --git a/qubesmanager/qube_manager.py b/qubesmanager/qube_manager.py index 2515529..35be98d 100644 --- a/qubesmanager/qube_manager.py +++ b/qubesmanager/qube_manager.py @@ -35,7 +35,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) + QMessageBox) # pylint: disable=import-error from PyQt5.QtGui import (QIcon, QPixmap, QRegExpValidator, QFont, QColor) @@ -628,8 +628,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) @@ -639,6 +642,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 @@ -657,38 +685,14 @@ 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 = {} self.frame_width = 0 self.frame_height = 0 - self.context_menu = QMenu(self) - - self.context_menu.addAction(self.action_settings) - 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.addAction(self.action_show_logs) - self.context_menu.addSeparator() + self.__init_context_menu() self.tools_context_menu = QMenu(self) self.tools_context_menu.addAction(self.action_toolbar) @@ -701,6 +705,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow): self.action_menubar.toggled.connect(self.showhide_menubar) self.action_toolbar.toggled.connect(self.showhide_toolbar) self.action_show_logs.triggered.connect(self.show_log) + self.action_compact_view.toggled.connect(self.set_compactview) self.table.resizeColumnsToContents() @@ -711,7 +716,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) @@ -719,10 +724,16 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow): self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxy.layoutChanged.connect(self.save_sorting) + 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) @@ -740,6 +751,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() @@ -792,12 +805,51 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow): self.check_updates() + def __init_context_menu(self): + self.context_menu = QMenu(self) + self.context_menu.addAction(self.action_settings) + 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.addAction(self.action_show_logs) + + 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( @@ -945,6 +997,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", @@ -1260,6 +1329,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): @@ -1386,6 +1458,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: diff --git a/ui/qubemanager.ui b/ui/qubemanager.ui index 24175db..e5f5043 100644 --- a/ui/qubemanager.ui +++ b/ui/qubemanager.ui @@ -52,23 +52,6 @@ QLayout::SetDefaultConstraint - - - - 6 - - - 6 - - - - - Search: - - - - - @@ -108,7 +91,7 @@ true - QAbstractItemView::SingleSelection + QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows @@ -236,6 +219,103 @@ Template + + + + 6 + + + 6 + + + + + Search: + + + + + + + Show: + + + + + + + Running + + + true + + + + + + + Halted + + + true + + + + + + + Network + + + true + + + + + + + Templates + + + true + + + + + + + Standalone + + + true + + + + + + + All + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -878,6 +958,14 @@ Template Logs + + + true + + + Compact View + +