From 0a187a3aa58d326349d6c3415a031f0f68fdb84a Mon Sep 17 00:00:00 2001 From: jaime02 Date: Tue, 15 Feb 2022 12:10:46 +0100 Subject: example: add google suggest Ported from C++ Task-number: PYSIDE-841 Pick-to: 6.2 6.3 Change-Id: Ib64218925961f3d0bbd783c5bb9d8365a81e8767 Reviewed-by: Cristian Maureira-Fredes --- .../network/googlesuggest/doc/googlesuggest.png | Bin 0 -> 2467 bytes .../network/googlesuggest/doc/googlesuggest.rst | 11 ++ examples/network/googlesuggest/googlesuggest.py | 137 +++++++++++++++++++++ .../network/googlesuggest/googlesuggest.pyproject | 3 + examples/network/googlesuggest/main.py | 14 +++ examples/network/googlesuggest/searchbox.py | 27 ++++ 6 files changed, 192 insertions(+) create mode 100644 examples/network/googlesuggest/doc/googlesuggest.png create mode 100644 examples/network/googlesuggest/doc/googlesuggest.rst create mode 100644 examples/network/googlesuggest/googlesuggest.py create mode 100644 examples/network/googlesuggest/googlesuggest.pyproject create mode 100644 examples/network/googlesuggest/main.py create mode 100644 examples/network/googlesuggest/searchbox.py (limited to 'examples') diff --git a/examples/network/googlesuggest/doc/googlesuggest.png b/examples/network/googlesuggest/doc/googlesuggest.png new file mode 100644 index 000000000..cb2e91d4d Binary files /dev/null and b/examples/network/googlesuggest/doc/googlesuggest.png differ diff --git a/examples/network/googlesuggest/doc/googlesuggest.rst b/examples/network/googlesuggest/doc/googlesuggest.rst new file mode 100644 index 000000000..ada224311 --- /dev/null +++ b/examples/network/googlesuggest/doc/googlesuggest.rst @@ -0,0 +1,11 @@ +Google Suggest Example +====================== + +Obtains the list of search recommendations by the Google search engine. + +The example uses the QNetworkAccessManager to obtain the list of search +recommendations by Google as the user types into a QLineEdit. + +.. image:: googlesuggest.png + :width: 502 + :alt: google suggest program screenshot diff --git a/examples/network/googlesuggest/googlesuggest.py b/examples/network/googlesuggest/googlesuggest.py new file mode 100644 index 000000000..6dac0eb22 --- /dev/null +++ b/examples/network/googlesuggest/googlesuggest.py @@ -0,0 +1,137 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from typing import List + +from PySide6.QtCore import (QByteArray, QEvent, QObject, QPoint, Qt, QTimer, + QXmlStreamReader, Slot) +from PySide6.QtGui import QPalette +from PySide6.QtNetwork import (QNetworkAccessManager, QNetworkReply, + QNetworkRequest) +from PySide6.QtWidgets import QFrame, QTreeWidget, QTreeWidgetItem + + +class GSuggestCompletion(QObject): + def __init__(self, parent=None): + super().__init__(parent) + self.editor = parent + self.popup = QTreeWidget() + self.popup.setWindowFlags(Qt.Popup) + self.popup.setFocusPolicy(Qt.NoFocus) + self.popup.setFocusProxy(parent) + self.popup.setMouseTracking(True) + + self.popup.setColumnCount(1) + self.popup.setUniformRowHeights(True) + self.popup.setRootIsDecorated(False) + self.popup.setEditTriggers(QTreeWidget.NoEditTriggers) + self.popup.setSelectionBehavior(QTreeWidget.SelectRows) + self.popup.setFrameStyle(QFrame.Box | QFrame.Plain) + self.popup.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.popup.header().hide() + + self.popup.installEventFilter(self) + + self.popup.itemClicked.connect(self.done_completion) + + self.timer = QTimer() + self.timer.setSingleShot(True) + self.timer.setInterval(500) + self.timer.timeout.connect(self.auto_suggest) + self.editor.textEdited.connect(self.timer.start) + + self.network_manager = QNetworkAccessManager() + self.network_manager.finished.connect(self.handle_network_data) + + def eventFilter(self, obj: QObject, ev: QEvent): + if obj is not self.popup: + return False + if ev.type() == QEvent.MouseButtonPress: + self.popup.hide() + self.editor.setFocus() + return True + + if ev.type() == QEvent.KeyPress: + consumed = False + key = ev.key() + if key in (Qt.Key_Enter, Qt.Key_Return): + self.done_completion() + consumed = True + elif key == Qt.Key_Escape: + self.editor.setFocus() + self.popup.hide() + consumed = True + elif key in ( + Qt.Key_Up, + Qt.Key_Down, + Qt.Key_Home, + Qt.Key_End, + Qt.Key_PageUp, + Qt.Key_PageDown, + ): + pass + else: + self.editor.setFocus() + self.editor.event(ev) + self.popup.hide() + return consumed + return False + + def show_completion(self, choices: List[str]): + if not choices: + return + pal = self.editor.palette() + color = pal.color(QPalette.Disabled, QPalette.WindowText) + + self.popup.setUpdatesEnabled(False) + self.popup.clear() + + for choice in choices: + item = QTreeWidgetItem(self.popup) + item.setText(0, choice) + item.setForeground(0, color) + + self.popup.setCurrentItem(self.popup.topLevelItem(0)) + self.popup.resizeColumnToContents(0) + self.popup.setUpdatesEnabled(True) + + self.popup.move(self.editor.mapToGlobal(QPoint(0, self.editor.height()))) + self.popup.setFocus() + self.popup.show() + + @Slot() + def done_completion(self): + self.timer.stop() + self.popup.hide() + self.editor.setFocus() + item = self.popup.currentItem() + if item: + self.editor.setText(item.text(0)) + self.editor.returnPressed.emit() + + @Slot() + def auto_suggest(self): + s = self.editor.text() + url = f"https://google.com/complete/search?output=toolbar&q={s}" + self.network_manager.get(QNetworkRequest(url)) + + def prevent_suggest(self): + self.timer.stop() + + @Slot() + def handle_network_data(self, network_reply: QNetworkReply): + url = network_reply.url() + if network_reply.error() == QNetworkReply.NoError: + choices: List[str] = [] + + response: QByteArray = network_reply.readAll() + xml = QXmlStreamReader(response) + while not xml.atEnd(): + xml.readNext() + if xml.tokenType() == QXmlStreamReader.StartElement: + if xml.name() == "suggestion": + s = xml.attributes().value("data") + choices.append(s) + self.show_completion(choices) + + network_reply.deleteLater() diff --git a/examples/network/googlesuggest/googlesuggest.pyproject b/examples/network/googlesuggest/googlesuggest.pyproject new file mode 100644 index 000000000..a8cb57674 --- /dev/null +++ b/examples/network/googlesuggest/googlesuggest.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["main.py", "googlesuggest.py"] +} diff --git a/examples/network/googlesuggest/main.py b/examples/network/googlesuggest/main.py new file mode 100644 index 000000000..1efda08e6 --- /dev/null +++ b/examples/network/googlesuggest/main.py @@ -0,0 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import sys + +from PySide6.QtWidgets import QApplication + +from searchbox import SearchBox + +if __name__ == "__main__": + app = QApplication(sys.argv) + search_edit = SearchBox() + search_edit.show() + sys.exit(app.exec()) diff --git a/examples/network/googlesuggest/searchbox.py b/examples/network/googlesuggest/searchbox.py new file mode 100644 index 000000000..9cbe20b23 --- /dev/null +++ b/examples/network/googlesuggest/searchbox.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import Slot +from PySide6.QtGui import QDesktopServices +from PySide6.QtWidgets import QLineEdit + +from googlesuggest import GSuggestCompletion + + +class SearchBox(QLineEdit): + def __init__(self, parent=None): + super().__init__(parent) + self.completer = GSuggestCompletion(self) + + self.returnPressed.connect(self.do_search) + self.setWindowTitle("Search with Google") + + self.adjustSize() + self.resize(400, self.height()) + self.setFocus() + + @Slot() + def do_search(self): + self.completer.prevent_suggest() + url = f"https://www.google.com/search?q={self.text()}" + QDesktopServices.openUrl(url) -- cgit v1.2.3