Source code for spinetoolbox.widgets.graph_view_demo

######################################################################################################################
# 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 GraphViewForm class.

:author: M. Marin (KTH)
:date:   26.11.2018
"""

import random
from PySide2.QtCore import Slot, QFinalState, QState, QItemSelectionModel, QAbstractAnimation, QVariantAnimation
from PySide2.QtGui import QCursor
from .state_machine_widget import StateMachineWidget


[docs]class GraphViewDemo(StateMachineWidget): """A widget that shows a demo for the graph view.""" def __init__(self, parent): """Initializes class. Args: parent (GraphViewForm) """ super().__init__("Live demo", parent)
[docs] def _make_select_one(self): select_one = QState(self.machine) begin = QState(select_one) simulate = QState(select_one) finalize = QFinalState(select_one) begin.assignProperty(self.parent().ui.dockWidget_object_tree, "visible", True) text = """ <html> <p>Selecting items in the object tree automatically triggers graph generation.</b><br /> <p>Press <b>Show</b> to see it in action.</p> </html> """ begin.assignProperty(self.label_msg, "text", text) begin.assignProperty(self.button_right, "text", "Show") begin.assignProperty(self.button_right, "enabled", True) simulate.assignProperty(self.parent().ui.dockWidget_object_tree, "visible", True) simulate.assignProperty(self.button_right, "enabled", False) select_one.setInitialState(begin) transition = begin.addTransition(self.button_right.clicked, simulate) animation = SelectionAnimation(self.parent().ui.treeView_object, command=QItemSelectionModel.ClearAndSelect) transition.addAnimation(animation) simulate.addTransition(animation.finished, finalize) return select_one
[docs] def _make_select_more(self): select_more = QState(self.machine) begin = QState(select_more) simulate = QState(select_more) finalize = QFinalState(select_more) begin.assignProperty(self.parent().ui.dockWidget_object_tree, "visible", True) sticky = self.parent().qsettings.value("appSettings/stickySelection", defaultValue="false") note = " (by holding down the <b>Ctrl</b> key)" if sticky == "false" else "" text = f""" <html> <p>Selecting multiple items{note} makes things more interesting.</p> <p>Press <b>Show</b> to see it in action.</p> </html> """ begin.assignProperty(self.label_msg, "text", text) begin.assignProperty(self.button_right, "text", "Show") begin.assignProperty(self.button_right, "enabled", True) simulate.assignProperty(self.parent().ui.dockWidget_object_tree, "visible", True) simulate.assignProperty(self.button_right, "enabled", False) select_more.setInitialState(begin) transition = begin.addTransition(self.button_right.clicked, simulate) animation = SelectionAnimation(self.parent().ui.treeView_object, command=QItemSelectionModel.Select) transition.addAnimation(animation) simulate.addTransition(animation.finished, finalize) return select_more
[docs] def _make_good_bye(self): good_bye = QState(self.machine) begin = QState(good_bye) finalize = QFinalState(good_bye) good_bye.assignProperty(self.label_msg, "text", "<html>That's all for now. Hope you liked it.</html>") good_bye.assignProperty(self.button_left, "visible", False) good_bye.assignProperty(self.button_right, "enabled", True) good_bye.assignProperty(self.button_right, "text", "Close") good_bye.setInitialState(begin) begin.addTransition(self.button_right.clicked, finalize) return good_bye
[docs] def set_up_machine(self): super().set_up_machine() select_one = self._make_select_one() select_more = self._make_select_more() good_bye = self._make_good_bye() self.welcome.addTransition(self.welcome.finished, select_one) select_one.addTransition(select_one.finished, select_more) select_more.addTransition(select_more.finished, good_bye) good_bye.finished.connect(self.close)
[docs]class SelectionAnimation(QVariantAnimation): def __init__(self, view, command, duration=2000, max_steps=4): """ Args: view (QAbstractItemView) command (QItemSelectionModel.SelectionFlags) duration (int): milliseconds max_steps (int) """ super().__init__(view) self.viewport = view.viewport() self._command = command self._duration = duration self._selection_model = view.selectionModel() model = view.model() root_item = model.root_item population_size = root_item.child_count() sample_size = min(max_steps, population_size) picks = random.sample(range(population_size), k=sample_size) self._indexes = [model.index_from_item(root_item.child(k)) for k in picks] self._positions = [self._random_point(view.visualRect(ind)) for ind in self._indexes] self._lines = None self.setStartValue(0.0) self.setEndValue(1.0) self.setLoopCount(len(self._indexes)) self.setDuration(self._duration) self.currentLoopChanged.connect(self._handle_current_loop_changed) self.valueChanged.connect(self._handle_value_changed) self.finished.connect(self._handle_finished) @staticmethod
[docs] def _random_point(rect): return rect.topLeft() + (rect.bottomRight() - rect.topLeft()) / random.triangular(1, 3)
[docs] def updateState(self, new, old): if new == QAbstractAnimation.Running and old == QAbstractAnimation.Stopped: self._selection_model.clearSelection() pos = self.viewport.mapFromGlobal(QCursor.pos()) self._positions.insert(0, pos) self._lines = list(zip(self._positions[:-1], self._positions[1:]))
@Slot("QVariant")
[docs] def _handle_value_changed(self, value): src_pos, dst_pos = self._lines[self.currentLoop()] value = max(0.0, min(1.0, 4.0 * (value - 0.5))) pos = src_pos + (dst_pos - src_pos) * value pos = self.viewport.mapToGlobal(pos) QCursor.setPos(pos)
@Slot(int)
[docs] def _handle_current_loop_changed(self, loop): self._selection_model.select(self._indexes[loop - 1], self._command)
@Slot()
[docs] def _handle_finished(self): self._selection_model.select(self._indexes[-1], self._command)