Source code for spinetoolbox.widgets.data_store_widget
######################################################################################################################
# Copyright (C) 2017-2020 Spine project consortium
# This file is part of Spine Toolbox.
# Spine Toolbox 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 3 of the License, or (at your option)
# any later version. This program 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
# this program. If not, see <http://www.gnu.org/licenses/>.
######################################################################################################################
"""
Contains the DataStoreForm class.
:author: M. Marin (KTH)
:date: 26.11.2018
"""
import os
import time # just to measure loading time and sqlalchemy ORM performance
from PySide2.QtWidgets import (
QMainWindow,
QErrorMessage,
QDockWidget,
QMessageBox,
QDialog,
QFileDialog,
QInputDialog,
QTreeView,
QTableView,
)
from PySide2.QtCore import Qt, Signal, Slot, QSettings
from PySide2.QtGui import QFont, QFontMetrics, QGuiApplication, QIcon
from spinedb_api import copy_database
from ..config import MAINWINDOW_SS
from .edit_db_items_dialogs import ManageParameterTagsDialog
from .custom_menus import ParameterValueListContextMenu
from .parameter_view_mixin import ParameterViewMixin
from .tree_view_mixin import TreeViewMixin
from .graph_view_mixin import GraphViewMixin
from .tabular_view_mixin import TabularViewMixin
from .toolbars import ParameterTagToolBar
from .db_session_history_dialog import DBSessionHistoryDialog
from .notification import NotificationStack
from ..mvcmodels.parameter_value_list_model import ParameterValueListModel
from ..helpers import busy_effect
from .import_widget import ImportDialog
from ..spine_io.exporters.excel import export_spine_database_to_xlsx
from ..logger_interface import LoggerInterface
[docs]class DataStoreFormBase(QMainWindow):
"""Base class for DataStoreForm"""
def __init__(self, db_mngr, *db_urls):
"""Initializes form.
Args:
db_mngr (SpineDBManager): The manager to use
*db_urls (tuple): Database url, codename.
"""
super().__init__(flags=Qt.Window)
from ..ui.data_store_view import Ui_MainWindow
self.db_urls = list(db_urls)
self.db_url = self.db_urls[0]
self.db_mngr = db_mngr
self.db_maps = [
self.db_mngr.get_db_map_for_listener(self, url, codename=codename) for url, codename in self.db_urls
]
self.db_map = self.db_maps[0]
self.db_mngr.set_logger_for_db_map(self, self.db_map)
# Setup UI from Qt Designer file
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.takeCentralWidget()
self.setWindowIcon(QIcon(":/symbols/app.ico"))
self.setStyleSheet(MAINWINDOW_SS)
self.setAttribute(Qt.WA_DeleteOnClose)
self.qsettings = QSettings("SpineProject", "Spine Toolbox")
self.err_msg = QErrorMessage(self)
self.err_msg.setWindowTitle("Error")
self.err_msg.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
self.notification_stack = NotificationStack(self)
self.parameter_tag_toolbar = ParameterTagToolBar(self, self.db_mngr, *self.db_maps)
self.addToolBar(Qt.TopToolBarArea, self.parameter_tag_toolbar)
self.selected_ent_cls_ids = {"object class": {}, "relationship class": {}}
self.selected_ent_ids = {"object": {}, "relationship": {}}
self.selected_parameter_tag_ids = dict()
self.selected_param_def_ids = {"object class": {}, "relationship class": {}}
self.parameter_value_list_model = ParameterValueListModel(self, self.db_mngr, *self.db_maps)
fm = QFontMetrics(QFont("", 0))
self.default_row_height = 1.2 * fm.lineSpacing()
max_screen_height = max([s.availableSize().height() for s in QGuiApplication.screens()])
self.visible_rows = int(max_screen_height / self.default_row_height)
self._selection_source = None
self._selection_locked = False
self._focusable_childs = [self.ui.treeView_parameter_value_list]
self.settings_group = 'treeViewWidget'
self.undo_action = None
self.redo_action = None
db_names = ", ".join(["{0}".format(db_map.codename) for db_map in self.db_maps])
self.setWindowTitle("{0}[*] - Data store view".format(db_names))
self.update_commit_enabled()
[docs] def connect_signals(self):
"""Connects signals to slots."""
# Message signals
self.msg.connect(self.add_message)
self.msg_error.connect(self.err_msg.showMessage)
self.error_box.connect(lambda title, msg: self.err_msg.showMessage(msg))
# Menu actions
self.ui.actionCommit.triggered.connect(self.commit_session)
self.ui.actionRollback.triggered.connect(self.rollback_session)
self.ui.actionRefresh.triggered.connect(self.refresh_session)
self.ui.actionView_history.triggered.connect(self.show_history_dialog)
self.ui.actionClose.triggered.connect(self.close)
self.ui.menuEdit.aboutToShow.connect(self._handle_menu_edit_about_to_show)
self.ui.actionImport.triggered.connect(self.show_import_file_dialog)
self.ui.actionExport.triggered.connect(self.export_database)
self.ui.actionCopy.triggered.connect(self.copy)
self.ui.actionPaste.triggered.connect(self.paste)
self.ui.actionRemove_selection.triggered.connect(self.remove_selection)
self.ui.actionManage_parameter_tags.triggered.connect(self.show_manage_parameter_tags_form)
self.parameter_tag_toolbar.manage_tags_action_triggered.connect(self.show_manage_parameter_tags_form)
self.parameter_tag_toolbar.tag_button_toggled.connect(self._handle_tag_button_toggled)
self.ui.treeView_parameter_value_list.selectionModel().selectionChanged.connect(
self._handle_parameter_value_list_selection_changed
)
self.ui.treeView_parameter_value_list.customContextMenuRequested.connect(
self.show_parameter_value_list_context_menu
)
self.parameter_value_list_model.remove_selection_requested.connect(self.remove_parameter_value_lists)
@Slot(int)
[docs] def update_undo_redo_actions(self, index):
undo_ages = {db_map: self.db_mngr.undo_stack[db_map].undo_age for db_map in self.db_maps}
redo_ages = {db_map: self.db_mngr.undo_stack[db_map].redo_age for db_map in self.db_maps}
undo_ages = {db_map: age for db_map, age in undo_ages.items() if age is not None}
redo_ages = {db_map: age for db_map, age in redo_ages.items() if age is not None}
new_undo_action = self.db_mngr.undo_action[max(undo_ages, key=undo_ages.get, default=self.db_map)]
new_redo_action = self.db_mngr.redo_action[min(redo_ages, key=redo_ages.get, default=self.db_map)]
if new_undo_action != self.undo_action:
self.ui.menuEdit.insertAction(self.undo_action, new_undo_action)
self.ui.menuEdit.removeAction(self.undo_action)
self.undo_action = new_undo_action
if new_redo_action != self.redo_action:
self.ui.menuEdit.insertAction(self.redo_action, new_redo_action)
self.ui.menuEdit.removeAction(self.redo_action)
self.redo_action = new_redo_action
@Slot(bool)
[docs] def update_commit_enabled(self, _clean=False):
dirty = not all(self.db_mngr.undo_stack[db_map].isClean() for db_map in self.db_maps)
self.ui.actionCommit.setEnabled(dirty)
self.ui.actionRollback.setEnabled(dirty)
self.ui.actionView_history.setEnabled(dirty)
self.setWindowModified(dirty)
@Slot(bool)
[docs] def show_history_dialog(self, checked=False):
dialog = DBSessionHistoryDialog(self, self.db_mngr, *self.db_maps)
dialog.show()
[docs] def init_models(self):
"""Initializes models."""
self.parameter_value_list_model.build_tree()
for item in self.parameter_value_list_model.visit_all():
index = self.parameter_value_list_model.index_from_item(item)
self.ui.treeView_parameter_value_list.expand(index)
self.ui.treeView_parameter_value_list.resizeColumnToContents(0)
self.ui.treeView_parameter_value_list.header().hide()
self.parameter_tag_toolbar.init_toolbar()
@Slot(str)
[docs] def add_message(self, msg):
"""Appends regular message to status bar.
Args:
msg (str): String to show in QStatusBar
"""
self.notification_stack.push(msg)
[docs] def restore_dock_widgets(self):
"""Docks all floating and or hidden QDockWidgets back to the window."""
for dock in self.findChildren(QDockWidget):
dock.setVisible(True)
dock.setFloating(False)
self.addDockWidget(Qt.RightDockWidgetArea, dock)
self.parameter_tag_toolbar.setVisible(True)
self.addToolBar(Qt.TopToolBarArea, self.parameter_tag_toolbar)
@Slot()
[docs] def _find_focus_child(self):
for child in self._focusable_childs:
if child.hasFocus():
return child
[docs] def selected_entity_class_ids(self, entity_class_type):
"""Returns object class ids selected in object tree *and* parameter tag toolbar."""
if self.selected_param_def_ids[entity_class_type] is None:
return None
tree_class_ids = self.selected_ent_cls_ids[entity_class_type]
tag_class_ids = dict()
for db_map, class_id in self.selected_param_def_ids[entity_class_type]:
tag_class_ids.setdefault(db_map, set()).add(class_id)
result = dict()
for db_map in tree_class_ids.keys() | tag_class_ids.keys():
tree_cls_ids = tree_class_ids.get(db_map, set())
tag_cls_ids = tag_class_ids.get(db_map, set())
if tree_cls_ids == set():
result[db_map] = tag_cls_ids
elif tag_cls_ids == set():
result[db_map] = tree_cls_ids
else:
result[db_map] = tree_cls_ids & tag_cls_ids
return result
[docs] def _accept_selection(self, widget):
"""Clears selection from all widgets except the given one, so there's only one selection
in the form at a time. In addition, registers the given widget as the official source
for all operations involving selections (copy, remove, edit), but only in case it *has* a selection."""
if not self._selection_locked:
self._selection_source = widget if widget.selectionModel().hasSelection() else None
self._selection_locked = True
for w in self.findChildren(QTreeView) + self.findChildren(QTableView):
if w != widget:
w.selectionModel().clearSelection()
self._selection_locked = False
return True
return False
@Slot(bool)
[docs] def remove_selection(self, checked=False):
"""Removes selection of items."""
if not self._selection_source:
return
self._selection_source.model().remove_selection_requested.emit()
@Slot(bool)
[docs] def copy(self, checked=False):
"""Copies data to clipboard."""
if not self._selection_source:
return
self._selection_source.copy()
@Slot(bool)
[docs] def paste(self, checked=False):
"""Pastes data from clipboard."""
focus_widget = self._find_focus_child()
if not focus_widget:
return
focus_widget.paste()
@Slot(bool)
[docs] def show_import_file_dialog(self, checked=False):
"""Shows dialog to allow user to select a file to import."""
db_map = next(iter(self.db_maps))
if db_map.has_pending_changes():
commit_warning = QMessageBox(parent=self)
commit_warning.setText("Please commit or rollback before importing data")
commit_warning.setStandardButtons(QMessageBox.Ok)
commit_warning.exec()
return
dialog = ImportDialog(self.qsettings, parent=self)
# assume that dialog is modal, if not use accepted, rejected signals
if dialog.exec() == QDialog.Accepted:
if db_map.has_pending_changes():
self.msg.emit("Import successful")
self.init_models()
dialog.close()
dialog.deleteLater()
@Slot(bool)
[docs] def export_database(self, checked=False):
"""Exports data from database into a file."""
# noinspection PyCallByClass, PyTypeChecker, PyArgumentList
db_map = self._select_database()
if db_map is None: # Database selection cancelled
return
proj_dir = self.db_mngr.parent().project_dir # Parent should be SpineToolboxProject
file_path, selected_filter = QFileDialog.getSaveFileName(
self, "Export to file", proj_dir, "Excel file (*.xlsx);;SQlite database (*.sqlite *.db)"
)
if not file_path: # File selection cancelled
return
if selected_filter.startswith("SQlite"):
self.export_to_sqlite(db_map, file_path)
elif selected_filter.startswith("Excel"):
self.export_to_excel(db_map, file_path)
[docs] def _select_database(self):
"""
Lets user select a database from available databases.
Shows a dialog from which user can select a single database.
If there is only a single database it is selected automatically and no dialog is shown.
Returns:
the database map of the database or None if no database was selected
"""
if len(self.db_maps) == 1:
return next(iter(self.db_maps))
db_names = [x.codename for x in self.db_maps]
selected_database, ok = QInputDialog.getItem(
self, "Select database", "Select database to export", db_names, editable=False
)
if not ok:
return None
return self.db_maps[db_names.index(selected_database)]
@busy_effect
[docs] def export_to_excel(self, db_map, file_path):
"""Exports data from database into Excel file."""
filename = os.path.split(file_path)[1]
try:
export_spine_database_to_xlsx(db_map, file_path)
self.msg.emit("Excel file successfully exported.")
except PermissionError:
self.msg_error.emit(
"Unable to export to file <b>{0}</b>.<br/>" "Close the file in Excel and try again.".format(filename)
)
except OSError:
self.msg_error.emit("[OSError] Unable to export to file <b>{0}</b>".format(filename))
@busy_effect
[docs] def export_to_sqlite(self, db_map, file_path):
"""Exports data from database into SQlite file."""
dst_url = 'sqlite:///{0}'.format(file_path)
copy_database(dst_url, db_map, overwrite=True)
self.msg.emit("SQlite file successfully exported.")
@Slot(bool)
@Slot(bool)
[docs] def commit_session(self, checked=False):
"""Commits session."""
self.db_mngr.commit_session(*self.db_maps)
@Slot(bool)
[docs] def receive_session_committed(self, db_maps):
db_maps = set(self.db_maps) & set(db_maps)
if not db_maps:
return
db_names = ", ".join([x.codename for x in db_maps])
msg = f"All changes in {db_names} committed successfully."
self.msg.emit(msg)
[docs] def receive_session_rolled_back(self, db_maps):
db_maps = set(self.db_maps) & set(db_maps)
if not db_maps:
return
self.init_models()
db_names = ", ".join([x.codename for x in db_maps])
msg = f"All changes in {db_names} rolled back successfully."
self.msg.emit(msg)
@Slot(bool)
[docs] def receive_session_refreshed(self, db_maps):
db_maps = set(self.db_maps) & set(db_maps)
if not db_maps:
return
self.init_models()
self.msg.emit("Session refreshed.")
@Slot("QVariant", bool)
[docs] def _handle_tag_button_toggled(self, db_map_ids, checked):
"""Updates filter according to selected tags.
"""
for db_map, id_ in db_map_ids:
if checked:
self.selected_parameter_tag_ids.setdefault(db_map, set()).add(id_)
else:
self.selected_parameter_tag_ids[db_map].remove(id_)
selected_param_defs = self.db_mngr.find_cascading_parameter_definitions_by_tag(self.selected_parameter_tag_ids)
if any(v for v in self.selected_parameter_tag_ids.values()) and not any(
v for v in selected_param_defs.values()
):
# There are tags selected but no matching parameter definitions ~> we need to reject them all
self.selected_param_def_ids["object class"] = None
self.selected_param_def_ids["relationship class"] = None
else:
self.selected_param_def_ids["object class"] = {}
self.selected_param_def_ids["relationship class"] = {}
for db_map, param_defs in selected_param_defs.items():
for param_def in param_defs:
if "object_class_id" in param_def:
self.selected_param_def_ids["object class"].setdefault(
(db_map, param_def["object_class_id"]), set()
).add(param_def["id"])
elif "relationship_class_id" in param_def:
self.selected_param_def_ids["relationship class"].setdefault(
(db_map, param_def["relationship_class_id"]), set()
).add(param_def["id"])
self.update_filter()
@Slot(bool)
[docs] def show_manage_parameter_tags_form(self, checked=False):
dialog = ManageParameterTagsDialog(self, self.db_mngr, *self.db_maps)
dialog.show()
@Slot("QItemSelection", "QItemSelection")
[docs] def _handle_parameter_value_list_selection_changed(self, selected, deselected):
"""Accepts selection."""
self._accept_selection(self.ui.treeView_parameter_value_list)
@Slot("QPoint")
@Slot()
[docs] def remove_parameter_value_lists(self):
"""Removes selection of parameter value-lists.
"""
db_map_typed_data_to_rm = {}
db_map_data_to_upd = {}
selected = [
self.parameter_value_list_model.item_from_index(index)
for index in self.ui.treeView_parameter_value_list.selectionModel().selectedIndexes()
]
for db_item in self.parameter_value_list_model._invisible_root_item.children:
db_map_typed_data_to_rm[db_item.db_map] = {"parameter value list": []}
db_map_data_to_upd[db_item.db_map] = []
for list_item in reversed(db_item.children[:-1]):
if list_item.id:
if list_item in selected:
db_map_typed_data_to_rm[db_item.db_map]["parameter value list"].append(
{"id": list_item.id, "name": list_item.name}
)
continue
curr_value_list = list_item.compile_value_list()
value_list = [
value
for value_item, value in zip(list_item.children, curr_value_list)
if value_item not in selected
]
if not value_list:
db_map_typed_data_to_rm[db_item.db_map]["parameter value list"].append(
{"id": list_item.id, "name": list_item.name}
)
continue
if value_list != curr_value_list:
item = {"id": list_item.id, "value_list": value_list}
db_map_data_to_upd[db_item.db_map].append(item)
else:
# WIP lists, just remove everything selected
if list_item in selected:
db_item.remove_children(list_item.child_number(), list_item.child_number())
continue
for value_item in reversed(list_item.children[:-1]):
if value_item in selected:
list_item.remove_children(value_item.child_number(), value_item.child_number())
self.db_mngr.update_parameter_value_lists(db_map_data_to_upd)
self.db_mngr.remove_items(db_map_typed_data_to_rm)
self.ui.treeView_parameter_value_list.selectionModel().clearSelection()
[docs] def notify_items_changed(self, action, item_type, db_map_data):
"""Enables or disables actions and informs the user about what just happened."""
count = sum(len(data) for data in db_map_data.values())
msg = f"Successfully {action} {count} {item_type} item(s)"
self.msg.emit(msg)
[docs] def receive_object_classes_added(self, db_map_data):
self.notify_items_changed("added", "object class", db_map_data)
[docs] def receive_objects_added(self, db_map_data):
self.notify_items_changed("added", "object", db_map_data)
[docs] def receive_relationship_classes_added(self, db_map_data):
self.notify_items_changed("added", "relationship class", db_map_data)
[docs] def receive_relationships_added(self, db_map_data):
self.notify_items_changed("added", "relationship", db_map_data)
[docs] def receive_parameter_definitions_added(self, db_map_data):
self.notify_items_changed("added", "parameter definition", db_map_data)
[docs] def receive_parameter_values_added(self, db_map_data):
self.notify_items_changed("added", "parameter value", db_map_data)
[docs] def receive_parameter_value_lists_added(self, db_map_data):
self.notify_items_changed("added", "parameter value list", db_map_data)
self.parameter_value_list_model.receive_parameter_value_lists_added(db_map_data)
[docs] def receive_parameter_tags_added(self, db_map_data):
self.notify_items_changed("added", "parameter tag", db_map_data)
self.parameter_tag_toolbar.receive_parameter_tags_added(db_map_data)
[docs] def receive_object_classes_updated(self, db_map_data):
self.notify_items_changed("updated", "object class", db_map_data)
[docs] def receive_objects_updated(self, db_map_data):
self.notify_items_changed("updated", "object", db_map_data)
[docs] def receive_relationship_classes_updated(self, db_map_data):
self.notify_items_changed("updated", "relationship class", db_map_data)
[docs] def receive_relationships_updated(self, db_map_data):
self.notify_items_changed("updated", "relationship", db_map_data)
[docs] def receive_parameter_definitions_updated(self, db_map_data):
self.notify_items_changed("updated", "parameter definition", db_map_data)
[docs] def receive_parameter_values_updated(self, db_map_data):
self.notify_items_changed("updated", "parameter value", db_map_data)
[docs] def receive_parameter_value_lists_updated(self, db_map_data):
self.notify_items_changed("updated", "parameter value list", db_map_data)
self.parameter_value_list_model.receive_parameter_value_lists_updated(db_map_data)
[docs] def receive_parameter_tags_updated(self, db_map_data):
self.notify_items_changed("updated", "parameter tag", db_map_data)
self.parameter_tag_toolbar.receive_parameter_tags_updated(db_map_data)
[docs] def receive_parameter_definition_tags_set(self, db_map_data):
self.notify_items_changed("set", "parameter definition tag", db_map_data)
[docs] def receive_object_classes_removed(self, db_map_data):
self.notify_items_changed("removed", "object class", db_map_data)
[docs] def receive_objects_removed(self, db_map_data):
self.notify_items_changed("removed", "object", db_map_data)
[docs] def receive_relationship_classes_removed(self, db_map_data):
self.notify_items_changed("removed", "relationship class", db_map_data)
[docs] def receive_relationships_removed(self, db_map_data):
self.notify_items_changed("removed", "relationship", db_map_data)
[docs] def receive_parameter_definitions_removed(self, db_map_data):
self.notify_items_changed("removed", "parameter definition", db_map_data)
[docs] def receive_parameter_values_removed(self, db_map_data):
self.notify_items_changed("removed", "parameter value", db_map_data)
[docs] def receive_parameter_value_lists_removed(self, db_map_data):
self.notify_items_changed("removed", "parameter value list", db_map_data)
self.parameter_value_list_model.receive_parameter_value_lists_removed(db_map_data)
[docs] def receive_parameter_tags_removed(self, db_map_data):
self.notify_items_changed("removed", "parameter tag", db_map_data)
self.parameter_tag_toolbar.receive_parameter_tags_removed(db_map_data)
[docs] def restore_ui(self):
"""Restore UI state from previous session."""
self.qsettings.beginGroup(self.settings_group)
window_size = self.qsettings.value("windowSize")
window_pos = self.qsettings.value("windowPosition")
window_state = self.qsettings.value("windowState")
window_maximized = self.qsettings.value("windowMaximized", defaultValue='false')
n_screens = self.qsettings.value("n_screens", defaultValue=1)
self.qsettings.endGroup()
if window_size:
self.resize(window_size)
if window_pos:
self.move(window_pos)
if window_state:
self.restoreState(window_state, version=1) # Toolbar and dockWidget positions
if window_maximized == 'true':
self.setWindowState(Qt.WindowMaximized)
# noinspection PyArgumentList
if len(QGuiApplication.screens()) < int(n_screens):
# There are less screens available now than on previous application startup
self.move(0, 0) # Move this widget to primary screen position (0,0)
[docs] def save_window_state(self):
"""Save window state parameters (size, position, state) via QSettings."""
self.qsettings.beginGroup(self.settings_group)
self.qsettings.setValue("windowSize", self.size())
self.qsettings.setValue("windowPosition", self.pos())
self.qsettings.setValue("windowState", self.saveState(version=1))
self.qsettings.setValue("windowMaximized", self.windowState() == Qt.WindowMaximized)
self.qsettings.endGroup()
[docs] def closeEvent(self, event):
"""Handle close window.
Args:
event (QCloseEvent): Closing event
"""
for db_map in self.db_maps:
if not self.db_mngr.remove_db_map_listener(db_map, self):
event.ignore()
return
self.db_mngr.unset_logger_for_db_map(db_map)
# Save UI form state
self.save_window_state()
event.accept()
[docs]class DataStoreForm(TabularViewMixin, GraphViewMixin, ParameterViewMixin, TreeViewMixin, DataStoreFormBase):
"""A widget to visualize Spine dbs."""
def __init__(self, db_mngr, *db_urls):
"""Initializes everything.
Args:
db_mngr (SpineDBManager): The manager to use
*db_urls (tuple): Database url, codename.
"""
tic = time.process_time()
super().__init__(db_mngr, *db_urls)
self._size = None
self.init_models()
self.add_menu_actions()
self.connect_signals()
self.apply_tree_style()
self.restore_ui()
toc = time.process_time()
self.msg.emit("Data store view created in {0:.2f} seconds".format(toc - tic))
[docs] def connect_signals(self):
super().connect_signals()
self.ui.actionTree_style.triggered.connect(self.apply_tree_style)
self.ui.actionGraph_style.triggered.connect(self.apply_graph_style)
self.ui.actionTabular_style.triggered.connect(self.apply_tabular_style)
[docs] def tabify_and_raise(self, docks):
"""
Tabifies docks in given list, then raises the first.
Args:
docks (list)
"""
for first, second in zip(docks[:-1], docks[1:]):
self.tabifyDockWidget(first, second)
docks[0].raise_()
[docs] def begin_style_change(self):
"""Begins a style change operation."""
self._size = self.size()
self.restore_dock_widgets()
[docs] def end_style_change(self):
"""Ends a style change operation."""
qApp.processEvents() # pylint: disable=undefined-variable
self.resize(self._size)
@Slot(bool)
[docs] def apply_tree_style(self, checked=False):
"""Applies the tree style, inspired in the former tree view."""
self.begin_style_change()
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_object_parameter_value, Qt.Horizontal)
self.splitDockWidget(
self.ui.dockWidget_object_parameter_value, self.ui.dockWidget_parameter_value_list, Qt.Horizontal
)
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_relationship_tree, Qt.Vertical)
self.splitDockWidget(
self.ui.dockWidget_object_parameter_value, self.ui.dockWidget_relationship_parameter_value, Qt.Vertical
)
self.tabify_and_raise(
[self.ui.dockWidget_object_parameter_value, self.ui.dockWidget_object_parameter_definition]
)
self.tabify_and_raise(
[self.ui.dockWidget_relationship_parameter_value, self.ui.dockWidget_relationship_parameter_definition]
)
self.ui.dockWidget_entity_graph.hide()
self.ui.dockWidget_item_palette.hide()
self.ui.dockWidget_pivot_table.hide()
self.ui.dockWidget_frozen_table.hide()
docks = [
self.ui.dockWidget_object_tree,
self.ui.dockWidget_object_parameter_value,
self.ui.dockWidget_parameter_value_list,
]
width = sum(d.size().width() for d in docks)
self.resizeDocks(docks, [0.3 * width, 0.5 * width, 0.2 * width], Qt.Horizontal)
self.end_style_change()
@Slot(bool)
[docs] def apply_tabular_style(self, checked=False):
"""Applies the tree style, inspired in the former tabular view."""
self.begin_style_change()
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_pivot_table, Qt.Horizontal)
self.splitDockWidget(self.ui.dockWidget_pivot_table, self.ui.dockWidget_frozen_table, Qt.Horizontal)
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_relationship_tree, Qt.Vertical)
self.ui.dockWidget_entity_graph.hide()
self.ui.dockWidget_item_palette.hide()
self.ui.dockWidget_object_parameter_value.hide()
self.ui.dockWidget_object_parameter_definition.hide()
self.ui.dockWidget_relationship_parameter_value.hide()
self.ui.dockWidget_relationship_parameter_definition.hide()
self.ui.dockWidget_parameter_value_list.hide()
self.parameter_tag_toolbar.hide()
docks = [self.ui.dockWidget_object_tree, self.ui.dockWidget_pivot_table, self.ui.dockWidget_frozen_table]
width = sum(d.size().width() for d in docks)
self.resizeDocks(docks, [0.3 * width, 0.5 * width, 0.2 * width], Qt.Horizontal)
self.end_style_change()
@Slot(bool)
[docs] def apply_graph_style(self, checked=False):
"""Applies the tree style, inspired in the former graph view."""
self.begin_style_change()
self.ui.dockWidget_relationship_tree.hide()
self.ui.dockWidget_parameter_value_list.hide()
self.ui.dockWidget_pivot_table.hide()
self.ui.dockWidget_frozen_table.hide()
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_entity_graph, Qt.Horizontal)
self.splitDockWidget(self.ui.dockWidget_entity_graph, self.ui.dockWidget_object_parameter_value, Qt.Vertical)
self.splitDockWidget(self.ui.dockWidget_object_tree, self.ui.dockWidget_item_palette, Qt.Vertical)
self.tabify_and_raise(
[
self.ui.dockWidget_object_parameter_value,
self.ui.dockWidget_object_parameter_definition,
self.ui.dockWidget_relationship_parameter_value,
self.ui.dockWidget_relationship_parameter_definition,
]
)
docks = [self.ui.dockWidget_object_tree, self.ui.dockWidget_entity_graph]
width = sum(d.size().width() for d in docks)
self.resizeDocks(docks, [0.3 * width, 0.7 * width], Qt.Horizontal)
docks = [self.ui.dockWidget_entity_graph, self.ui.dockWidget_object_parameter_value]
height = sum(d.size().height() for d in docks)
self.resizeDocks(docks, [0.7 * height, 0.3 * height], Qt.Vertical)
self.end_style_change()
self.ui.graphicsView.reset_zoom()