diff --git a/src/sakia/gui/graphs/explorer/__init__.py b/src/sakia/gui/graphs/explorer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/sakia/gui/graphs/explorer/controller.py b/src/sakia/gui/graphs/explorer/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..e116ab342ede338c32ca2af9c8f40fa5eec8002b --- /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 6f39bea9cc3fc8879eaa3575eadf0b5d2065b2c8..935840498e4b15831eb429040507aa2c8d464bed 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 58aa9891db98f11f4f66fcf45a3be4913937e123..9b3c7087a99704e5f553bff9cc96f518bbd81c1f 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 927d9f085cb63c2c633a878a0c70cb4f380781f1..3229282c5f63c2cd16d79cf7ffa0d40df6b8ba7b 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 0000000000000000000000000000000000000000..02879cd24cf95e6802a99739e1047ccfbadefc06 --- /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 854d1e8fef5ac9b9eaa50aaf9981a1a696840037..1be84d23635d6e8db63a60a97f45f187df5d22fd 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 11b8511c8ecce401d6079dfc7275826ed0cb1b23..1036bc3809e94c3789e0eb5339355f24bba60b54 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 0000000000000000000000000000000000000000..7157e74f6f4903d7b76cc08a2310a85050d30795 --- /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 66ac4c7bd9abce3c904305633afd593b130593d5..0000000000000000000000000000000000000000 --- 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 3ca1e9f7ff0ac7bd3d3e7c9e4d8344dd36a87a54..0000000000000000000000000000000000000000 --- 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 188e3acfd2ccf101092227b52be070f696cd68c4..0000000000000000000000000000000000000000 --- 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 28332381ab8beea33a4eb493b6d21af09a582adb..0000000000000000000000000000000000000000 --- 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 f2eef13ee6a02e87b65491aa2d076785a7fc84d1..0000000000000000000000000000000000000000 --- 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 a54b558107edf42566916d1e0fd9221a3502b67d..df4eab8540612aba48e4340818e864e437785884 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 c09825c3981ce28d8cecaa742a6481a2465abdd2..ac01f3bbd9ec9506dc2ed642d02468cb3aa63724 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 0d59fe1d7f31a609b6074e6275fc42ccbca10c8b..166e625df04a2262e4520d0ed8655ea80f81a47c 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 fd6bf345f9336427c118c4a6c30cc29eed3bb934..c516b4d6961dbc32c012733e415958dbc9c75923 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): """