diff --git a/src/sakia/gui/community_view.py b/src/sakia/gui/community_view.py index e953759d4994af62a5d2e515c88becba6f36af25..f9d8ee42546f388818704281e186adb814c29dff 100644 --- a/src/sakia/gui/community_view.py +++ b/src/sakia/gui/community_view.py @@ -71,17 +71,17 @@ class CommunityWidget(QWidget, Ui_CommunityWidget): super().setupUi(self) self.tab_identities.view_in_wot.connect(self.tab_wot.draw_graph) - self.tab_identities.view_in_wot.connect(lambda: self.tabs.setCurrentWidget(self.tab_wot)) + self.tab_identities.view_in_wot.connect(lambda: self.tabs.setCurrentWidget(self.tab_wot.widget)) self.tab_history.view_in_wot.connect(self.tab_wot.draw_graph) - self.tab_history.view_in_wot.connect(lambda: self.tabs.setCurrentWidget(self.tab_wot)) - self.tab_identities.money_sent.connect(lambda: self.tab_history.table_history.model().sourceModel().refresh_transfers()) - self.tab_wot.money_sent.connect(lambda: self.tab_history.table_history.model().sourceModel().refresh_transfers()) + self.tab_history.view_in_wot.connect(lambda: self.tabs.setCurrentWidget(self.tab_wot.widget)) + self.tab_identities.money_sent.connect(lambda: self.tab_history.widget.table_history.model().sourceModel().refresh_transfers()) + self.tab_wot.money_sent.connect(lambda: self.tab_history.widget.table_history.model().sourceModel().refresh_transfers()) self.tabs.addTab(self.tab_history.widget, QIcon(':/icons/tx_icon'), self.tr(CommunityWidget._tab_history_label)) - self.tabs.addTab(self.tab_wot, + self.tabs.addTab(self.tab_wot.widget, QIcon(':/icons/wot_icon'), self.tr(CommunityWidget._tab_wot_label)) @@ -99,7 +99,7 @@ class CommunityWidget(QWidget, Ui_CommunityWidget): self.toolbutton_menu.addAction(action_showinfo) action_showexplorer = QAction(self.tr("Show explorer"), self.toolbutton_menu) - action_showexplorer.triggered.connect(lambda : self.show_closable_tab(self.tab_explorer, + action_showexplorer.triggered.connect(lambda : self.show_closable_tab(self.tab_explorer.widget, QIcon(":/icons/explorer_icon"), self.tr("Explorer"))) self.toolbutton_menu.addAction(action_showexplorer) @@ -410,7 +410,7 @@ The process to join back the community later will have to be done again.""") :param widget: :return: """ - self.tabs.setTabText(self.tabs.indexOf(self.tab_wot), self.tr(CommunityWidget._tab_wot_label)) + self.tabs.setTabText(self.tabs.indexOf(self.tab_wot.widget), self.tr(CommunityWidget._tab_wot_label)) self.tabs.setTabText(self.tabs.indexOf(self.tab_network), self.tr(CommunityWidget._tab_network_label)) self.tabs.setTabText(self.tabs.indexOf(self.tab_informations), self.tr(CommunityWidget._tab_informations_label)) self.tabs.setTabText(self.tabs.indexOf(self.tab_history.widget), self.tr(CommunityWidget._tab_history_label)) diff --git a/src/sakia/gui/graphs/explorer_tab.py b/src/sakia/gui/graphs/explorer_tab.py index 75321719cb4994d532b1b4e070209e5a437735a5..b39ee555759d06f08b01380046b79748f6e76628 100644 --- a/src/sakia/gui/graphs/explorer_tab.py +++ b/src/sakia/gui/graphs/explorer_tab.py @@ -1,11 +1,9 @@ import logging -from PyQt5.QtCore import QEvent, pyqtSignal, QT_TRANSLATE_NOOP - -from ucoinpy.api import bma +from PyQt5.QtCore import QEvent, pyqtSignal +from PyQt5.QtWidgets import QWidget from ...tools.decorators import asyncify, once_at_a_time, cancel_once_task -from ...tools.exceptions import NoPeerAvailable from ...core.graph import ExplorerGraph from .graph_tab import GraphTabWidget from ...gen_resources.explorer_tab_uic import Ui_ExplorerTabWidget @@ -15,20 +13,22 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): money_sent = pyqtSignal() - def __init__(self, app): + 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) - self.setupUi(self) - self.search_user_widget.init(app) - - self.set_scene(self.graphicsView.scene()) + super().__init__(app, account, community, password_asker, widget) + self.ui = view() + self.ui.setupUi(self.widget) + self.ui.search_user_widget.init(app) - self.account = None - self.community = None - self.password_asker = None + self.set_scene(self.ui.graphicsView.scene()) self.graph = None self.app = app self.draw_task = None @@ -38,16 +38,16 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): # create node metadata from account self._current_identity = None - self.button_go.clicked.connect(self.go_clicked) - self.search_user_widget.identity_selected.connect(self.draw_graph) - self.search_user_widget.reset.connect(self.reset) + 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.search_user_widget.change_account(account) + self.ui.search_user_widget.change_account(account) self.account = account self.password_asker = password_asker @@ -57,8 +57,8 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): self.graph.stop_exploration() self.graph = ExplorerGraph(self.app, self.community) self.graph.graph_changed.connect(self.refresh) - self.search_user_widget.change_community(community) - self.graph.current_identity_changed.connect(self.graphicsView.scene().update_current_identity) + 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): @@ -79,11 +79,11 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): if self._current_identity != identity: self._current_identity = identity - self.graph.start_exploration(identity, self.steps_slider.value()) + self.graph.start_exploration(identity, self.ui.steps_slider.value()) # draw graph in qt scene - self.graphicsView.scene().clear() - self.graphicsView.scene().update_wot(self.graph.nx_graph, identity, self.steps_slider.maximum()) + self.ui.graphicsView.scene().clear() + self.ui.graphicsView.scene().update_wot(self.graph.nx_graph, identity, self.ui.steps_slider.maximum()) def refresh(self): """ @@ -91,7 +91,7 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): """ if self._current_identity: # draw graph in qt scene - self.graphicsView.scene().update_wot(self.graph.nx_graph, self._current_identity, self.steps_slider.maximum()) + self.ui.graphicsView.scene().update_wot(self.graph.nx_graph, self._current_identity, self.ui.steps_slider.maximum()) else: self.reset() @@ -103,8 +103,8 @@ class ExplorerTabWidget(GraphTabWidget, Ui_ExplorerTabWidget): """ if self.account and self.community: parameters = await self.community.parameters() - self.steps_slider.setMaximum(parameters['stepMax']) - self.steps_slider.setValue(int(0.33 * parameters['stepMax'])) + 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) diff --git a/src/sakia/gui/graphs/graph_tab.py b/src/sakia/gui/graphs/graph_tab.py index 18ec848cfaf37cbc6da5c248fde16fd58cf15ecf..4bb7e4547445ff50132df7a29fb5cb66e11c946d 100644 --- a/src/sakia/gui/graphs/graph_tab.py +++ b/src/sakia/gui/graphs/graph_tab.py @@ -1,35 +1,38 @@ -from PyQt5.QtWidgets import QWidget, QDialog -from PyQt5.QtCore import pyqtSlot, QEvent, QLocale, QDateTime, pyqtSignal +from PyQt5.QtWidgets import QWidget +from PyQt5.QtCore import pyqtSlot, QEvent, QLocale, QDateTime, pyqtSignal, QObject +from PyQt5.QtGui import QCursor from ...tools.exceptions import MembershipNotFoundError from ...tools.decorators import asyncify, once_at_a_time from ...core.registry import BlockchainState -from ...gui.member import MemberDialog -from ...gui.certification import CertificationDialog -from ...gui.transfer import TransferMoneyDialog -from ...gui.contact import ConfigureContactDialog +from ..widgets import ContextMenu -class GraphTabWidget(QWidget): +class GraphTabWidget(QObject): money_sent = pyqtSignal() - def __init__(self, app): + + def __init__(self, app, account=None, community=None, password_asker=None, widget=QWidget): """ :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 + :param class widget: The class of the graph tab """ super().__init__() - self.password_asker = None + self.widget = widget() + self.account = account + self.community = community + self.password_asker = password_asker + self.app = app def set_scene(self, scene): # add scene events - scene.node_clicked.connect(self.handle_node_click) - scene.node_signed.connect(self.sign_node) - scene.node_transaction.connect(self.send_money_to_node) - scene.node_contact.connect(self.add_node_as_contact) - scene.node_member.connect(self.identity_informations) - scene.node_copy_pubkey.connect(self.copy_node_pubkey) + scene.node_context_menu_requested.connect(self.node_context_menu) @once_at_a_time @asyncify @@ -147,59 +150,18 @@ class GraphTabWidget(QWidget): """ pass - def identity_informations(self, pubkey, metadata): - identity = self.app.identities_registry.from_handled_data( - metadata['text'], - pubkey, - None, - BlockchainState.VALIDATED, - self.community - ) - dialog = MemberDialog(self.app, self.account, self.community, identity) - dialog.exec_() - @asyncify - async def sign_node(self, pubkey, metadata): - identity = self.app.identities_registry.from_handled_data( - metadata['text'], - pubkey, - None, - BlockchainState.VALIDATED, - self.community - ) - await CertificationDialog.certify_identity(self.app, self.account, self.password_asker, - self.community, identity) + async def node_context_menu(self, pubkey): + """ + Open the node context menu + :param str pubkey: the pubkey of the node to open + """ + identity = await self.app.identities_registry.future_find(pubkey, self.community) + menu = ContextMenu.from_data(self.widget, self.app, self.account, self.community, self.password_asker, + (identity,)) - @asyncify - async def send_money_to_node(self, pubkey, metadata): - identity = self.app.identities_registry.from_handled_data( - metadata['text'], - pubkey, - None, - BlockchainState.VALIDATED, - self.community - ) - result = await TransferMoneyDialog.send_money_to_identity(self.app, self.account, self.password_asker, - self.community, identity) - if result == QDialog.Accepted: - self.money_sent.emit() - - def copy_node_pubkey(self, pubkey): - cb = self.app.qapp.clipboard() - cb.clear(mode=cb.Clipboard) - cb.setText(pubkey, mode=cb.Clipboard) - - def add_node_as_contact(self, pubkey, metadata): - # check if contact already exists... - if pubkey == self.account.pubkey \ - or pubkey in [contact['pubkey'] for contact in self.account.contacts]: - return False - dialog = ConfigureContactDialog(self.account, self.window(), {'name': metadata['text'], - 'pubkey': pubkey, - }) - result = dialog.exec_() - if result == QDialog.Accepted: - self.window().refresh_contacts() + # Show the context menu. + menu.qmenu.popup(QCursor.pos()) def changeEvent(self, event): """ diff --git a/src/sakia/gui/graphs/wot_tab.py b/src/sakia/gui/graphs/wot_tab.py index ce12bd290e659bde210edc643d3f2d9b80427e2b..62bc74f8b447bde1c3b0181be6e3cc73e0bde702 100644 --- a/src/sakia/gui/graphs/wot_tab.py +++ b/src/sakia/gui/graphs/wot_tab.py @@ -1,7 +1,8 @@ import logging import asyncio -from PyQt5.QtCore import QEvent, pyqtSignal, QT_TRANSLATE_NOOP +from PyQt5.QtCore import QEvent, pyqtSignal, QT_TRANSLATE_NOOP, QObject +from PyQt5.QtWidgets import QWidget from ...tools.decorators import asyncify, once_at_a_time, cancel_once_task from ...core.graph import WoTGraph from ...gen_resources.wot_tab_uic import Ui_WotTabWidget @@ -9,31 +10,39 @@ from ...gui.widgets.busy import Busy from .graph_tab import GraphTabWidget -class WotTabWidget(GraphTabWidget, Ui_WotTabWidget): +class WotTabWidget(GraphTabWidget): money_sent = pyqtSignal() - def __init__(self, app): + def __init__(self, app, account=None, community=None, password_asker=None, widget=QWidget, view=Ui_WotTabWidget): """ :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 + :param class widget: The class of the PyQt5 widget used for this tab + :param class view: The class of the UI View for this tab """ - super().__init__(app) + super().__init__(app, account, community, password_asker, widget) # construct from qtDesigner - self.setupUi(self) - self.search_user_widget.init(app) - self.busy = Busy(self.graphicsView) + self.ui = view() + self.ui.setupUi(self.widget) + + self.ui.search_user_widget.init(app) + self.busy = Busy(self.ui.graphicsView) self.busy.hide() - self.set_scene(self.graphicsView.scene()) + self.set_scene(self.ui.graphicsView.scene()) - self.account = None - self.community = None - self.password_asker = None + self.account = account + self.community = community + self.password_asker = password_asker self.app = app self.draw_task = None - self.search_user_widget.identity_selected.connect(self.draw_graph) - self.search_user_widget.reset.connect(self.reset) + self.ui.search_user_widget.identity_selected.connect(self.draw_graph) + self.ui.search_user_widget.reset.connect(self.reset) # create node metadata from account self._current_identity = None @@ -45,13 +54,13 @@ class WotTabWidget(GraphTabWidget, Ui_WotTabWidget): def change_account(self, account, password_asker): self.cancel_once_tasks() - self.search_user_widget.change_account(account) + self.ui.search_user_widget.change_account(account) self.account = account self.password_asker = password_asker def change_community(self, community): self.cancel_once_tasks() - self.search_user_widget.change_community(community) + self.ui.search_user_widget.change_community(community) self._auto_refresh(community) self.community = community self.reset() @@ -87,14 +96,14 @@ class WotTabWidget(GraphTabWidget, Ui_WotTabWidget): graph = WoTGraph(self.app, self.community) await graph.initialize(identity, identity_account) # draw graph in qt scene - self.graphicsView.scene().update_wot(graph.nx_graph, identity) + self.ui.graphicsView.scene().update_wot(graph.nx_graph, identity) # if selected member is not the account member... if identity.pubkey != identity_account.pubkey: # add path from selected member to account member path = await graph.get_shortest_path_to_identity(identity_account, identity) if path: - self.graphicsView.scene().update_path(graph.nx_graph, path) + self.ui.graphicsView.scene().update_path(graph.nx_graph, path) self.busy.hide() @once_at_a_time diff --git a/src/sakia/gui/views/nodes/base_node.py b/src/sakia/gui/views/nodes/base_node.py index f486d1c556f02bb0d5876a8b953e0ebda91bd42e..ec41cb7f328f9a40554a59bbb34bcf98cd87e2bf 100644 --- a/src/sakia/gui/views/nodes/base_node.py +++ b/src/sakia/gui/views/nodes/base_node.py @@ -3,7 +3,7 @@ from PyQt5.QtWidgets import QGraphicsEllipseItem, \ QGraphicsSceneContextMenuEvent from PyQt5.QtCore import Qt, QCoreApplication, QT_TRANSLATE_NOOP, pyqtSignal from PyQt5.QtGui import QMouseEvent -from sakia.core.graph.constants import NodeStatus +from ....core.graph.constants import NodeStatus class BaseNode(QGraphicsEllipseItem): @@ -65,68 +65,4 @@ class BaseNode(QGraphicsEllipseItem): # no menu on the wallet node if self.status_wallet: return None - # create node context menus - self.menu = QMenu() - # action show member - QT_TRANSLATE_NOOP('WoT.Node', 'Informations') - self.action_show_member = QAction(QCoreApplication.translate('WoT.Node', 'Informations'), self.scene()) - self.menu.addAction(self.action_show_member) - self.action_show_member.triggered.connect(self.member_action) - # action add identity as contact - QT_TRANSLATE_NOOP('WoT.Node', 'Add as contact') - self.action_contact = QAction(QCoreApplication.translate('WoT.Node', 'Add as contact'), self.scene()) - self.menu.addAction(self.action_contact) - self.action_contact.triggered.connect(self.contact_action) - # action transaction toward identity - QT_TRANSLATE_NOOP('WoT.Node', 'Send money') - self.action_transaction = QAction(QCoreApplication.translate('WoT.Node', 'Send money'), self.scene()) - self.menu.addAction(self.action_transaction) - self.action_transaction.triggered.connect(self.transaction_action) - # action sign identity - QT_TRANSLATE_NOOP('WoT.Node', 'Certify identity') - self.action_sign = QAction(QCoreApplication.translate('WoT.Node', 'Certify identity'), self.scene()) - self.menu.addAction(self.action_sign) - self.action_sign.triggered.connect(self.sign_action) - # action copy identity pubkey - QT_TRANSLATE_NOOP('WoT.Node', 'Copy pubkey') - self.action_copy = QAction(QCoreApplication.translate('WoT.Node', 'Copy pubkey'), self.scene()) - self.menu.addAction(self.action_copy) - self.action_copy.triggered.connect(self.copy_action) - - # run menu - self.menu.exec(event.screenPos()) - - def member_action(self): - """ - Transaction action to identity node - """ - # trigger scene signal - self.scene().node_member.emit(self.id, self.metadata) - - def contact_action(self): - """ - Transaction action to identity node - """ - # trigger scene signal - self.scene().node_contact.emit(self.id, self.metadata) - - def sign_action(self): - """ - Sign identity node - """ - # trigger scene signal - self.scene().node_signed.emit(self.id, self.metadata) - - def copy_action(self): - """ - Copy identity node pubkey - """ - # trigger scene signal - self.scene().node_copy_pubkey.emit(self.id) - - def transaction_action(self): - """ - Transaction action to identity node - """ - # trigger scene signal - self.scene().node_transaction.emit(self.id, self.metadata) + self.scene().node_context_menu_requested.emit(self.id) diff --git a/src/sakia/gui/views/scenes/base_scene.py b/src/sakia/gui/views/scenes/base_scene.py index 7a4762b0c4926d4d9fc473c06b840c789828c3c6..ec8d59c45490bb4571021d2fce09aee9de6bb14d 100644 --- a/src/sakia/gui/views/scenes/base_scene.py +++ b/src/sakia/gui/views/scenes/base_scene.py @@ -1,16 +1,11 @@ from PyQt5.QtCore import pyqtSignal -from PyQt5.QtWidgets import QGraphicsScene +from PyQt5.QtWidgets import QGraphicsScene, QGraphicsSceneContextMenuEvent class BaseScene(QGraphicsScene): # This defines signals taking string arguments - node_clicked = pyqtSignal(str, dict) - node_signed = pyqtSignal(str, dict) - node_transaction = pyqtSignal(str, dict) - node_contact = pyqtSignal(str, dict) - node_member = pyqtSignal(str, dict) - node_copy_pubkey = pyqtSignal(str) + node_context_menu_requested = pyqtSignal(str) node_hovered = pyqtSignal(str) def __init__(self, parent=None): - super().__init__(parent) \ No newline at end of file + super().__init__(parent) diff --git a/src/sakia/gui/views/wot.py b/src/sakia/gui/views/wot.py index c4e874e971f68cad026eacfe17d819c1c3d657a5..b8ed76f58844a68d0a1aaf5226a893f00b2c970c 100644 --- a/src/sakia/gui/views/wot.py +++ b/src/sakia/gui/views/wot.py @@ -1,11 +1,9 @@ import networkx from PyQt5.QtCore import Qt, QPoint, pyqtSignal from PyQt5.QtGui import QPainter, QWheelEvent -from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene - -from .edges import WotEdge -from .nodes import WotNode +from PyQt5.QtWidgets import QGraphicsView, QGraphicsSceneContextMenuEvent from .scenes import WotScene +from ..widgets import ContextMenu class WotView(QGraphicsView): @@ -15,7 +13,7 @@ class WotView(QGraphicsView): :param parent: [Optional, default=None] Parent widget """ - super(WotView, self).__init__(parent) + super().__init__(parent) self.setScene(WotScene(self)) @@ -44,4 +42,4 @@ class WotView(QGraphicsView): # act normally on scrollbar else: # transmit event to parent class wheelevent - super().wheelEvent(event) + super().wheelEvent(event) \ No newline at end of file diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index 8cb327cc69a86a572d48a524ec50b389dd13dd2f..c66e6d0c6c0d5917ff4d8148ee15c4f244c65195 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -34,7 +34,7 @@ class ContextMenu(QObject): :param ContextMenu menu: the qmenu to add actions to :param Identity identity: the identity """ - menu.qmenu.addSeparator().setText(menu.qmenu.tr("Identity")) + menu.qmenu.addSeparator().setText(identity.uid) informations = QAction(menu.qmenu.tr("Informations"), menu.qmenu.parent()) informations.triggered.connect(lambda checked, i=identity: menu.informations(i)) diff --git a/src/sakia/gui/widgets/search_user.py b/src/sakia/gui/widgets/search_user.py index 94114755631209cefe8ea8551beecb72cc4d0a59..994d37f673470f06492a8112d6f71e07739b68e4 100644 --- a/src/sakia/gui/widgets/search_user.py +++ b/src/sakia/gui/widgets/search_user.py @@ -77,6 +77,15 @@ class SearchUserWidget(QWidget, Ui_SearchUserWidget): self.combobox_search.addItem(uid) self.blockSignals(False) self.combobox_search.showPopup() + except ValueError as e: + if '404' in str(e): + self.nodes = list() + self.blockSignals(True) + self.combobox_search.clear() + self.blockSignals(False) + self.combobox_search.showPopup() + else: + pass except NoPeerAvailable: pass