From 53d69141010b3a5707e6141dccb82759d7091ef6 Mon Sep 17 00:00:00 2001 From: inso <insomniak.fr@gmaiL.com> Date: Mon, 8 Aug 2016 19:00:21 +0200 Subject: [PATCH] New explorer component --- src/sakia/gui/graphs/explorer/__init__.py | 0 src/sakia/gui/graphs/explorer/controller.py | 75 +++++++++++ .../explorer_edge.py => explorer/edge.py} | 2 +- .../sakia/gui/graphs/explorer/explorer.ui | 12 +- .../explorer.py => explorer/graphics_view.py} | 14 +- src/sakia/gui/graphs/explorer/model.py | 54 ++++++++ .../explorer_node.py => explorer/node.py} | 2 +- .../explorer_scene.py => explorer/scene.py} | 6 +- src/sakia/gui/graphs/explorer/view.py | 54 ++++++++ src/sakia/gui/graphs/explorer_tab.py | 124 ------------------ src/sakia/gui/graphs/views/__init__.py | 2 - src/sakia/gui/graphs/views/edges/__init__.py | 2 - src/sakia/gui/graphs/views/nodes/__init__.py | 2 - src/sakia/gui/graphs/views/scenes/__init__.py | 2 - src/sakia/gui/informations/controller.py | 10 +- src/sakia/gui/navigation/controller.py | 4 +- src/sakia/gui/navigation/model.py | 8 ++ src/sakia/gui/txhistory/model.py | 1 + 18 files changed, 217 insertions(+), 157 deletions(-) create mode 100644 src/sakia/gui/graphs/explorer/__init__.py create mode 100644 src/sakia/gui/graphs/explorer/controller.py rename src/sakia/gui/graphs/{views/edges/explorer_edge.py => explorer/edge.py} (99%) rename res/ui/explorer_tab.ui => src/sakia/gui/graphs/explorer/explorer.ui (87%) rename src/sakia/gui/graphs/{views/explorer.py => explorer/graphics_view.py} (81%) create mode 100644 src/sakia/gui/graphs/explorer/model.py rename src/sakia/gui/graphs/{views/nodes/explorer_node.py => explorer/node.py} (99%) rename src/sakia/gui/graphs/{views/scenes/explorer_scene.py => explorer/scene.py} (99%) create mode 100644 src/sakia/gui/graphs/explorer/view.py delete mode 100644 src/sakia/gui/graphs/explorer_tab.py delete mode 100644 src/sakia/gui/graphs/views/__init__.py delete mode 100644 src/sakia/gui/graphs/views/edges/__init__.py delete mode 100644 src/sakia/gui/graphs/views/nodes/__init__.py delete mode 100644 src/sakia/gui/graphs/views/scenes/__init__.py diff --git a/src/sakia/gui/graphs/explorer/__init__.py b/src/sakia/gui/graphs/explorer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/sakia/gui/graphs/explorer/controller.py b/src/sakia/gui/graphs/explorer/controller.py new file mode 100644 index 00000000..e116ab34 --- /dev/null +++ b/src/sakia/gui/graphs/explorer/controller.py @@ -0,0 +1,75 @@ +from ..base.controller import BaseGraphController +from sakia.tools.decorators import asyncify, once_at_a_time +from .view import ExplorerView +from .model import ExplorerModel + + +class ExplorerController(BaseGraphController): + """ + The homescreen view + """ + + def __init__(self, parent, view, model, password_asker=None): + """ + Constructor of the explorer component + + :param sakia.gui.graphs.explorer.view.ExplorerView: the view + :param sakia.gui.graphs.explorer.model.ExplorerModel model: the model + """ + super().__init__(parent, view, model, password_asker) + self.set_scene(view.scene()) + self.reset() + self.view.button_go.clicked.connect(self.refresh) + + @property + def view(self) -> ExplorerView: + return self._view + + @property + def model(self) -> ExplorerModel: + return self._model + + @classmethod + def create(cls, parent, app, **kwargs): + account = kwargs['account'] + community = kwargs['community'] + + view = ExplorerView(parent.view) + model = ExplorerModel(None, app, account, community) + wot = cls(parent, view, model) + model.setParent(wot) + return wot + + async def draw_graph(self, identity): + """ + Draw community graph centered on the identity + + :param sakia.core.registry.Identity identity: Center identity + """ + await self.model.set_identity(identity) + self.model.graph.start_exploration(self.model.identity, self.view.steps()) + + # draw graph in qt scene + self.view.scene().clear() + self.view.update_wot(self.model.graph.nx_graph, self.model.identity) + + @once_at_a_time + @asyncify + async def refresh(self): + """ + Refresh graph scene to current metadata + """ + self.model.graph.stop_exploration() + self.draw_graph(self.model.identity) + self.view.update_wot(self.model.graph.nx_graph, self.model.identity) + + @once_at_a_time + @asyncify + async def reset(self, checked=False): + """ + Reset graph scene to wallet identity + """ + self.view.reset_steps() + maximum_steps = await self.model.maximum_steps() + self.view.set_steps_max(maximum_steps) + await self.draw_graph(None) diff --git a/src/sakia/gui/graphs/views/edges/explorer_edge.py b/src/sakia/gui/graphs/explorer/edge.py similarity index 99% rename from src/sakia/gui/graphs/views/edges/explorer_edge.py rename to src/sakia/gui/graphs/explorer/edge.py index 6f39bea9..93584049 100644 --- a/src/sakia/gui/graphs/views/edges/explorer_edge.py +++ b/src/sakia/gui/graphs/explorer/edge.py @@ -2,7 +2,7 @@ from PyQt5.QtCore import Qt, QRectF, QLineF, QPointF, QSizeF, \ qFuzzyCompare, QTimeLine from PyQt5.QtGui import QColor, QPen, QPolygonF import math -from .base_edge import BaseEdge +from ..base.edge import BaseEdge from ....core.graph.constants import EdgeStatus diff --git a/res/ui/explorer_tab.ui b/src/sakia/gui/graphs/explorer/explorer.ui similarity index 87% rename from res/ui/explorer_tab.ui rename to src/sakia/gui/graphs/explorer/explorer.ui index 58aa9891..9b3c7087 100644 --- a/res/ui/explorer_tab.ui +++ b/src/sakia/gui/graphs/explorer/explorer.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ExplorerTabWidget</class> - <widget class="QWidget" name="ExplorerTabWidget"> + <class>ExplorerWidget</class> + <widget class="QWidget" name="ExplorerWidget"> <property name="geometry"> <rect> <x>0</x> @@ -18,7 +18,7 @@ <widget class="SearchUserWidget" name="search_user_widget" native="true"/> </item> <item> - <widget class="ExplorerView" name="graphicsView"> + <widget class="ExplorerGraphicsView" name="graphics_view"> <property name="viewportUpdateMode"> <enum>QGraphicsView::BoundingRectViewportUpdate</enum> </property> @@ -72,9 +72,9 @@ </widget> <customwidgets> <customwidget> - <class>ExplorerView</class> + <class>ExplorerGraphicsView</class> <extends>QGraphicsView</extends> - <header>sakia.gui.views</header> + <header>sakia.gui.graphs.explorer.graphics_view</header> </customwidget> <customwidget> <class>SearchUserWidget</class> @@ -84,7 +84,7 @@ </customwidget> </customwidgets> <resources> - <include location="../icons/icons.qrc"/> + <include location="../../../../../res/icons/icons.qrc"/> </resources> <connections/> <slots> diff --git a/src/sakia/gui/graphs/views/explorer.py b/src/sakia/gui/graphs/explorer/graphics_view.py similarity index 81% rename from src/sakia/gui/graphs/views/explorer.py rename to src/sakia/gui/graphs/explorer/graphics_view.py index 927d9f08..3229282c 100644 --- a/src/sakia/gui/graphs/views/explorer.py +++ b/src/sakia/gui/graphs/explorer/graphics_view.py @@ -1,14 +1,10 @@ -import logging - -import networkx -from PyQt5.QtCore import Qt, QPoint, pyqtSignal +from PyQt5.QtCore import Qt from PyQt5.QtGui import QPainter, QWheelEvent -from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene - -from .scenes import ExplorerScene +from PyQt5.QtWidgets import QGraphicsView +from .scene import ExplorerScene -class ExplorerView(QGraphicsView): +class ExplorerGraphicsView(QGraphicsView): def __init__(self, parent=None): """ Create View to display scene @@ -44,4 +40,4 @@ class ExplorerView(QGraphicsView): # act normally on scrollbar else: # transmit event to parent class wheelevent - super(QGraphicsView, self).wheelEvent(event) + super().wheelEvent(event) \ No newline at end of file diff --git a/src/sakia/gui/graphs/explorer/model.py b/src/sakia/gui/graphs/explorer/model.py new file mode 100644 index 00000000..02879cd2 --- /dev/null +++ b/src/sakia/gui/graphs/explorer/model.py @@ -0,0 +1,54 @@ +from ..base.model import BaseGraphModel +from sakia.core.graph import ExplorerGraph +from sakia.tools.exceptions import NoPeerAvailable + + +class ExplorerModel(BaseGraphModel): + """ + The model of Navigation component + """ + + def __init__(self, parent, app, account, community): + super().__init__(parent, app, account, community) + self.app = app + self.account = account + self.community = community + self.explorer_graph = ExplorerGraph(self.app, self.community) + self.identity = None + + @property + def graph(self): + """ + Return underlying graph object + """ + return self.explorer_graph + + async def set_identity(self, identity=None): + """ + Change current identity + If identity is None, it defaults to account identity + :param sakia.core.registry.Identity identity: the new identity to show + :return: + """ + identity_account = await self.account.identity(self.community) + if identity: + self.identity = identity + else: + self.identity = identity_account + + async def start_exploration(self, steps): + """ + Get nx graph of current identity wot graph + :rtype: sakia.core.registry.Identity + """ + return self.explorer_graph.start_exploration(self.identity, steps) + + async def maximum_steps(self): + """ + Get the maximum steps to display in the view + """ + try: + parameters = await self.community.parameters() + return parameters['stepMax'] + except NoPeerAvailable: + return 0 diff --git a/src/sakia/gui/graphs/views/nodes/explorer_node.py b/src/sakia/gui/graphs/explorer/node.py similarity index 99% rename from src/sakia/gui/graphs/views/nodes/explorer_node.py rename to src/sakia/gui/graphs/explorer/node.py index 854d1e8f..1be84d23 100644 --- a/src/sakia/gui/graphs/views/nodes/explorer_node.py +++ b/src/sakia/gui/graphs/explorer/node.py @@ -2,7 +2,7 @@ from PyQt5.QtWidgets import QGraphicsSimpleTextItem from PyQt5.QtCore import Qt, QPointF, QTimeLine, QTimer from PyQt5.QtGui import QTransform, QColor, QPen, QBrush, QRadialGradient from ....core.graph.constants import NodeStatus -from .base_node import BaseNode +from ..base.node import BaseNode import math diff --git a/src/sakia/gui/graphs/views/scenes/explorer_scene.py b/src/sakia/gui/graphs/explorer/scene.py similarity index 99% rename from src/sakia/gui/graphs/views/scenes/explorer_scene.py rename to src/sakia/gui/graphs/explorer/scene.py index 11b8511c..1036bc38 100644 --- a/src/sakia/gui/graphs/views/scenes/explorer_scene.py +++ b/src/sakia/gui/graphs/explorer/scene.py @@ -4,10 +4,10 @@ import math from PyQt5.QtCore import QPoint, pyqtSignal from PyQt5.QtWidgets import QGraphicsScene -from ..edges import ExplorerEdge -from ..nodes import ExplorerNode +from .edge import ExplorerEdge +from .node import ExplorerNode -from .base_scene import BaseScene +from ..base.scene import BaseScene class ExplorerScene(BaseScene): diff --git a/src/sakia/gui/graphs/explorer/view.py b/src/sakia/gui/graphs/explorer/view.py new file mode 100644 index 00000000..7157e74f --- /dev/null +++ b/src/sakia/gui/graphs/explorer/view.py @@ -0,0 +1,54 @@ +from PyQt5.QtCore import QEvent +from ..base.view import BaseGraphView +from .explorer_uic import Ui_ExplorerWidget + + +class ExplorerView(BaseGraphView, Ui_ExplorerWidget): + """ + Wot graph view + """ + + def __init__(self, parent): + """ + Constructor + """ + super().__init__(parent) + self.setupUi(self) + + def set_steps_max(self, maximum): + """ + Set the steps slider max value + :param int maximum: the max value + """ + self.steps_slider.setMaximum(maximum) + + def scene(self): + """ + Get the scene of the underlying graphics view + :return: + """ + return self.graphics_view.scene() + + def reset_steps(self): + """ + Reset the steps slider + """ + self.steps_slider.setValue(0) + + def steps(self): + """ + Get the number of steps selected + :return: + """ + return self.steps_slider.value() + + def update_wot(self, nx_graph, identity): + """ + Display given wot around given identity + :param nx_graph: + :param identity: + :param steps: + :return: + """ + # draw graph in qt scene + self.scene().update_wot(nx_graph, identity, self.steps_slider.maximum()) diff --git a/src/sakia/gui/graphs/explorer_tab.py b/src/sakia/gui/graphs/explorer_tab.py deleted file mode 100644 index 66ac4c7b..00000000 --- a/src/sakia/gui/graphs/explorer_tab.py +++ /dev/null @@ -1,124 +0,0 @@ -import logging - -from PyQt5.QtCore import QEvent, pyqtSignal -from PyQt5.QtWidgets import QWidget - -from ...tools.exceptions import NoPeerAvailable -from ...tools.decorators import asyncify, once_at_a_time, cancel_once_task -from ...core.graph import ExplorerGraph -from .graph_tab import GraphTabWidget -from ...presentation.explorer_tab_uic import Ui_ExplorerTabWidget - - -class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): - - money_sent = pyqtSignal() - - def __init__(self, app, account=None, community=None, password_asker=None, - widget=QWidget, view=Ui_ExplorerTabWidget): - """ - :param sakia.core.app.Application app: Application instance - :param sakia.core.app.Application app: Application instance - :param sakia.core.Account account: The account displayed in the widget - :param sakia.core.Community community: The community displayed in the widget - :param sakia.gui.Password_Asker: password_asker: The widget to ask for passwords - """ - # construct from qtDesigner - super().__init__(app, account, community, password_asker, widget) - self.ui = view() - self.ui.setupUi(self.widget) - self.ui.search_user_widget.init(app) - - self.set_scene(self.ui.graphicsView.scene()) - self.graph = None - self.app = app - self.draw_task = None - - # nodes list for menu from search - self.nodes = list() - - # create node metadata from account - self._current_identity = None - self.ui.button_go.clicked.connect(self.go_clicked) - self.ui.search_user_widget.identity_selected.connect(self.draw_graph) - self.ui.search_user_widget.reset.connect(self.reset) - - def cancel_once_tasks(self): - cancel_once_task(self, self.refresh_informations_frame) - cancel_once_task(self, self.reset) - - def change_account(self, account, password_asker): - self.ui.search_user_widget.change_account(account) - self.account = account - self.password_asker = password_asker - - def change_community(self, community): - self.community = community - if self.graph: - self.graph.stop_exploration() - self.graph = ExplorerGraph(self.app, self.community) - self.graph.graph_changed.connect(self.refresh) - self.ui.search_user_widget.change_community(community) - self.graph.current_identity_changed.connect(self.ui.graphicsView.scene().update_current_identity) - self.reset() - - def go_clicked(self): - if self.graph: - self.graph.stop_exploration() - self.draw_graph(self._current_identity) - - def draw_graph(self, identity): - """ - Draw community graph centered on the identity - - :param sakia.core.registry.Identity identity: Graph node identity - """ - logging.debug("Draw graph - " + identity.uid) - - if self.community: - #Connect new identity - if self._current_identity != identity: - self._current_identity = identity - - self.graph.start_exploration(identity, self.ui.steps_slider.value()) - - # draw graph in qt scene - self.ui.graphicsView.scene().clear() - self.ui.graphicsView.scene().update_wot(self.graph.nx_graph, identity, self.ui.steps_slider.maximum()) - - def refresh(self): - """ - Refresh graph scene to current metadata - """ - if self._current_identity: - # draw graph in qt scene - self.ui.graphicsView.scene().update_wot(self.graph.nx_graph, self._current_identity, self.ui.steps_slider.maximum()) - else: - self.reset() - - @once_at_a_time - @asyncify - async def reset(self, checked=False): - """ - Reset graph scene to wallet identity - """ - if self.account and self.community: - try: - parameters = await self.community.parameters() - self.ui.steps_slider.setMaximum(parameters['stepMax']) - self.ui.steps_slider.setValue(int(0.33 * parameters['stepMax'])) - identity = await self.account.identity(self.community) - self.draw_graph(identity) - except NoPeerAvailable: - logging.debug("No peer available") - - def changeEvent(self, event): - """ - Intercepte LanguageChange event to translate UI - :param QEvent QEvent: Event - :return: - """ - if event.type() == QEvent.LanguageChange: - self.retranslateUi(self) - self.refresh() - return super().changeEvent(event) diff --git a/src/sakia/gui/graphs/views/__init__.py b/src/sakia/gui/graphs/views/__init__.py deleted file mode 100644 index 3ca1e9f7..00000000 --- a/src/sakia/gui/graphs/views/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .explorer import ExplorerView -from .wot import WotView \ No newline at end of file diff --git a/src/sakia/gui/graphs/views/edges/__init__.py b/src/sakia/gui/graphs/views/edges/__init__.py deleted file mode 100644 index 188e3acf..00000000 --- a/src/sakia/gui/graphs/views/edges/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .wot_edge import WotEdge -from .explorer_edge import ExplorerEdge \ No newline at end of file diff --git a/src/sakia/gui/graphs/views/nodes/__init__.py b/src/sakia/gui/graphs/views/nodes/__init__.py deleted file mode 100644 index 28332381..00000000 --- a/src/sakia/gui/graphs/views/nodes/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .wot_node import WotNode -from .explorer_node import ExplorerNode \ No newline at end of file diff --git a/src/sakia/gui/graphs/views/scenes/__init__.py b/src/sakia/gui/graphs/views/scenes/__init__.py deleted file mode 100644 index f2eef13e..00000000 --- a/src/sakia/gui/graphs/views/scenes/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .wot_scene import WotScene -from .explorer_scene import ExplorerScene \ No newline at end of file diff --git a/src/sakia/gui/informations/controller.py b/src/sakia/gui/informations/controller.py index a54b5581..df4eab85 100644 --- a/src/sakia/gui/informations/controller.py +++ b/src/sakia/gui/informations/controller.py @@ -2,7 +2,8 @@ from ..component.controller import ComponentController from .model import InformationsModel from .view import InformationsView from sakia.tools.decorators import asyncify - +from sakia.tools.exceptions import NoPeerAvailable +import logging class InformationsController(ComponentController): """ @@ -55,9 +56,10 @@ class InformationsController(ComponentController): self.view.set_general_text_no_dividend() self.view.set_text_referentials(referentials) params = await self.model.parameters() - self.view.set_money_text(params, self.model.short_currency()) - self.view.set_wot_text(params) - self.refresh_localized_data() + if params: + self.view.set_money_text(params, self.model.short_currency()) + self.view.set_wot_text(params) + self.refresh_localized_data() @asyncify async def refresh_localized_data(self): diff --git a/src/sakia/gui/navigation/controller.py b/src/sakia/gui/navigation/controller.py index c09825c3..ac01f3bb 100644 --- a/src/sakia/gui/navigation/controller.py +++ b/src/sakia/gui/navigation/controller.py @@ -7,6 +7,7 @@ from ..network.controller import NetworkController from ..identities.controller import IdentitiesController from ..informations.controller import InformationsController from ..graphs.wot.controller import WotController +from ..graphs.explorer.controller import ExplorerController class NavigationController(ComponentController): @@ -28,7 +29,8 @@ class NavigationController(ComponentController): 'Network': NetworkController, 'Identities': IdentitiesController, 'Informations': InformationsController, - 'Wot': WotController + 'Wot': WotController, + 'Explorer': ExplorerController } @classmethod diff --git a/src/sakia/gui/navigation/model.py b/src/sakia/gui/navigation/model.py index 0d59fe1d..166e625d 100644 --- a/src/sakia/gui/navigation/model.py +++ b/src/sakia/gui/navigation/model.py @@ -69,6 +69,14 @@ class NavigationModel(ComponentModel): 'community': c, 'account': self.app.current_account } + }, + { + 'node': { + 'title': self.tr('Explorer'), + 'component': "Explorer", + 'community': c, + 'account': self.app.current_account + } } ] }) diff --git a/src/sakia/gui/txhistory/model.py b/src/sakia/gui/txhistory/model.py index fd6bf345..c516b4d6 100644 --- a/src/sakia/gui/txhistory/model.py +++ b/src/sakia/gui/txhistory/model.py @@ -96,6 +96,7 @@ class TxHistoryModel(ComponentModel): logging.debug(str(e)) except errors.DuniterError as e: logging.debug(str(e)) + return QDateTime().currentDateTime(), QDateTime.currentDateTime().addDays(1) async def received_amount(self, received_list): """ -- GitLab