From 2a8469e0bc16005204822e8a4b8950d0c8f1370d Mon Sep 17 00:00:00 2001 From: inso <insomniak.fr@gmaiL.com> Date: Mon, 8 Aug 2016 19:05:38 +0200 Subject: [PATCH] New search_user component --- res/ui/certification.ui | 21 --- res/ui/transfer.ui | 21 --- src/sakia/core/app.py | 3 +- src/sakia/gui/graphs/explorer/controller.py | 4 +- src/sakia/gui/graphs/explorer/explorer.ui | 9 -- src/sakia/gui/graphs/wot/wot_tab.ui | 11 +- src/sakia/gui/network/model.py | 2 +- src/sakia/gui/network/table_model.py | 1 + src/sakia/gui/search_user/__init__.py | 0 src/sakia/gui/search_user/controller.py | 63 +++++++++ src/sakia/gui/search_user/model.py | 85 +++++++++++ .../sakia/gui/search_user/search_user.ui | 0 src/sakia/gui/search_user/view.py | 64 +++++++++ src/sakia/gui/toolbar/controller.py | 22 --- src/sakia/gui/txhistory/model.py | 2 +- src/sakia/gui/txhistory/view.py | 6 +- src/sakia/gui/widgets/search_user.py | 132 ------------------ 17 files changed, 223 insertions(+), 223 deletions(-) create mode 100644 src/sakia/gui/search_user/__init__.py create mode 100644 src/sakia/gui/search_user/controller.py create mode 100644 src/sakia/gui/search_user/model.py rename res/ui/search_user_view.ui => src/sakia/gui/search_user/search_user.ui (100%) create mode 100644 src/sakia/gui/search_user/view.py delete mode 100644 src/sakia/gui/widgets/search_user.py diff --git a/res/ui/certification.ui b/res/ui/certification.ui index 59f527b5..cd0c6b75 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 71e07789..018f7f3e 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 b1b922c2..3f4397b1 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 e116ab34..605708eb 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 9b3c7087..b7609502 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 22ee6350..70b8a9f4 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 443839dc..f337e428 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 6bb77668..6d698795 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 00000000..e69de29b diff --git a/src/sakia/gui/search_user/controller.py b/src/sakia/gui/search_user/controller.py new file mode 100644 index 00000000..497912d0 --- /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 00000000..aa53a704 --- /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 00000000..fee440ee --- /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 00e2c077..6e2ebd9f 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 c516b4d6..b7666644 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 3f969bd1..44c69085 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 21860415..00000000 --- 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) -- GitLab