diff --git a/res/ui/certification.ui b/res/ui/certification.ui index 59f527b542ae9c55862bc1c29a8ae8438b2aa1c7..cd0c6b75abc4b98e6f27c6b98ac265d1f71e99fd 100644 --- a/res/ui/certification.ui +++ b/res/ui/certification.ui @@ -194,19 +194,6 @@ </property> </spacer> </item> - <item> - <widget class="SearchUserWidget" name="search_user" native="true"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> </layout> </item> </layout> @@ -226,14 +213,6 @@ </item> </layout> </widget> - <customwidgets> - <customwidget> - <class>SearchUserWidget</class> - <extends>QWidget</extends> - <header>sakia.gui.widgets.search_user</header> - <container>1</container> - </customwidget> - </customwidgets> <resources/> <connections/> <slots> diff --git a/res/ui/transfer.ui b/res/ui/transfer.ui index 71e077897ca067494ae853a3f50c3981e09a86ac..018f7f3e4603839da12d1cb2a103ae7d7dc18c6c 100644 --- a/res/ui/transfer.ui +++ b/res/ui/transfer.ui @@ -173,19 +173,6 @@ </property> </spacer> </item> - <item> - <widget class="SearchUserWidget" name="search_user" native="true"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> </layout> </item> </layout> @@ -314,14 +301,6 @@ </item> </layout> </widget> - <customwidgets> - <customwidget> - <class>SearchUserWidget</class> - <extends>QWidget</extends> - <header>sakia.gui.widgets.search_user</header> - <container>1</container> - </customwidget> - </customwidgets> <resources/> <connections/> <slots> diff --git a/src/sakia/core/app.py b/src/sakia/core/app.py index b1b922c29d3b762895b6f2690ede0bdca2582cce..3f4397b12a14c63c62159c5df16223ee71579b51 100644 --- a/src/sakia/core/app.py +++ b/src/sakia/core/app.py @@ -99,7 +99,8 @@ class Application(QObject): app.save_preferences(app.preferences) # open it logging.debug("No default account in preferences. Set %s as default account." % names[0]) - + if app._current_account: + app._current_account.start_coroutines() return app def set_proxy(self): diff --git a/src/sakia/gui/graphs/explorer/controller.py b/src/sakia/gui/graphs/explorer/controller.py index e116ab342ede338c32ca2af9c8f40fa5eec8002b..605708eb9077dfb91185204b8fb99c65b826826c 100644 --- a/src/sakia/gui/graphs/explorer/controller.py +++ b/src/sakia/gui/graphs/explorer/controller.py @@ -19,7 +19,7 @@ class ExplorerController(BaseGraphController): super().__init__(parent, view, model, password_asker) self.set_scene(view.scene()) self.reset() - self.view.button_go.clicked.connect(self.refresh) + self.view.button_go.clicked.connect(lambda checked: self.refresh()) @property def view(self) -> ExplorerView: @@ -60,7 +60,7 @@ class ExplorerController(BaseGraphController): Refresh graph scene to current metadata """ self.model.graph.stop_exploration() - self.draw_graph(self.model.identity) + await self.draw_graph(self.model.identity) self.view.update_wot(self.model.graph.nx_graph, self.model.identity) @once_at_a_time diff --git a/src/sakia/gui/graphs/explorer/explorer.ui b/src/sakia/gui/graphs/explorer/explorer.ui index 9b3c7087a99704e5f553bff9cc96f518bbd81c1f..b76095027c51e6fbac268e081a010a973a7a3abf 100644 --- a/src/sakia/gui/graphs/explorer/explorer.ui +++ b/src/sakia/gui/graphs/explorer/explorer.ui @@ -14,9 +14,6 @@ <string>Form</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="SearchUserWidget" name="search_user_widget" native="true"/> - </item> <item> <widget class="ExplorerGraphicsView" name="graphics_view"> <property name="viewportUpdateMode"> @@ -76,12 +73,6 @@ <extends>QGraphicsView</extends> <header>sakia.gui.graphs.explorer.graphics_view</header> </customwidget> - <customwidget> - <class>SearchUserWidget</class> - <extends>QWidget</extends> - <header>sakia.gui.widgets.search_user</header> - <container>1</container> - </customwidget> </customwidgets> <resources> <include location="../../../../../res/icons/icons.qrc"/> diff --git a/src/sakia/gui/graphs/wot/wot_tab.ui b/src/sakia/gui/graphs/wot/wot_tab.ui index 22ee6350fc0fbfb0f85e32d4b46891bd1702bbc8..70b8a9f412b5eb014c6f5a28cc3cd1331a25f8f6 100644 --- a/src/sakia/gui/graphs/wot/wot_tab.ui +++ b/src/sakia/gui/graphs/wot/wot_tab.ui @@ -14,16 +14,13 @@ <string>Form</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0" colspan="2"> + <item row="0" column="0" colspan="2"> <widget class="WotGraphicsView" name="graphics_view"> <property name="viewportUpdateMode"> <enum>QGraphicsView::BoundingRectViewportUpdate</enum> </property> </widget> </item> - <item row="0" column="0" colspan="2"> - <widget class="SearchUserWidget" name="search_user_widget" native="true"/> - </item> </layout> </widget> <customwidgets> @@ -32,12 +29,6 @@ <extends>QGraphicsView</extends> <header>sakia.gui.graphs.wot.graphics_view</header> </customwidget> - <customwidget> - <class>SearchUserWidget</class> - <extends>QWidget</extends> - <header>sakia.gui.widgets.search_user</header> - <container>1</container> - </customwidget> </customwidgets> <resources> <include location="../../../../../res/icons/icons.qrc"/> diff --git a/src/sakia/gui/network/model.py b/src/sakia/gui/network/model.py index 443839dc2774519769a023e17f23549ab1d42964..f337e4289d0e1fad01d283b703c0c3253f4b0b75 100644 --- a/src/sakia/gui/network/model.py +++ b/src/sakia/gui/network/model.py @@ -12,7 +12,7 @@ class NetworkModel(ComponentModel): """ Constructor of an network model - :param sakia.gui.component.controller.NetworkController parent: the controller + :param sakia.gui.network.controller.NetworkController parent: the controller :param sakia.core.Application app: the app :param sakia.core.Account account: the account :param sakia.core.Community community: the community diff --git a/src/sakia/gui/network/table_model.py b/src/sakia/gui/network/table_model.py index 6bb77668b8084d08425166e57ec441287fdcb535..6d6987956fd3ea263be5f752f4c7e10336290deb 100644 --- a/src/sakia/gui/network/table_model.py +++ b/src/sakia/gui/network/table_model.py @@ -163,6 +163,7 @@ class NetworkTableModel(QAbstractTableModel): Node.CORRUPTED: lambda: self.tr('Corrupted') } self.nodes_data = [] + self.community.network.nodes_changed.connect(self.refresh_nodes) async def data_node(self, node: Node) -> tuple: """ diff --git a/src/sakia/gui/search_user/__init__.py b/src/sakia/gui/search_user/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/sakia/gui/search_user/controller.py b/src/sakia/gui/search_user/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..497912d087e1433c14d086f7f1279850850a1ae8 --- /dev/null +++ b/src/sakia/gui/search_user/controller.py @@ -0,0 +1,63 @@ +from PyQt5.QtCore import pyqtSignal +from ..component.controller import ComponentController +from .model import SearchUserModel +from .view import SearchUserView +from ...tools.decorators import asyncify +from sakia.core.registry import Identity + + +class SearchUserController(ComponentController): + """ + The navigation panel + """ + search_started = pyqtSignal() + search_ended = pyqtSignal() + identity_selected = pyqtSignal(Identity) + + def __init__(self, parent, view, model): + """ + :param sakia.gui.agent.controller.AgentController parent: the parent + :param sakia.gui.search_user.view.SearchUserView view: + :param sakia.gui.search_user.model.SearchUserModel model: + """ + super().__init__(parent, view, model) + self.view.search_requested.connect(self.search) + self.view.node_selected.connect(self.select_node) + + @classmethod + def create(cls, parent, app, **kwargs): + account = kwargs['account'] + community = kwargs['community'] + + view = SearchUserView(parent.view) + model = SearchUserModel(app, account, community) + search_user = cls(parent, view, model) + model.setParent(search_user) + return search_user + + @property + def view(self) -> SearchUserView: + return self._view + + @property + def model(self) -> SearchUserModel: + return self._model + + @asyncify + async def search(self, text): + """ + Search for a user + :param text: + :return: + """ + if len(text) > 2: + await self.model.find_user(text) + user_nodes = self.model.user_nodes() + self.view.set_search_result(text, user_nodes) + + def select_node(self, index): + """ + Select node in graph when item is selected in combobox + """ + self.model.select_identity(index) + self.identity_selected.emit(self.model.identity()) \ No newline at end of file diff --git a/src/sakia/gui/search_user/model.py b/src/sakia/gui/search_user/model.py new file mode 100644 index 0000000000000000000000000000000000000000..aa53a704af70027e3494f5f5c38a7e2dada5343f --- /dev/null +++ b/src/sakia/gui/search_user/model.py @@ -0,0 +1,85 @@ +from sakia.core.registry import BlockchainState +from ..component.model import ComponentModel +from duniterpy.api import errors +from sakia.tools.exceptions import NoPeerAvailable + +import logging + + +class SearchUserModel(ComponentModel): + """ + The model of Navigation component + """ + + def __init__(self, parent, app, account, community): + """ + + :param sakia.gui.search_user.controller.NetworkController parent: the controller + :param sakia.core.Application app: the app + :param sakia.core.Account account: the account + :param sakia.core.Community community: the community + """ + super().__init__(parent) + self.app = app + self.account = account + self.community = community + self._nodes = list() + self._current_identity = None + + def identity(self): + """ + Get current identity selected + :rtype: sakia.core.registry.Identity + """ + return self._current_identity + + def user_nodes(self): + """ + Gets user nodes + :return: + """ + return self._nodes + + async def find_user(self, text): + """ + Search for a user + :param text: + :return: + """ + try: + response = await self.community.bma_access.future_request(bma.wot.Lookup, {'search': text}) + + nodes = {} + for identity in response['results']: + nodes[identity['pubkey']] = identity['uids'][0]['uid'] + + if nodes: + self._nodes = list() + for pubkey, uid in nodes.items(): + self._nodes.append({'pubkey': pubkey, 'uid': uid}) + except errors.DuniterError as e: + if e.ucode == errors.NO_MATCHING_IDENTITY: + self._nodes = list() + else: + logging.debug(str(e)) + except NoPeerAvailable as e: + logging.debug(str(e)) + + def select_identity(self, index): + """ + Select an identity from a node index + :param index: + :return: + """ + if index < 0 or index >= len(self.nodes): + self._current_identity = None + return False + node = self.nodes[index] + metadata = {'id': node['pubkey'], 'text': node['uid']} + self._current_identity = self.app.identities_registry.from_handled_data( + metadata['text'], + metadata['id'], + None, + BlockchainState.VALIDATED, + self.community + ) \ No newline at end of file diff --git a/res/ui/search_user_view.ui b/src/sakia/gui/search_user/search_user.ui similarity index 100% rename from res/ui/search_user_view.ui rename to src/sakia/gui/search_user/search_user.ui diff --git a/src/sakia/gui/search_user/view.py b/src/sakia/gui/search_user/view.py new file mode 100644 index 0000000000000000000000000000000000000000..fee440ee6ba3adde52fda4d78ea5c9e3f08e0aa0 --- /dev/null +++ b/src/sakia/gui/search_user/view.py @@ -0,0 +1,64 @@ +from PyQt5.QtWidgets import QWidget, QComboBox +from PyQt5.QtCore import QT_TRANSLATE_NOOP, pyqtSignal, Qt +from .search_user_uic import Ui_SearchUserWidget + + +class SearchUserView(QWidget, Ui_SearchUserWidget): + """ + The model of Navigation component + """ + _search_placeholder = QT_TRANSLATE_NOOP("SearchUserWidget", "Research a pubkey, an uid...") + search_requested = pyqtSignal(str) + reset_requested = pyqtSignal() + node_selected = pyqtSignal() + + def __init__(self, parent): + # construct from qtDesigner + super().__init__(parent) + self.setupUi(self) + # Default text when combo lineEdit is empty + self.combobox_search.lineEdit().setPlaceholderText(self.tr(SearchUserView._search_placeholder)) + # add combobox events + self.combobox_search.lineEdit().returnPressed.connect(self.search) + self.button_reset.clicked.connect(self.reset_requested) + # To fix a recall of the same item with different case, + # the edited text is not added in the item list + self.combobox_search.setInsertPolicy(QComboBox.NoInsert) + self.combobox_search.activated.connect(self.node_selected) + + def search(self): + """ + Search nodes when return is pressed in combobox lineEdit + """ + text = self.combobox_search.lineEdit().text() + self.combobox_search.lineEdit().clear() + self.combobox_search.lineEdit().setPlaceholderText(self.tr("Looking for {0}...".format(text))) + self.search_requested.emit(text) + + def set_search_result(self, text, nodes): + """ + Set the list of users displayed in the combo box + :param str text: the text of the search + :param list[str] nodes: the list of users found + """ + self.blockSignals(True) + self.combobox_search.clear() + if len(nodes) > 0: + self.combobox_search.lineEdit().setText(text) + for uid in nodes: + self.combobox_search.addItem(uid) + self.blockSignals(False) + self.combobox_search.showPopup() + + def retranslateUi(self, widget): + """ + Retranslate missing widgets from generated code + """ + self.combobox_search.lineEdit().setPlaceholderText(self.tr(SearchUserView._search_placeholder)) + super().retranslateUi(self) + + def keyPressEvent(self, event): + if event.key() == Qt.Key_Return: + return + + super().keyPressEvent(event) diff --git a/src/sakia/gui/toolbar/controller.py b/src/sakia/gui/toolbar/controller.py index 00e2c0772d572ca259c5289c3284b4974c5dc652..6e2ebd9f6544ad06b469e53c5dd4d2fe9e132d5d 100644 --- a/src/sakia/gui/toolbar/controller.py +++ b/src/sakia/gui/toolbar/controller.py @@ -50,28 +50,6 @@ class ToolbarController(ComponentController): @property def model(self) -> ToolbarModel: return self._model - - def cancel_once_tasks(self): - cancel_once_task(self, self.refresh_block) - cancel_once_task(self, self.refresh_status) - logging.debug("Cancelled status") - cancel_once_task(self, self.refresh_quality_buttons) - - def change_account(self, account, password_asker): - self.cancel_once_tasks() - self.account = account - self.password_asker = password_asker - - def change_community(self, community): - self.cancel_once_tasks() - - logging.debug("Changed community to {0}".format(community)) - self.button_membership.setText(self.tr("Membership")) - self.button_membership.setEnabled(False) - self.button_certification.setEnabled(False) - self.action_publish_uid.setEnabled(False) - self.community = community - self.refresh_quality_buttons() @asyncify async def action_save_revokation(self, checked=False): diff --git a/src/sakia/gui/txhistory/model.py b/src/sakia/gui/txhistory/model.py index c516b4d6961dbc32c012733e415958dbc9c75923..b7666644e1976b860b1dcaeb2a78a1d116a3ef57 100644 --- a/src/sakia/gui/txhistory/model.py +++ b/src/sakia/gui/txhistory/model.py @@ -76,7 +76,7 @@ class TxHistoryModel(ComponentModel): def stop_progress(self, community, received_list): if community == self.community: self.loading_progressed.emit(100, 100) - self.model.refresh_transfers() + self.table_model.sourceModel().refresh_transfers() self.parent().notification_reception(received_list) async def minimum_maximum_datetime(self): diff --git a/src/sakia/gui/txhistory/view.py b/src/sakia/gui/txhistory/view.py index 3f969bd1d26d67b335905e2a4af60dd4340122f9..44c69085c60df8e544a78b363dd357b405c82107 100644 --- a/src/sakia/gui/txhistory/view.py +++ b/src/sakia/gui/txhistory/view.py @@ -71,9 +71,9 @@ class TxHistoryView(QWidget, Ui_TxHistoryWidget): if value >= maximum: self.progressbar.hide() else: - self.view.progressbar.show() - self.view.progressbar.setValue(value) - self.view.progressbar.setMaximum(maximum) + self.progressbar.show() + self.progressbar.setValue(value) + self.progressbar.setMaximum(maximum) def resizeEvent(self, event): self.busy_balance.resize(event.size()) diff --git a/src/sakia/gui/widgets/search_user.py b/src/sakia/gui/widgets/search_user.py deleted file mode 100644 index 21860415ac5cf5e67bf5d29e284d96fd9aa5eb7a..0000000000000000000000000000000000000000 --- a/src/sakia/gui/widgets/search_user.py +++ /dev/null @@ -1,132 +0,0 @@ -import logging - -from PyQt5.QtCore import QEvent, pyqtSignal, QT_TRANSLATE_NOOP, Qt -from PyQt5.QtWidgets import QComboBox, QWidget - -from duniterpy.api import bma, errors - -from ...tools.decorators import asyncify -from ...tools.exceptions import NoPeerAvailable -from ...core.registry import BlockchainState, Identity -from ...presentation.search_user_view_uic import Ui_SearchUserWidget - - -class SearchUserWidget(QWidget, Ui_SearchUserWidget): - _search_placeholder = QT_TRANSLATE_NOOP("SearchUserWidget", "Research a pubkey, an uid...") - - identity_selected = pyqtSignal(Identity) - search_started = pyqtSignal() - search_completed = pyqtSignal() - reset = pyqtSignal() - - def __init__(self, parent): - """ - :param sakia.core.app.Application app: Application instance - """ - # construct from qtDesigner - super().__init__(parent) - self.setupUi(self) - # Default text when combo lineEdit is empty - self.combobox_search.lineEdit().setPlaceholderText(self.tr(SearchUserWidget._search_placeholder)) - # add combobox events - self.combobox_search.lineEdit().returnPressed.connect(self.search) - # To fix a recall of the same item with different case, - # the edited text is not added in the item list - self.combobox_search.setInsertPolicy(QComboBox.NoInsert) - self.combobox_search.activated.connect(self.select_node) - self.button_reset.clicked.connect(self.reset) - self.nodes = list() - self.community = None - self.account = None - self.app = None - self._current_identity = None - - def current_identity(self): - return self._current_identity - - def init(self, app): - """ - Initialize the widget - :param sakia.core.Application app: the application - """ - self.app = app - - def change_account(self, account): - self.account = account - - def change_community(self, community): - self.community = community - - @asyncify - async def search(self): - """ - Search nodes when return is pressed in combobox lineEdit - """ - self.search_started.emit() - text = self.combobox_search.lineEdit().text() - self.combobox_search.lineEdit().clear() - self.combobox_search.lineEdit().setPlaceholderText(self.tr("Looking for {0}...".format(text))) - - if len(text) > 2: - try: - response = await self.community.bma_access.future_request(bma.wot.Lookup, {'search': text}) - - nodes = {} - for identity in response['results']: - nodes[identity['pubkey']] = identity['uids'][0]['uid'] - - if nodes: - self.nodes = list() - self.blockSignals(True) - self.combobox_search.clear() - self.combobox_search.lineEdit().setText(text) - for pubkey, uid in nodes.items(): - self.nodes.append({'pubkey': pubkey, 'uid': uid}) - self.combobox_search.addItem(uid) - self.blockSignals(False) - self.combobox_search.showPopup() - except errors.DuniterError as e: - if e.ucode == errors.NO_MATCHING_IDENTITY: - self.nodes = list() - self.blockSignals(True) - self.combobox_search.clear() - self.blockSignals(False) - self.combobox_search.showPopup() - else: - pass - except NoPeerAvailable: - pass - self.search_completed.emit() - - def select_node(self, index): - """ - Select node in graph when item is selected in combobox - """ - if index < 0 or index >= len(self.nodes): - self._current_identity = None - return False - node = self.nodes[index] - metadata = {'id': node['pubkey'], 'text': node['uid']} - self._current_identity = self.app.identities_registry.from_handled_data( - metadata['text'], - metadata['id'], - None, - BlockchainState.VALIDATED, - self.community - ) - self.identity_selected.emit( - self._current_identity - ) - - def retranslateUi(self, widget): - """ - Retranslate missing widgets from generated code - """ - self.combobox_search.lineEdit().setPlaceholderText(self.tr(SearchUserWidget._search_placeholder)) - super().retranslateUi(self) - - def keyPressEvent(self, event): - if event.key() == Qt.Key_Return: - return - - super().keyPressEvent(event)