mirror of
https://github.com/SELinuxProject/setools
synced 2025-03-25 04:26:28 +00:00
apol: Implement core save/load tabs and workspace functions.
Still needs funtions on each individual tab for saving/loading settings. Related to #97 and #98.
This commit is contained in:
parent
6eaf7a26f5
commit
7b2c99cbfe
69
data/apol.ui
69
data/apol.ui
@ -96,7 +96,21 @@
|
||||
<addaction name="edit_permmap_action"/>
|
||||
<addaction name="save_permmap_action"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuWorkspace">
|
||||
<property name="title">
|
||||
<string>Workspace</string>
|
||||
</property>
|
||||
<addaction name="new_analysis"/>
|
||||
<addaction name="new_from_settings_action"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="load_settings_action"/>
|
||||
<addaction name="save_settings_action"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="load_workspace_action"/>
|
||||
<addaction name="save_workspace_action"/>
|
||||
</widget>
|
||||
<addaction name="menu_File"/>
|
||||
<addaction name="menuWorkspace"/>
|
||||
<addaction name="menu_Edit"/>
|
||||
<addaction name="menuPerm_Map"/>
|
||||
<addaction name="menu_Help"/>
|
||||
@ -230,6 +244,61 @@
|
||||
<string>Apol Help</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="save_settings_action">
|
||||
<property name="text">
|
||||
<string>Save Tab Settings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save the current tab's settings to file.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="load_settings_action">
|
||||
<property name="text">
|
||||
<string>Load Tab Settings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load settings for the current tab.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="load_workspace_action">
|
||||
<property name="text">
|
||||
<string>Load Workspace</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load workspace from file.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+L</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="save_workspace_action">
|
||||
<property name="text">
|
||||
<string>Save Workspace</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Save workspace to file.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+S</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="new_from_settings_action">
|
||||
<property name="text">
|
||||
<string>New Analysis From Settings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start a new analysis using settings from a file.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+N</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
@ -49,6 +49,33 @@ from .typequery import TypeQueryTab
|
||||
from .userquery import UserQueryTab
|
||||
|
||||
|
||||
# TODO: is there a better way than hardcoding this while still being safe?
|
||||
tab_map = {"BoolQueryTab": BoolQueryTab,
|
||||
"BoundsQueryTab": BoundsQueryTab,
|
||||
"CategoryQueryTab": CategoryQueryTab,
|
||||
"CommonQueryTab": CommonQueryTab,
|
||||
"ConstraintQueryTab": ConstraintQueryTab,
|
||||
"DefaultQueryTab": DefaultQueryTab,
|
||||
"DomainTransitionAnalysisTab": DomainTransitionAnalysisTab,
|
||||
"FSUseQueryTab": FSUseQueryTab,
|
||||
"GenfsconQueryTab": GenfsconQueryTab,
|
||||
"InfoFlowAnalysisTab": InfoFlowAnalysisTab,
|
||||
"InitialSIDQueryTab": InitialSIDQueryTab,
|
||||
"MLSRuleQueryTab": MLSRuleQueryTab,
|
||||
"NetifconQueryTab": NetifconQueryTab,
|
||||
"NodeconQueryTab": NodeconQueryTab,
|
||||
"ObjClassQueryTab": ObjClassQueryTab,
|
||||
"PortconQueryTab": PortconQueryTab,
|
||||
"RBACRuleQueryTab": RBACRuleQueryTab,
|
||||
"RoleQueryTab": RoleQueryTab,
|
||||
"SensitivityQueryTab": SensitivityQueryTab,
|
||||
"SummaryTab": SummaryTab,
|
||||
"TERuleQueryTab": TERuleQueryTab,
|
||||
"TypeAttributeQueryTab": TypeAttributeQueryTab,
|
||||
"TypeQueryTab": TypeQueryTab,
|
||||
"UserQueryTab": UserQueryTab}
|
||||
|
||||
|
||||
class ChooseAnalysis(SEToolsWidget, QDialog):
|
||||
|
||||
"""
|
||||
|
@ -20,6 +20,7 @@ import os
|
||||
import sys
|
||||
import stat
|
||||
import logging
|
||||
import json
|
||||
from errno import ENOENT
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, Qt, QProcess
|
||||
@ -28,7 +29,7 @@ from setools import __version__, PermissionMap, SELinuxPolicy
|
||||
|
||||
from ..widget import SEToolsWidget
|
||||
from ..logtosignal import LogHandlerToSignal
|
||||
from .chooseanalysis import ChooseAnalysis
|
||||
from .chooseanalysis import ChooseAnalysis, tab_map
|
||||
from .permmapedit import PermissionMapEditor
|
||||
from .summary import SummaryTab
|
||||
|
||||
@ -51,6 +52,7 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
self.create_new_analysis("Summary", SummaryTab)
|
||||
|
||||
self.update_window_title()
|
||||
self.toggle_workspace_actions()
|
||||
|
||||
def setupUi(self):
|
||||
self.load_ui("apol.ui")
|
||||
@ -91,11 +93,17 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
self.close_policy_action.triggered.connect(self.close_policy)
|
||||
self.open_permmap.triggered.connect(self.select_permmap)
|
||||
self.new_analysis.triggered.connect(self.choose_analysis)
|
||||
self.AnalysisTabs.currentChanged.connect(self.toggle_workspace_actions)
|
||||
self.AnalysisTabs.tabCloseRequested.connect(self.close_tab)
|
||||
self.AnalysisTabs.tabBarDoubleClicked.connect(self.tab_name_editor)
|
||||
self.tab_editor.editingFinished.connect(self.rename_tab)
|
||||
self.rename_tab_action.triggered.connect(self.rename_active_tab)
|
||||
self.close_tab_action.triggered.connect(self.close_active_tab)
|
||||
self.new_from_settings_action.triggered.connect(self.new_analysis_from_config)
|
||||
self.load_settings_action.triggered.connect(self.load_settings)
|
||||
self.save_settings_action.triggered.connect(self.save_settings)
|
||||
self.load_workspace_action.triggered.connect(self.load_workspace)
|
||||
self.save_workspace_action.triggered.connect(self.save_workspace)
|
||||
self.copy_action.triggered.connect(self.copy)
|
||||
self.cut_action.triggered.connect(self.cut)
|
||||
self.paste_action.triggered.connect(self.paste)
|
||||
@ -147,6 +155,7 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
self.error_msg.critical(self, "Policy loading error", str(ex))
|
||||
else:
|
||||
self.update_window_title()
|
||||
self.toggle_workspace_actions()
|
||||
|
||||
if self._permmap:
|
||||
self._permmap.map_policy(self._policy)
|
||||
@ -165,6 +174,7 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
self.AnalysisTabs.clear()
|
||||
self._policy = None
|
||||
self.update_window_title()
|
||||
self.toggle_workspace_actions()
|
||||
|
||||
#
|
||||
# Permission map handling
|
||||
@ -244,6 +254,7 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
index = self.AnalysisTabs.addTab(newanalysis, counted_name)
|
||||
self.AnalysisTabs.setTabToolTip(index, tabtitle)
|
||||
self.AnalysisTabs.setCurrentIndex(index)
|
||||
return index
|
||||
|
||||
def tab_name_editor(self, index):
|
||||
if index >= 0:
|
||||
@ -275,9 +286,300 @@ class ApolMainWindow(SEToolsWidget, QMainWindow):
|
||||
def rename_tab(self):
|
||||
# this should never be negative since the editor is modal
|
||||
index = self.AnalysisTabs.currentIndex()
|
||||
tab = self.AnalysisTabs.widget(index)
|
||||
title = self.tab_editor.text()
|
||||
|
||||
self.tab_editor.hide()
|
||||
self.AnalysisTabs.setTabText(index, self.tab_editor.text())
|
||||
|
||||
self.AnalysisTabs.setTabText(index, title)
|
||||
tab.setObjectName(title)
|
||||
|
||||
#
|
||||
# Workspace actions
|
||||
#
|
||||
def toggle_workspace_actions(self, index=-1):
|
||||
"""
|
||||
Enable or disable workspace actions depending on
|
||||
how many tabs are open and if a policy is open.
|
||||
|
||||
This is a slot for the QTabWidget.currentChanged()
|
||||
signal, though index is ignored.
|
||||
"""
|
||||
open_tabs = self.AnalysisTabs.count() > 0
|
||||
open_policy = self._policy is not None
|
||||
|
||||
self.log.debug("{0} actions requiring an open policy.".
|
||||
format("Enabling" if open_policy else "Disabling"))
|
||||
self.log.debug("{0} actions requiring open tabs.".
|
||||
format("Enabling" if open_tabs else "Disabling"))
|
||||
self.save_settings_action.setEnabled(open_tabs)
|
||||
self.save_workspace_action.setEnabled(open_tabs)
|
||||
self.new_analysis.setEnabled(open_policy)
|
||||
self.new_from_settings_action.setEnabled(open_policy)
|
||||
self.load_settings_action.setEnabled(open_tabs)
|
||||
|
||||
def _get_settings(self, index=None):
|
||||
"""Return a dictionary with the settings of the tab at the specified index."""
|
||||
if index is None:
|
||||
index = self.AnalysisTabs.currentIndex()
|
||||
|
||||
assert index >= 0, "Tab index is negative in _get_settings. This is an SETools bug."
|
||||
tab = self.AnalysisTabs.widget(index)
|
||||
|
||||
settings = tab.save()
|
||||
|
||||
# add the tab info to the settings.
|
||||
settings["__title__"] = self.AnalysisTabs.tabText(index)
|
||||
settings["__tab__"] = type(tab).__name__
|
||||
|
||||
return settings
|
||||
|
||||
def _put_settings(self, settings, index=None):
|
||||
"""Load the settings into the specified tab."""
|
||||
|
||||
if index is None:
|
||||
index = self.AnalysisTabs.currentIndex()
|
||||
|
||||
assert index >= 0, "Tab index is negative in _put_settings. This is an SETools bug."
|
||||
tab = self.AnalysisTabs.widget(index)
|
||||
|
||||
if settings["__tab__"] != type(tab).__name__:
|
||||
raise TypeError("The current tab ({0}) does not match the tab in the settings file "
|
||||
"({1}).".format(type(tab).__name__, settings["__tab__"]))
|
||||
|
||||
try:
|
||||
self.AnalysisTabs.setTabText(index, settings["__title__"])
|
||||
except KeyError:
|
||||
self.log.warning("Settings file does not have a title setting.")
|
||||
|
||||
tab.load(settings)
|
||||
|
||||
def load_settings(self, new=False):
|
||||
filename = QFileDialog.getOpenFileName(self, "Open settings file", ".",
|
||||
"Apol Tab Settings File (*.apolt);;"
|
||||
"All Files (*)")[0]
|
||||
if not filename:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(filename, "r") as fd:
|
||||
settings = json.load(fd)
|
||||
except ValueError as ex:
|
||||
self.log.critical("Invalid settings file \"{0}\"".format(filename))
|
||||
self.error_msg.critical(self, "Failed to load settings",
|
||||
"Invalid settings file: \"{0}\"".format(filename))
|
||||
return
|
||||
except (IOError, OSError) as ex:
|
||||
self.log.critical("Unable to load settings file \"{0.filename}\": {0.strerror}".
|
||||
format(ex))
|
||||
self.error_msg.critical(self, "Failed to load settings",
|
||||
"Failed to load \"{0.filename}\": {0.strerror}".format(ex))
|
||||
return
|
||||
except Exception as ex:
|
||||
self.log.critical("Unable to load settings file \"{0}\": {1}".format(filename, ex))
|
||||
self.error_msg.critical(self, "Failed to load settings", str(ex))
|
||||
return
|
||||
|
||||
self.log.info("Loading analysis settings from \"{0}\"".format(filename))
|
||||
|
||||
if new:
|
||||
try:
|
||||
tabclass = tab_map[settings["__tab__"]]
|
||||
except KeyError:
|
||||
self.log.critical("Missing analysis type in \"{0}\"".format(filename))
|
||||
self.error_msg.critical(self, "Failed to load settings",
|
||||
"The type of analysis is missing in the settings file.")
|
||||
return
|
||||
|
||||
# The tab title will be set by _put_settings.
|
||||
index = self.create_new_analysis("Tab", tabclass)
|
||||
else:
|
||||
index = None
|
||||
|
||||
try:
|
||||
self._put_settings(settings, index)
|
||||
except Exception as ex:
|
||||
self.log.critical("Error loading settings file \"{0}\": {1}".format(filename, ex))
|
||||
self.error_msg.critical(self, "Failed to load settings",
|
||||
"Error loading settings file \"{0}\": {1}".format(filename, ex))
|
||||
else:
|
||||
self.log.info("Successfully loaded analysis settings from \"{0}\"".format(filename))
|
||||
|
||||
def new_analysis_from_config(self):
|
||||
self.load_settings(new=True)
|
||||
|
||||
def save_settings(self):
|
||||
filename = QFileDialog.getSaveFileName(self, "Save analysis tab settings", "analysis.apolt",
|
||||
"Apol Tab Settings File (*.apolt);;"
|
||||
"All Files (*)")[0]
|
||||
|
||||
if not filename:
|
||||
return
|
||||
|
||||
settings = self._get_settings()
|
||||
|
||||
try:
|
||||
with open(filename, "w") as fd:
|
||||
json.dump(settings, fd, indent=1)
|
||||
except (IOError, OSError) as ex:
|
||||
self.log.critical("Unable to save settings file \"{0.filename}\": {0.strerror}".
|
||||
format(ex))
|
||||
self.error_msg.critical(self, "Failed to save settings",
|
||||
"Failed to save \"{0.filename}\": {0.strerror}".format(ex))
|
||||
except Exception as ex:
|
||||
self.log.critical("Unable to save settings file \"{0}\": {1}".format(filename, ex))
|
||||
self.error_msg.critical(self, "Failed to save settings", str(ex))
|
||||
else:
|
||||
self.log.info("Successfully saved settings file \"{0}\"".format(filename))
|
||||
|
||||
def load_workspace(self):
|
||||
# 1. if number of tabs > 0, check if we really want to do this
|
||||
if self.AnalysisTabs.count() > 0:
|
||||
reply = QMessageBox.question(
|
||||
self, "Continue?",
|
||||
"Loading a workspace will close all existing analyses. Continue?",
|
||||
QMessageBox.Yes | QMessageBox.No)
|
||||
|
||||
if reply == QMessageBox.No:
|
||||
return
|
||||
|
||||
# 2. try to load the workspace file, if we fail, bail
|
||||
filename = QFileDialog.getOpenFileName(self, "Open workspace file", ".",
|
||||
"Apol Workspace Files (*.apolw);;"
|
||||
"All Files (*)")[0]
|
||||
|
||||
if not filename:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(filename, "r") as fd:
|
||||
workspace = json.load(fd)
|
||||
except ValueError as ex:
|
||||
self.log.critical("Invalid workspace file \"{0}\"".format(filename))
|
||||
self.error_msg.critical(self, "Failed to load workspace",
|
||||
"Invalid workspace file: \"{0}\"".format(filename))
|
||||
return
|
||||
except (IOError, OSError) as ex:
|
||||
self.log.critical("Unable to load workspace file \"{0.filename}\": {0.strerror}".
|
||||
format(ex))
|
||||
self.error_msg.critical(self, "Failed to load workspace",
|
||||
"Failed to load \"{0.filename}\": {0.strerror}".format(ex))
|
||||
return
|
||||
except Exception as ex:
|
||||
self.log.critical("Unable to load workspace file \"{0}\": {1}".format(filename, ex))
|
||||
self.error_msg.critical(self, "Failed to load workspace", str(ex))
|
||||
return
|
||||
|
||||
# 3. close all tabs. Explicitly do this to avoid the question
|
||||
# about closing the policy with tabs open.
|
||||
self.AnalysisTabs.clear()
|
||||
|
||||
# 4. close policy
|
||||
self.close_policy()
|
||||
|
||||
# 5. try to open the specified policy, if we fail, bail. Note:
|
||||
# handling exceptions from the policy load is done inside
|
||||
# the load_policy function, so only the KeyError needs to be caught here
|
||||
try:
|
||||
self.load_policy(workspace["__policy__"])
|
||||
except KeyError:
|
||||
self.log.critical("Missing policy in workspace file \"{0}\"".format(filename))
|
||||
self.error_msg.critical(self, "Missing policy in workspace file \"{0}\"".
|
||||
format(filename))
|
||||
|
||||
if self._policy is None:
|
||||
self.log.critical("The policy could not be loaded in workspace file \"{0}\"".
|
||||
format(filename))
|
||||
self.error_msg.critical(self, "The policy could not be loaded in workspace file \"{0}\""
|
||||
". Aborting workspace load.".format(filename))
|
||||
return
|
||||
|
||||
# 6. try to open the specified perm map, if we fail,
|
||||
# tell the user we will continue with the default map; load the default map
|
||||
# Note: handling exceptions from the map load is done inside
|
||||
# the load_permmap function, so only the KeyError needs to be caught here
|
||||
try:
|
||||
self.load_permmap(workspace["__permmap__"])
|
||||
except KeyError:
|
||||
self.log.warning("Missing permission map in workspace file \"{0}\"".format(filename))
|
||||
self.error_msg.warning(self, "Missing permission map setting.",
|
||||
"Missing permission map in workspace file \"{0}\"".
|
||||
format(filename))
|
||||
|
||||
if self._permmap is None:
|
||||
self.error_msg.information(self, "Loading default permission map.",
|
||||
"The default permisison map will be loaded.")
|
||||
self.load_permmap()
|
||||
|
||||
# 7. try to open all tabs and apply settings. Record any errors
|
||||
try:
|
||||
tab_list = list(workspace["__tabs__"])
|
||||
except KeyError:
|
||||
self.log.critical("Missing tab list in workspace file \"{0}\"".format(filename))
|
||||
self.error_msg.critical(self, "Failed to load workspace",
|
||||
"The workspace file is missing the tab list. Aborting.")
|
||||
return
|
||||
except TypeError:
|
||||
self.log.critical("Invalid tab list in workspace file.")
|
||||
self.error_msg.critical(self, "Failed to load workspace",
|
||||
"The tab count is invalid. Aborting.")
|
||||
return
|
||||
|
||||
loading_errors = []
|
||||
for i, settings in enumerate(tab_list):
|
||||
try:
|
||||
tabclass = tab_map[settings["__tab__"]]
|
||||
except KeyError:
|
||||
error_str = "Missing analysis type for tab {0}. Skipping this tab.".format(i)
|
||||
self.log.error(error_str)
|
||||
loading_errors.append(error_str)
|
||||
continue
|
||||
|
||||
# The tab title will be set by _put_settings.
|
||||
index = self.create_new_analysis("Tab", tabclass)
|
||||
|
||||
try:
|
||||
self._put_settings(settings, index)
|
||||
except Exception as ex:
|
||||
error_str = "Error loading settings for tab {0}: {1}".format(i, ex)
|
||||
self.log.error(error_str)
|
||||
loading_errors.append(error_str)
|
||||
|
||||
self.log.info("Completed loading workspace from \"{0}\"".format(filename))
|
||||
|
||||
# 8. if there are any errors, open a dialog with the
|
||||
# complete list of tab errors
|
||||
if loading_errors:
|
||||
self.error_msg.warning(self, "Errors while loading workspace:",
|
||||
"There were errors while loading the workspace:\n\n{0}".
|
||||
format("\n\n".join(loading_errors)))
|
||||
|
||||
def save_workspace(self):
|
||||
filename = QFileDialog.getSaveFileName(self, "Save analysis workspace", "workspace.apolw",
|
||||
"Apol Workspace Files (*.apolw);;"
|
||||
"All Files (*)")[0]
|
||||
|
||||
if not filename:
|
||||
return
|
||||
|
||||
workspace = {}
|
||||
workspace["__policy__"] = os.path.abspath(str(self._policy))
|
||||
workspace["__permmap__"] = os.path.abspath(str(self._permmap))
|
||||
workspace["__tabs__"] = []
|
||||
|
||||
for index in range(self.AnalysisTabs.count()):
|
||||
tab = self.AnalysisTabs.widget(index)
|
||||
|
||||
settings = tab.save()
|
||||
|
||||
# add the tab info to the settings.
|
||||
settings["__title__"] = self.AnalysisTabs.tabText(index)
|
||||
settings["__tab__"] = type(tab).__name__
|
||||
|
||||
workspace["__tabs__"].append(settings)
|
||||
|
||||
with open(filename, "w") as fd:
|
||||
json.dump(workspace, fd, indent=1)
|
||||
|
||||
#
|
||||
# Edit actions
|
||||
|
196
setoolsgui/apol/workspace.py
Normal file
196
setoolsgui/apol/workspace.py
Normal file
@ -0,0 +1,196 @@
|
||||
# Copyright 2016, Tresys Technology, LLC
|
||||
#
|
||||
# This file is part of SETools.
|
||||
#
|
||||
# SETools is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation, either version 2.1 of
|
||||
# the License, or (at your option) any later version.
|
||||
#
|
||||
# SETools is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
import logging
|
||||
|
||||
import setools
|
||||
from setools.policyrep.symbol import PolicySymbol
|
||||
|
||||
from PyQt5.QtCore import Qt, QItemSelectionModel
|
||||
|
||||
|
||||
def save_checkboxes(tab, settings, checkboxes):
|
||||
"""
|
||||
Save settings from the checkable buttons (e.g. QCheckbox) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings. This will be mutated.
|
||||
checkboxes A list of attribute names (str) of buttons in the tab.
|
||||
"""
|
||||
|
||||
for entry in checkboxes:
|
||||
checkbox = getattr(tab, entry)
|
||||
settings[entry] = checkbox.isChecked()
|
||||
|
||||
|
||||
def load_checkboxes(tab, settings, checkboxes):
|
||||
"""
|
||||
Load settings into the checkable buttons (e.g. QCheckbox) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings.
|
||||
checkboxes A list of attribute names (str) of buttons in the tab.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# next set options
|
||||
for entry in checkboxes:
|
||||
checkbox = getattr(tab, entry)
|
||||
|
||||
try:
|
||||
checkbox.setChecked(bool(settings[entry]))
|
||||
except KeyError:
|
||||
log.warning("{0} option missing from settings file.".format(entry))
|
||||
|
||||
|
||||
def save_lineedits(tab, settings, lines):
|
||||
"""
|
||||
Save settings into the QLineEdit(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings. This will be mutated.
|
||||
lines A list of attribute names (str) of QLineEdits in the tab.
|
||||
"""
|
||||
|
||||
# set line edits
|
||||
for entry in lines:
|
||||
lineedit = getattr(tab, entry)
|
||||
settings[entry] = lineedit.text()
|
||||
|
||||
|
||||
def load_lineedits(tab, settings, lines):
|
||||
"""
|
||||
Load settings into the QLineEdit(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings.
|
||||
lines A list of attribute names (str) of QLineEdits in the tab.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# set line edits
|
||||
for entry in lines:
|
||||
lineedit = getattr(tab, entry)
|
||||
|
||||
try:
|
||||
lineedit.setText(settings[entry])
|
||||
except KeyError:
|
||||
log.warning("{0} criteria missing from settings file.".format(entry))
|
||||
|
||||
|
||||
def save_textedits(tab, settings, edits):
|
||||
"""
|
||||
Save settings into the QTextEdit(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings. This will be mutated.
|
||||
edits A list of attribute names (str) of QTextEdits in the tab.
|
||||
"""
|
||||
|
||||
# set line edits
|
||||
for entry in edits:
|
||||
textedit = getattr(tab, entry)
|
||||
settings[entry] = textedit.toPlainText()
|
||||
|
||||
|
||||
def load_textedits(tab, settings, edits):
|
||||
"""
|
||||
Load settings into the QTextEdit(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings.
|
||||
edits A list of attribute names (str) of QTextEdits in the tab.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# set line edits
|
||||
for entry in edits:
|
||||
textedit = getattr(tab, entry)
|
||||
|
||||
try:
|
||||
textedit.setPlainText(settings[entry])
|
||||
except KeyError:
|
||||
log.warning("{0} criteria missing from settings file.".format(entry))
|
||||
|
||||
|
||||
def save_listviews(tab, settings, listviews):
|
||||
"""
|
||||
Save settings from the QListView selection(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings. This will be mutated.
|
||||
listviews A list of attribute names (str) of QListViews in the tab.
|
||||
"""
|
||||
|
||||
for entry in listviews:
|
||||
listview = getattr(tab, entry)
|
||||
datamodel = listview.model()
|
||||
|
||||
selections = []
|
||||
for index in listview.selectedIndexes():
|
||||
item = datamodel.data(index, Qt.DisplayRole)
|
||||
selections.append(item)
|
||||
|
||||
settings[entry] = selections
|
||||
|
||||
|
||||
def load_listviews(tab, settings, listviews):
|
||||
"""
|
||||
Load settings into the QListView selection(s) in the tab.
|
||||
|
||||
Parameters:
|
||||
tab The tab object.
|
||||
settings The dictionary of settings.
|
||||
listviews A list of attribute names (str) of QListViews in the tab.
|
||||
"""
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# set list selections
|
||||
for entry in listviews:
|
||||
try:
|
||||
selections = settings[entry]
|
||||
except KeyError:
|
||||
log.warning("{0} criteria missing from settings file.".format(entry))
|
||||
continue
|
||||
|
||||
if not selections:
|
||||
continue
|
||||
|
||||
listview = getattr(tab, entry)
|
||||
selectionmodel = listview.selectionModel()
|
||||
selectionmodel.clear()
|
||||
datamodel = listview.selectionModel().model()
|
||||
|
||||
for row in range(datamodel.rowCount()):
|
||||
index = datamodel.createIndex(row, 0)
|
||||
item = datamodel.data(index, Qt.DisplayRole)
|
||||
|
||||
if item in selections:
|
||||
selectionmodel.select(index, QItemSelectionModel.Select)
|
Loading…
Reference in New Issue
Block a user