diff options
Diffstat (limited to 'examples/network')
-rw-r--r-- | examples/network/blockingfortuneclient/blockingfortuneclient.py | 85 | ||||
-rw-r--r-- | examples/network/downloader/downloader.py | 44 | ||||
-rw-r--r-- | examples/network/fortuneclient/fortuneclient.py | 72 | ||||
-rw-r--r-- | examples/network/fortuneserver/fortuneserver.py | 64 | ||||
-rw-r--r-- | examples/network/googlesuggest/doc/googlesuggest.png | bin | 0 -> 2467 bytes | |||
-rw-r--r-- | examples/network/googlesuggest/doc/googlesuggest.rst | 11 | ||||
-rw-r--r-- | examples/network/googlesuggest/googlesuggest.py | 136 | ||||
-rw-r--r-- | examples/network/googlesuggest/googlesuggest.pyproject | 3 | ||||
-rw-r--r-- | examples/network/googlesuggest/main.py | 14 | ||||
-rw-r--r-- | examples/network/googlesuggest/searchbox.py | 27 | ||||
-rw-r--r-- | examples/network/loopback/dialog.py | 151 | ||||
-rw-r--r-- | examples/network/loopback/doc/loopback.png | bin | 0 -> 6195 bytes | |||
-rw-r--r-- | examples/network/loopback/doc/loopback.rst | 10 | ||||
-rw-r--r-- | examples/network/loopback/loopback.pyproject | 3 | ||||
-rw-r--r-- | examples/network/loopback/main.py | 15 | ||||
-rw-r--r-- | examples/network/threadedfortuneserver/threadedfortuneserver.py | 54 |
16 files changed, 435 insertions, 254 deletions
diff --git a/examples/network/blockingfortuneclient/blockingfortuneclient.py b/examples/network/blockingfortuneclient/blockingfortuneclient.py index 50f4c1662..d0dd7e0ad 100644 --- a/examples/network/blockingfortuneclient/blockingfortuneclient.py +++ b/examples/network/blockingfortuneclient/blockingfortuneclient.py @@ -1,54 +1,17 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt""" from PySide6.QtCore import (Signal, QDataStream, QMutex, QMutexLocker, - QThread, QWaitCondition) + QThread, QWaitCondition) from PySide6.QtGui import QIntValidator from PySide6.QtWidgets import (QApplication, QDialogButtonBox, QGridLayout, - QLabel, QLineEdit, QMessageBox, QPushButton, QWidget) + QLabel, QLineEdit, QMessageBox, QPushButton, + QWidget) from PySide6.QtNetwork import (QAbstractSocket, QHostAddress, QNetworkInterface, - QTcpSocket) + QTcpSocket) class FortuneThread(QThread): @@ -73,13 +36,13 @@ class FortuneThread(QThread): self.wait() def request_new_fortune(self, hostname, port): - locker = QMutexLocker(self.mutex) - self._host_name = hostname - self.port = port - if not self.isRunning(): - self.start() - else: - self.cond.wakeOne() + with QMutexLocker(self.mutex): + self._host_name = hostname + self.port = port + if not self.isRunning(): + self.start() + else: + self.cond.wakeOne() def run(self): self.mutex.lock() @@ -147,7 +110,7 @@ class BlockingClient(QWidget): port_label.setBuddy(self._port_line_edit) self._status_label = QLabel( - "This example requires that you run the Fortune Server example as well.") + "This example requires that you run the Fortune Server example as well.") self._status_label.setWordWrap(True) self._get_fortune_button = QPushButton("Get Fortune") @@ -182,7 +145,7 @@ class BlockingClient(QWidget): def request_new_fortune(self): self._get_fortune_button.setEnabled(False) self.thread.request_new_fortune(self._host_line_edit.text(), - int(self._port_line_edit.text())) + int(self._port_line_edit.text())) def show_fortune(self, nextFortune): if nextFortune == self._current_fortune: @@ -196,22 +159,22 @@ class BlockingClient(QWidget): def display_error(self, socketError, message): if socketError == QAbstractSocket.HostNotFoundError: QMessageBox.information(self, "Blocking Fortune Client", - "The host was not found. Please check the host and port " - "settings.") + "The host was not found. Please check the host and port " + "settings.") elif socketError == QAbstractSocket.ConnectionRefusedError: QMessageBox.information(self, "Blocking Fortune Client", - "The connection was refused by the peer. Make sure the " - "fortune server is running, and check that the host name " - "and port settings are correct.") + "The connection was refused by the peer. Make sure the " + "fortune server is running, and check that the host name " + "and port settings are correct.") else: QMessageBox.information(self, "Blocking Fortune Client", - f"The following error occurred: {message}.") + f"The following error occurred: {message}.") self._get_fortune_button.setEnabled(True) def enable_get_fortune_button(self): - self._get_fortune_button.setEnabled(self._host_line_edit.text() != '' and - self._port_line_edit.text() != '') + self._get_fortune_button.setEnabled(self._host_line_edit.text() != '' + and self._port_line_edit.text() != '') if __name__ == '__main__': diff --git a/examples/network/downloader/downloader.py b/examples/network/downloader/downloader.py index a63c49be8..fba0cb980 100644 --- a/examples/network/downloader/downloader.py +++ b/examples/network/downloader/downloader.py @@ -1,42 +1,5 @@ -############################################################################# -## -## Copyright (C) 2021 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause from PySide6.QtWidgets import ( QWidget, @@ -72,7 +35,8 @@ class DownloaderWidget(QWidget): self.link_box.setPlaceholderText("Download Link ...") self._open_folder_action = self.dest_box.addAction( - qApp.style().standardIcon(QStyle.SP_DirOpenIcon), QLineEdit.TrailingPosition + qApp.style().standardIcon(QStyle.SP_DirOpenIcon), # noqa: F821 + QLineEdit.TrailingPosition ) self._open_folder_action.triggered.connect(self.on_open_folder) diff --git a/examples/network/fortuneclient/fortuneclient.py b/examples/network/fortuneclient/fortuneclient.py index a94b62ec8..e88e5e35b 100644 --- a/examples/network/fortuneclient/fortuneclient.py +++ b/examples/network/fortuneclient/fortuneclient.py @@ -1,55 +1,16 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the network/fortuneclient example from Qt v5.x""" import sys -from PySide6.QtCore import QDataStream, QTimer, Qt +from PySide6.QtCore import QDataStream, QTimer from PySide6.QtGui import QIntValidator from PySide6.QtNetwork import QAbstractSocket, QTcpSocket from PySide6.QtWidgets import (QApplication, QDialog, QDialogButtonBox, QGridLayout, - QLabel, QLineEdit, QMessageBox, QPushButton, - QVBoxLayout, QWidget) + QLabel, QLineEdit, QMessageBox, QPushButton) class Client(QDialog): @@ -70,7 +31,7 @@ class Client(QDialog): port_label.setBuddy(self._port_line_edit) self._status_label = QLabel("This examples requires that you run " - "the Fortune Server example as well.") + "the Fortune Server example as well.") self._get_fortune_button = QPushButton("Get Fortune") self._get_fortune_button.setDefault(True) @@ -79,8 +40,7 @@ class Client(QDialog): quit_button = QPushButton("Quit") button_box = QDialogButtonBox() - button_box.addButton(self._get_fortune_button, - QDialogButtonBox.ActionRole) + button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole) button_box.addButton(quit_button, QDialogButtonBox.RejectRole) self._tcp_socket = QTcpSocket(self) @@ -108,7 +68,7 @@ class Client(QDialog): self._block_size = 0 self._tcp_socket.abort() self._tcp_socket.connectToHost(self._host_line_edit.text(), - int(self._port_line_edit.text())) + int(self._port_line_edit.text())) def read_fortune(self): instr = QDataStream(self._tcp_socket) @@ -138,23 +98,23 @@ class Client(QDialog): pass elif socketError == QAbstractSocket.HostNotFoundError: QMessageBox.information(self, "Fortune Client", - "The host was not found. Please check the host name and " - "port settings.") + "The host was not found. Please check the host name and " + "port settings.") elif socketError == QAbstractSocket.ConnectionRefusedError: QMessageBox.information(self, "Fortune Client", - "The connection was refused by the peer. Make sure the " - "fortune server is running, and check that the host name " - "and port settings are correct.") + "The connection was refused by the peer. Make sure the " + "fortune server is running, and check that the host name " + "and port settings are correct.") else: reason = self._tcp_socket.errorString() QMessageBox.information(self, "Fortune Client", - f"The following error occurred: {reason}.") + f"The following error occurred: {reason}.") self._get_fortune_button.setEnabled(True) def enable_get_fortune_button(self): - self._get_fortune_button.setEnabled(bool(self._host_line_edit.text() and - self._port_line_edit.text())) + self._get_fortune_button.setEnabled(bool(self._host_line_edit.text() + and self._port_line_edit.text())) if __name__ == '__main__': diff --git a/examples/network/fortuneserver/fortuneserver.py b/examples/network/fortuneserver/fortuneserver.py index d315dcf23..a94a49f42 100644 --- a/examples/network/fortuneserver/fortuneserver.py +++ b/examples/network/fortuneserver/fortuneserver.py @@ -1,44 +1,6 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2021 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the network/fortuneserver example from Qt v5.x""" @@ -49,7 +11,7 @@ from PySide6.QtCore import QByteArray, QDataStream, QIODevice, Qt from PySide6.QtNetwork import QTcpServer from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel, QMessageBox, QPushButton, - QVBoxLayout, QWidget) + QVBoxLayout) class Server(QDialog): @@ -65,21 +27,21 @@ class Server(QDialog): if not self._tcp_server.listen(): reason = self._tcp_server.errorString() QMessageBox.critical(self, "Fortune Server", - f"Unable to start the server: {reason}.") + f"Unable to start the server: {reason}.") self.close() return port = self._tcp_server.serverPort() status_label.setText(f"The server is running on port {port}.\nRun the " - "Fortune Client example now.") + "Fortune Client example now.") self.fortunes = ( - "You've been leading a dog's life. Stay off the furniture.", - "You've got to think about tomorrow.", - "You will be surprised by a loud noise.", - "You will feel hungry again in another hour.", - "You might have mail.", - "You cannot kill time without injuring eternity.", - "Computers are not intelligent. They only think they are.") + "You've been leading a dog's life. Stay off the furniture.", + "You've got to think about tomorrow.", + "You will be surprised by a loud noise.", + "You will feel hungry again in another hour.", + "You might have mail.", + "You cannot kill time without injuring eternity.", + "Computers are not intelligent. They only think they are.") quit_button.clicked.connect(self.close) self._tcp_server.newConnection.connect(self.send_fortune) diff --git a/examples/network/googlesuggest/doc/googlesuggest.png b/examples/network/googlesuggest/doc/googlesuggest.png Binary files differnew file mode 100644 index 000000000..cb2e91d4d --- /dev/null +++ b/examples/network/googlesuggest/doc/googlesuggest.png 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..d3fc67c05 --- /dev/null +++ b/examples/network/googlesuggest/googlesuggest.py @@ -0,0 +1,136 @@ +# 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(QNetworkReply) + def handle_network_data(self, network_reply: QNetworkReply): + if network_reply.error() == QNetworkReply.NoError: + choices: List[str] = [] + + response: QByteArray = network_reply.readAll() + xml = QXmlStreamReader(str(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) diff --git a/examples/network/loopback/dialog.py b/examples/network/loopback/dialog.py new file mode 100644 index 000000000..673afce28 --- /dev/null +++ b/examples/network/loopback/dialog.py @@ -0,0 +1,151 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +from PySide6.QtCore import QByteArray, Qt +from PySide6.QtGui import QGuiApplication +from PySide6.QtNetwork import (QAbstractSocket, QHostAddress, QTcpServer, + QTcpSocket) +from PySide6.QtWidgets import (QDialog, QDialogButtonBox, QLabel, QMessageBox, + QProgressBar, QPushButton, QVBoxLayout) + + +class Dialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + + self.total_bytes = 50 * 1024 * 1024 # 50 MB + self.payload_size = 64 * 1024 # 64 KB + + self.bytes_to_write = 0 + self.bytes_written = 0 + self.bytes_received = 0 + + self.client_progress_bar = QProgressBar() + self.client_status_label = QLabel("Client ready") + self.server_progress_bar = QProgressBar() + self.server_status_label = QLabel("Server ready") + + self.start_button = QPushButton("&Start") + self.quit_button = QPushButton("&Quit") + + self.button_box = QDialogButtonBox() + self.button_box.addButton(self.start_button, QDialogButtonBox.ActionRole) + self.button_box.addButton(self.quit_button, QDialogButtonBox.RejectRole) + + self.start_button.clicked.connect(self.start) + self.quit_button.clicked.connect(self.close) + + self.tcp_server = QTcpServer() + self.tcp_client = QTcpSocket() + self.tcp_server.newConnection.connect(self.accept_connection) + self.tcp_client.connected.connect(self.start_transfer) + self.tcp_client.bytesWritten.connect(self.update_client_progress) + self.tcp_client.errorOccurred.connect(self.display_error) + + main_layout = QVBoxLayout() + main_layout.addWidget(self.client_progress_bar) + main_layout.addWidget(self.client_status_label) + main_layout.addWidget(self.server_progress_bar) + main_layout.addWidget(self.server_status_label) + main_layout.addStretch(1) + main_layout.addSpacing(10) + main_layout.addWidget(self.button_box) + self.setLayout(main_layout) + + self.setWindowTitle("Loopback") + + def start(self): + + self.start_button.setEnabled(False) + + QGuiApplication.setOverrideCursor(Qt.WaitCursor) + + self.bytes_written = 0 + self.bytes_received = 0 + + while not self.tcp_server.isListening() and not self.tcp_server.listen(): + ret: QMessageBox.StandardButton = QMessageBox.critical( + self, + "Loopback", + f"Unable to start the test {self.tcp_server.errorString()}", + QMessageBox.Retry | QMessageBox.Cancel, + ) + if ret == QMessageBox.Cancel: + return + + self.server_status_label.setText("Listening") + self.client_status_label.setText("Connecting") + self.tcp_client.connectToHost(QHostAddress.LocalHost, self.tcp_server.serverPort()) + + def accept_connection(self): + + self.tcp_server_connection = self.tcp_server.nextPendingConnection() + if not self.tcp_server_connection: + self.server_status_label.setText("Error: got invalid pending connection") + return + + self.tcp_server_connection.readyRead.connect(self.update_server_progress) + self.tcp_server_connection.errorOccurred.connect(self.display_error) + self.tcp_server_connection.disconnected.connect(self.tcp_server_connection.deleteLater) + + self.server_status_label.setText("Accepted connection") + self.tcp_server.close() + + def start_transfer(self): + + # Called when the TCP client has connected to the loopback server + self.bytes_to_write = self.total_bytes - self.tcp_client.write( + QByteArray(self.payload_size, "@") + ) + self.client_status_label.setText("Connected") + + def update_server_progress(self): + + self.bytes_received += self.tcp_server_connection.bytesAvailable() + self.tcp_server_connection.readAll() + + self.server_progress_bar.setMaximum(self.total_bytes) + self.server_progress_bar.setValue(self.bytes_received) + self.server_status_label.setText(f"Received {self.bytes_received / (1024 ** 2)} MB") + + if self.bytes_received == self.total_bytes: + + self.tcp_server_connection.close() + self.start_button.setEnabled(True) + + QGuiApplication.restoreOverrideCursor() + + def update_client_progress(self, num_bytes: int): + + # called when the TCP client has written some bytes + self.bytes_written += num_bytes + + # only write more if not finished and when the Qt write buffer is below a certain size + if self.bytes_to_write > 0 and self.tcp_client.bytesToWrite() <= 4 * self.payload_size: + self.bytes_to_write -= self.tcp_client.write( + QByteArray(min(self.bytes_to_write, self.payload_size), "@") + ) + + self.client_progress_bar.setMaximum(self.total_bytes) + self.client_progress_bar.setValue(self.bytes_written) + self.client_status_label.setText(f"Sent {self.bytes_written / (1024 ** 2)} MB") + + def display_error(self, socket_error: QAbstractSocket.SocketError): + if socket_error == QAbstractSocket.RemoteHostClosedError: + return + + QMessageBox.information( + self, + "Network error", + f"The following error occurred: {self.tcp_client.errorString()}", + ) + + self.tcp_client.close() + self.tcp_server.close() + self.client_progress_bar.reset() + self.server_progress_bar.reset() + self.client_status_label.setText("Client ready") + self.server_status_label.setText("Server ready") + self.start_button.setEnabled(True) + + QGuiApplication.restoreOverrideCursor() diff --git a/examples/network/loopback/doc/loopback.png b/examples/network/loopback/doc/loopback.png Binary files differnew file mode 100644 index 000000000..2b1bd4a0f --- /dev/null +++ b/examples/network/loopback/doc/loopback.png diff --git a/examples/network/loopback/doc/loopback.rst b/examples/network/loopback/doc/loopback.rst new file mode 100644 index 000000000..0a40b57a8 --- /dev/null +++ b/examples/network/loopback/doc/loopback.rst @@ -0,0 +1,10 @@ +Loopback Example +================ + +Demonstrates the client-server communication on a local host. + +The example demonstrates how the clients and servers on a local host communicate with each other. + +.. image:: loopback.png + :width: 208 + :alt: loopback program screenshot diff --git a/examples/network/loopback/loopback.pyproject b/examples/network/loopback/loopback.pyproject new file mode 100644 index 000000000..957714cda --- /dev/null +++ b/examples/network/loopback/loopback.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["main.py", "dialog.py"] +} diff --git a/examples/network/loopback/main.py b/examples/network/loopback/main.py new file mode 100644 index 000000000..d684b6bab --- /dev/null +++ b/examples/network/loopback/main.py @@ -0,0 +1,15 @@ +# 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 dialog import Dialog + +if __name__ == "__main__": + app = QApplication(sys.argv) + + dialog = Dialog() + dialog.show() + sys.exit(app.exec()) diff --git a/examples/network/threadedfortuneserver/threadedfortuneserver.py b/examples/network/threadedfortuneserver/threadedfortuneserver.py index 0722d4739..c75e2bc57 100644 --- a/examples/network/threadedfortuneserver/threadedfortuneserver.py +++ b/examples/network/threadedfortuneserver/threadedfortuneserver.py @@ -1,55 +1,17 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """PySide6 port of the network/threadedfortuneserver example from Qt v5.x, originating from PyQt""" import random from PySide6.QtCore import (Signal, QByteArray, QDataStream, QIODevice, - QThread, Qt) + QThread, Qt) from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel, - QMessageBox, QPushButton, QVBoxLayout) + QMessageBox, QPushButton, QVBoxLayout) from PySide6.QtNetwork import (QHostAddress, QNetworkInterface, QTcpServer, - QTcpSocket) + QTcpSocket) class FortuneThread(QThread): @@ -113,7 +75,7 @@ class Dialog(QDialog): if not self.server.listen(): reason = self.server.errorString() QMessageBox.critical(self, "Threaded Fortune Server", - f"Unable to start the server: {reason}.") + f"Unable to start the server: {reason}.") self.close() return @@ -127,7 +89,7 @@ class Dialog(QDialog): port = self.server.serverPort() status_label.setText(f"The server is running on\n\nIP: {ip_address}\nport: {port}\n\n" - "Run the Fortune Client example now.") + "Run the Fortune Client example now.") quit_button.clicked.connect(self.close) |