From 80a0360d1f4c267b9a996c82ca1dcce94e5f08f6 Mon Sep 17 00:00:00 2001 From: Antti Soininen Date: Tue, 16 Apr 2024 13:52:14 +0300 Subject: [PATCH] Add message to commit viewer that explains why items may be missing The message hopefully helps users to understand why the viewer sometimes shows no affected items. Re #2602 --- .../spine_db_editor/ui/db_commit_viewer.py | 99 ++++++++++++++ .../spine_db_editor/ui/db_commit_viewer.ui | 121 ++++++++++++++++++ .../spine_db_editor/widgets/commit_viewer.py | 61 +++++---- .../widgets/custom_qtreeview.py | 6 +- spinetoolbox/spine_db_manager.py | 2 +- .../widgets/test_commit_viewer.py | 6 +- 6 files changed, 256 insertions(+), 39 deletions(-) create mode 100644 spinetoolbox/spine_db_editor/ui/db_commit_viewer.py create mode 100644 spinetoolbox/spine_db_editor/ui/db_commit_viewer.ui diff --git a/spinetoolbox/spine_db_editor/ui/db_commit_viewer.py b/spinetoolbox/spine_db_editor/ui/db_commit_viewer.py new file mode 100644 index 000000000..b9d750e60 --- /dev/null +++ b/spinetoolbox/spine_db_editor/ui/db_commit_viewer.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +###################################################################################################################### +# Copyright (C) 2017-2022 Spine project consortium +# Copyright Spine Toolbox contributors +# 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 . +###################################################################################################################### + +################################################################################ +## Form generated from reading UI file 'db_commit_viewer.ui' +## +## Created by: Qt User Interface Compiler version 6.5.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QHBoxLayout, QHeaderView, QSizePolicy, + QSplitter, QStackedWidget, QTextBrowser, QTreeWidget, + QTreeWidgetItem, QWidget) + +class Ui_DBCommitViewer(object): + def setupUi(self, DBCommitViewer): + if not DBCommitViewer.objectName(): + DBCommitViewer.setObjectName(u"DBCommitViewer") + DBCommitViewer.resize(716, 218) + self.horizontalLayout_2 = QHBoxLayout(DBCommitViewer) + self.horizontalLayout_2.setSpacing(0) + self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.splitter = QSplitter(DBCommitViewer) + self.splitter.setObjectName(u"splitter") + self.splitter.setOrientation(Qt.Horizontal) + self.commit_list = QTreeWidget(self.splitter) + self.commit_list.setObjectName(u"commit_list") + self.splitter.addWidget(self.commit_list) + self.affected_items_widget_stack = QStackedWidget(self.splitter) + self.affected_items_widget_stack.setObjectName(u"affected_items_widget_stack") + self.page = QWidget() + self.page.setObjectName(u"page") + self.horizontalLayout_3 = QHBoxLayout(self.page) + self.horizontalLayout_3.setSpacing(0) + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.affected_items = QTreeWidget(self.page) + self.affected_items.setObjectName(u"affected_items") + + self.horizontalLayout_3.addWidget(self.affected_items) + + self.affected_items_widget_stack.addWidget(self.page) + self.page_2 = QWidget() + self.page_2.setObjectName(u"page_2") + self.horizontalLayout = QHBoxLayout(self.page_2) + self.horizontalLayout.setSpacing(0) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.no_affected_items_notice = QTextBrowser(self.page_2) + self.no_affected_items_notice.setObjectName(u"no_affected_items_notice") + self.no_affected_items_notice.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.no_affected_items_notice.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.no_affected_items_notice.setOpenLinks(False) + + self.horizontalLayout.addWidget(self.no_affected_items_notice) + + self.affected_items_widget_stack.addWidget(self.page_2) + self.splitter.addWidget(self.affected_items_widget_stack) + + self.horizontalLayout_2.addWidget(self.splitter) + + + self.retranslateUi(DBCommitViewer) + + QMetaObject.connectSlotsByName(DBCommitViewer) + # setupUi + + def retranslateUi(self, DBCommitViewer): + self.no_affected_items_notice.setHtml(QCoreApplication.translate("DBCommitViewer", u"\n" +"\n" +"

No affected items found for selected commit.

\n" +"

Note that we cannot show items that have been removed by this or a later commit.

", None)) + pass + # retranslateUi + diff --git a/spinetoolbox/spine_db_editor/ui/db_commit_viewer.ui b/spinetoolbox/spine_db_editor/ui/db_commit_viewer.ui new file mode 100644 index 000000000..9a09ab605 --- /dev/null +++ b/spinetoolbox/spine_db_editor/ui/db_commit_viewer.ui @@ -0,0 +1,121 @@ + + + + DBCommitViewer + + + + 0 + 0 + 716 + 218 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">No affected items found for selected commit.</p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Note that we cannot show items that have been removed by this or a later commit.</p></body></html> + + + false + + + + + + + + + + + + + diff --git a/spinetoolbox/spine_db_editor/widgets/commit_viewer.py b/spinetoolbox/spine_db_editor/widgets/commit_viewer.py index 047178508..4576f9d63 100644 --- a/spinetoolbox/spine_db_editor/widgets/commit_viewer.py +++ b/spinetoolbox/spine_db_editor/widgets/commit_viewer.py @@ -15,7 +15,6 @@ QMainWindow, QTabWidget, QWidget, - QVBoxLayout, QGridLayout, QTreeWidget, QTreeWidgetItem, @@ -28,57 +27,55 @@ class _DBCommitViewer(QWidget): def __init__(self, db_mngr, db_map, parent=None): + from ..ui.db_commit_viewer import Ui_DBCommitViewer + super().__init__(parent=parent) + self._ui = Ui_DBCommitViewer() + self._ui.setupUi(self) self._db_mngr = db_mngr self._db_map = db_map - self._commit_list = QTreeWidget(self) - self._commit_list.setHeaderLabel("Commits") - self._commit_list.setIndentation(0) - self.splitter = QSplitter(self) - self.splitter.setChildrenCollapsible(False) - self.splitter.setSizes([0.3, 0.7]) - self._affected_items = QTreeWidget(self) - self._affected_items.setHeaderLabel("Affected items") - self.splitter.addWidget(self._commit_list) - self.splitter.addWidget(self._affected_items) - self.splitter.setStretchFactor(0, 0) - self.splitter.setStretchFactor(1, 1) - layout = QVBoxLayout(self) - self.setLayout(layout) - layout = self.layout() - layout.addWidget(self.splitter) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(0) + self._ui.commit_list.setHeaderLabel("Commits") + self._ui.commit_list.setIndentation(0) + self._ui.affected_items.setHeaderLabel("Affected items") + self._ui.splitter.setSizes([0.3, 0.7]) + self._ui.splitter.setStretchFactor(0, 0) + self._ui.splitter.setStretchFactor(1, 1) for commit in reversed(db_map.get_items("commit")): - tree_item = QTreeWidgetItem(self._commit_list) + tree_item = QTreeWidgetItem(self._ui.commit_list) tree_item.setData(0, Qt.ItemDataRole.UserRole + 1, commit["id"]) - self._commit_list.addTopLevelItem(tree_item) - index = self._commit_list.indexFromItem(tree_item) + self._ui.commit_list.addTopLevelItem(tree_item) + index = self._ui.commit_list.indexFromItem(tree_item) widget = _CommitItem(commit) - self._commit_list.setIndexWidget(index, widget) - self._commit_list.currentItemChanged.connect(self._select_commit) + self._ui.commit_list.setIndexWidget(index, widget) + self._ui.commit_list.currentItemChanged.connect(self._select_commit) + + @property + def splitter(self) -> QSplitter: + return self._ui.splitter @Slot(QTreeWidgetItem, QTreeWidgetItem) def _select_commit(self, current, previous): - self._commit_list.setDisabled(True) + self._ui.commit_list.setDisabled(True) self._do_select_commit(current) - self._commit_list.setEnabled(True) + self._ui.commit_list.setEnabled(True) @busy_effect def _do_select_commit(self, current): commit_id = current.data(0, Qt.ItemDataRole.UserRole + 1) - self._affected_items.clear() - # TODO: If no items, show message that data was overwritten by a further commit + self._ui.affected_items_widget_stack.setCurrentIndex(0) + self._ui.affected_items.clear() for item_type, ids in self._db_mngr.get_items_for_commit(self._db_map, commit_id).items(): top_level_item = QTreeWidgetItem([item_type]) - self._affected_items.addTopLevelItem(top_level_item) + self._ui.affected_items.addTopLevelItem(top_level_item) bottom_level_item = QTreeWidgetItem(top_level_item) bottom_level_item.setFlags(bottom_level_item.flags() & ~Qt.ItemIsSelectable) - index = self._affected_items.indexFromItem(bottom_level_item) + index = self._ui.affected_items.indexFromItem(bottom_level_item) items = [self._db_mngr.get_item(self._db_map, item_type, id_) for id_ in ids] - widget = _AffectedItemsFromOneTable(items, parent=self._affected_items) - self._affected_items.setIndexWidget(index, widget) + widget = _AffectedItemsFromOneTable(items, parent=self._ui.affected_items) + self._ui.affected_items.setIndexWidget(index, widget) top_level_item.setExpanded(True) + if self._ui.affected_items.topLevelItemCount() == 0: + self._ui.affected_items_widget_stack.setCurrentIndex(1) class _CommitItem(QWidget): diff --git a/spinetoolbox/spine_db_editor/widgets/custom_qtreeview.py b/spinetoolbox/spine_db_editor/widgets/custom_qtreeview.py index bdea9bf53..5ae9633d2 100644 --- a/spinetoolbox/spine_db_editor/widgets/custom_qtreeview.py +++ b/spinetoolbox/spine_db_editor/widgets/custom_qtreeview.py @@ -10,12 +10,12 @@ # this program. If not, see . ###################################################################################################################### -"""Classes for custom QTreeView.""" -from PySide6.QtWidgets import QApplication, QHeaderView, QMenu, QAbstractItemView +"""Classes for custom QTreeViews and QTreeWidgets.""" +from PySide6.QtWidgets import QApplication, QHeaderView, QMenu, QAbstractItemView, QTreeWidget, QTreeWidgetItem from PySide6.QtCore import Signal, Slot, Qt, QEvent, QTimer, QModelIndex, QItemSelection, QSignalBlocker from PySide6.QtGui import QMouseEvent, QIcon, QGuiApplication from spinetoolbox.widgets.custom_qtreeview import CopyPasteTreeView -from spinetoolbox.helpers import busy_effect, CharIconEngine +from spinetoolbox.helpers import busy_effect, CharIconEngine, DB_ITEM_SEPARATOR from .custom_delegates import ScenarioDelegate, AlternativeDelegate, ParameterValueListDelegate from .scenario_generator import ScenarioGenerator from ..mvcmodels import mime_types diff --git a/spinetoolbox/spine_db_manager.py b/spinetoolbox/spine_db_manager.py index ee6087879..294bdd796 100644 --- a/spinetoolbox/spine_db_manager.py +++ b/spinetoolbox/spine_db_manager.py @@ -816,7 +816,7 @@ def get_value(self, db_map, item_type, id_, role=Qt.ItemDataRole.DisplayRole): """Returns the value or default value of a parameter. Args: - db_map (DiffDatabaseMapping) + db_map (DatabaseMapping) item_type (str): either "parameter_definition", "parameter_value", or "list_value" id_ (int): The parameter_value or definition id role (int, optional) diff --git a/tests/spine_db_editor/widgets/test_commit_viewer.py b/tests/spine_db_editor/widgets/test_commit_viewer.py index 26ef8e9aa..1e746bf53 100644 --- a/tests/spine_db_editor/widgets/test_commit_viewer.py +++ b/tests/spine_db_editor/widgets/test_commit_viewer.py @@ -54,7 +54,7 @@ def test_initial_commit_shows_in_list(self): tab_widget = self._commit_viewer.centralWidget() self.assertEqual(tab_widget.currentIndex(), 0) current_tab = tab_widget.currentWidget() - commit_list = current_tab._commit_list + commit_list = current_tab._ui.commit_list self.assertEqual(commit_list.topLevelItemCount(), 1) initial_commit_item = commit_list.topLevelItem(0) commit_db_items = self._db_map.get_items("commit") @@ -66,10 +66,10 @@ def test_selecting_initial_commit_shows_base_alternative(self): tab_widget = self._commit_viewer.centralWidget() self.assertEqual(tab_widget.currentIndex(), 0) current_tab = tab_widget.currentWidget() - commit_list = current_tab._commit_list + commit_list = current_tab._ui.commit_list initial_commit_item = commit_list.topLevelItem(0) commit_list.setCurrentItem(initial_commit_item) - affected_list = current_tab._affected_items + affected_list = current_tab._ui.affected_items self.assertEqual(affected_list.topLevelItemCount(), 1) affected_item = affected_list.topLevelItem(0) self.assertEqual(affected_item.data(0, Qt.ItemDataRole.DisplayRole), "alternative")