aboutsummaryrefslogtreecommitdiffstats
path: root/examples/network
diff options
context:
space:
mode:
Diffstat (limited to 'examples/network')
-rw-r--r--examples/network/blockingfortuneclient/blockingfortuneclient.py85
-rw-r--r--examples/network/downloader/downloader.py44
-rw-r--r--examples/network/fortuneclient/fortuneclient.py72
-rw-r--r--examples/network/fortuneserver/fortuneserver.py64
-rw-r--r--examples/network/googlesuggest/doc/googlesuggest.pngbin0 -> 2467 bytes
-rw-r--r--examples/network/googlesuggest/doc/googlesuggest.rst11
-rw-r--r--examples/network/googlesuggest/googlesuggest.py136
-rw-r--r--examples/network/googlesuggest/googlesuggest.pyproject3
-rw-r--r--examples/network/googlesuggest/main.py14
-rw-r--r--examples/network/googlesuggest/searchbox.py27
-rw-r--r--examples/network/loopback/dialog.py151
-rw-r--r--examples/network/loopback/doc/loopback.pngbin0 -> 6195 bytes
-rw-r--r--examples/network/loopback/doc/loopback.rst10
-rw-r--r--examples/network/loopback/loopback.pyproject3
-rw-r--r--examples/network/loopback/main.py15
-rw-r--r--examples/network/threadedfortuneserver/threadedfortuneserver.py54
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
new file mode 100644
index 000000000..cb2e91d4d
--- /dev/null
+++ b/examples/network/googlesuggest/doc/googlesuggest.png
Binary files 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..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
new file mode 100644
index 000000000..2b1bd4a0f
--- /dev/null
+++ b/examples/network/loopback/doc/loopback.png
Binary files differ
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)