Source code for spinetoolbox.spine_db_editor.mvcmodels.tree_item_utility

######################################################################################################################
# Copyright (C) 2017-2021 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/>.
######################################################################################################################

"""
A tree model for parameter_value lists.

:authors: M. Marin (KTH)
:date:   28.6.2019
"""

from PySide2.QtCore import Qt
from PySide2.QtGui import QBrush, QFont, QIcon, QGuiApplication
from spinetoolbox.mvcmodels.minimal_tree_model import TreeItem
from spinetoolbox.helpers import CharIconEngine


[docs]class NonLazyTreeItem(TreeItem): """A tree item that fetches their children as they are inserted.""" @property
[docs] def item_type(self): return None
@property
[docs] def db_mngr(self): return self.model.db_mngr
@property
[docs] def display_data(self): return None
@property
[docs] def icon_code(self): return None
@property
[docs] def tool_tip(self): return None
@property
[docs] def display_icon(self): if self.icon_code is None: return None engine = CharIconEngine(self.icon_code, 0) return QIcon(engine.pixmap())
[docs] def data(self, column, role=Qt.DisplayRole): if column != 0: return None if role in (Qt.DisplayRole, Qt.EditRole): return self.display_data if role == Qt.DecorationRole: return self.display_icon if role == Qt.ToolTipRole: return self.tool_tip return super().data(column, role)
[docs] def set_data(self, column, value, role=Qt.DisplayRole): return False
[docs] def can_fetch_more(self): """Disables lazy loading by returning False.""" return False
[docs] def insert_children(self, position, *children): """Fetches the children as they become parented.""" if not super().insert_children(position, *children): return False for child in children: child.fetch_more() return True
[docs]class EditableMixin:
[docs] def flags(self, column): """Makes items editable.""" return Qt.ItemIsEditable | super().flags(column)
[docs]class LastGrayMixin: """Paints the last item gray."""
[docs] def data(self, column, role=Qt.DisplayRole): if role == Qt.ForegroundRole and self.child_number() == self.parent_item.child_count() - 1: gray_color = QGuiApplication.palette().text().color() gray_color.setAlpha(128) gray_brush = QBrush(gray_color) return gray_brush return super().data(column, role)
[docs]class AllBoldMixin: """Bolds text."""
[docs] def data(self, column, role=Qt.DisplayRole): if role == Qt.FontRole: bold_font = QFont() bold_font.setBold(True) return bold_font return super().data(column, role)
[docs]class EmptyChildMixin: """Guarantess there's always an empty child."""
[docs] def empty_child(self): raise NotImplementedError()
[docs] def fetch_more(self): empty_child = self.empty_child() self.append_children(empty_child) self._fetched = True
[docs] def append_empty_child(self, row): """Appends empty child if the row is the last one.""" if row == self.child_count() - 1: empty_child = self.empty_child() self.append_children(empty_child)
[docs]class NonLazyDBItem(NonLazyTreeItem): """An item representing a db.""" def __init__(self, db_map): """Init class. Args db_mngr (SpineDBManager) db_map (DiffDatabaseMapping) """ super().__init__() self.db_map = db_map @property
[docs] def item_type(self): return "db"
[docs] def data(self, column, role=Qt.DisplayRole): """Shows Spine icon for fun.""" if column != 0: return None if role == Qt.DecorationRole: return QIcon(":/symbols/Spine_symbol.png") if role in (Qt.DisplayRole, Qt.EditRole): return self.db_map.codename
[docs]class RootItem(AllBoldMixin, NonLazyTreeItem): """A root item.""" @property
[docs] def item_type(self): raise NotImplementedError
@property
[docs] def db_map(self): return self.parent_item.db_map
[docs]class EmptyChildRootItem(EmptyChildMixin, RootItem):
[docs] def empty_child(self): raise NotImplementedError
[docs]class LeafItem(NonLazyTreeItem): def __init__(self, identifier=None): super().__init__() self._id = identifier self._item_data = self._make_item_data()
[docs] def _make_item_data(self): return {"name": f"Type new {self.item_type} name here...", "description": ""}
@property
[docs] def item_type(self): raise NotImplementedError()
@property
[docs] def db_map(self): return self.parent_item.db_map
@property
[docs] def id(self): return self._id
@property
[docs] def item_data(self): if not self.id: return self._item_data return self.db_mngr.get_item(self.db_map, self.item_type, self.id)
@property
[docs] def name(self): return self.item_data["name"]
[docs] def add_item_to_db(self, db_item): raise NotImplementedError()
[docs] def update_item_in_db(self, db_item): raise NotImplementedError()
[docs] def header_data(self, column): return self.model.headerData(column, Qt.Horizontal)
[docs] def data(self, column, role=Qt.DisplayRole): if role in (Qt.DisplayRole, Qt.EditRole): data = self.item_data.get(self.header_data(column)) if data is None: data = "" return data return super().data(column, role)
[docs] def set_data(self, column, value, role=Qt.EditRole): if role != Qt.EditRole or value == self.data(column, role): return False if self.id: db_item = self._make_item_to_update(column, value) self.update_item_in_db(db_item) return True if column == 0: db_item = self._make_item_to_add(value) self.add_item_to_db(db_item) return True
[docs] def _make_item_to_add(self, value): return dict(name=value, description=self._item_data["description"])
[docs] def _make_item_to_update(self, column, value): field = self.header_data(column) return {"id": self.id, field: value}
[docs] def handle_updated_in_db(self): index = self.index() sibling = self.index().sibling(self.index().row(), 1) self.model.dataChanged.emit(index, sibling)