diff --git a/QtRemoteObjects/README.en.md b/QtRemoteObjects/README.en.md new file mode 100644 index 0000000..0f15d4b --- /dev/null +++ b/QtRemoteObjects/README.en.md @@ -0,0 +1 @@ +# QtRemoteObjects \ No newline at end of file diff --git a/QtRemoteObjects/README.md b/QtRemoteObjects/README.md new file mode 100644 index 0000000..e76b0cf --- /dev/null +++ b/QtRemoteObjects/README.md @@ -0,0 +1,17 @@ +# QtRemoteObjects + +- 目录 + - [modelview](#1modelview) + - [simpleswitch](#2simpleswitch) + +## 1、modelview +[运行 modelviewserver.py](modelview/modelviewserver.py) | [运行 modelviewclient.py](modelview/modelviewclient.py) + +官方关于QTreeView/QStandardItemModel的同步model例子 + +## 2、simpleswitch +[运行 directconnectdynamicserver.py](simpleswitch/directconnectdynamicserver.py) | [运行 directconnectdynamicclient.py](simpleswitch/directconnectdynamicclient.py) + +[运行 registryconnecteddynamicserver.py](simpleswitch/registryconnecteddynamicserver.py) | [运行 registryconnecteddynamicclient.py](simpleswitch/registryconnecteddynamicclient.py) + +官方关于简单的信号槽、属性访问测试例子 \ No newline at end of file diff --git a/QtRemoteObjects/__init__.py b/QtRemoteObjects/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/QtRemoteObjects/modelview/modelviewclient.py b/QtRemoteObjects/modelview/modelviewclient.py new file mode 100644 index 0000000..e234e59 --- /dev/null +++ b/QtRemoteObjects/modelview/modelviewclient.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the examples of PyQt. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +############################################################################# + + +import sys + +from PyQt5.QtCore import QLoggingCategory, QUrl +from PyQt5.QtRemoteObjects import QRemoteObjectNode +from PyQt5.QtWidgets import QApplication, QTreeView + + +QLoggingCategory.setFilterRules('qt.remoteobjects.debug=false\n' + 'qt.remoteobjects.warning=false\n' + 'qt.remoteobjects.models.debug=false\n' + 'qt.remoteobjects.models.debug=false') + +app = QApplication(sys.argv) + +node = QRemoteObjectNode(QUrl('local:registry')) +node.setHeartbeatInterval(1000) + +view = QTreeView() +view.setWindowTitle("RemoteView") +view.resize(640, 480) + +model = node.acquireModel('RemoteModel') +view.setModel(model) +view.show(); + +sys.exit(app.exec_()) \ No newline at end of file diff --git a/QtRemoteObjects/modelview/modelviewserver.py b/QtRemoteObjects/modelview/modelviewserver.py new file mode 100644 index 0000000..98d0618 --- /dev/null +++ b/QtRemoteObjects/modelview/modelviewserver.py @@ -0,0 +1,169 @@ +#!/usr/bin/python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the PyQt examples. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +## +############################################################################# + + +import sys + +from PyQt5.QtCore import (pyqtSlot, QLoggingCategory, QModelIndex, QObject, Qt, + QTimer, QUrl) +from PyQt5.QtGui import QColor, QStandardItem, QStandardItemModel +from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost +from PyQt5.QtWidgets import QApplication, QTreeView + + +class TimerHandler(QObject): + + def __init__(self, model, parent=None): + super().__init__(parent) + + self._model = model + + @pyqtSlot() + def changeData(self): + for i in range(10, 50): + self._model.setData(self._model.index(i, 1), QColor(Qt.blue), + Qt.BackgroundRole) + + @pyqtSlot() + def insertData(self): + self._model.insertRows(2, 9) + + for i in range(2, 11): + self._model.setData(self._model.index(i, 1), QColor(Qt.green), + Qt.BackgroundRole) + self._model.setData(self._model.index(i, 1), "InsertedRow", + Qt.DisplayRole) + + @pyqtSlot() + def removeData(self): + self._model.removeRows(2, 4) + + @pyqtSlot() + def changeFlags(self): + item = self._model.item(0, 0) + item.setEnabled(False) + + item = item.child(0, 0) + item.setFlags(item.flags() & Qt.ItemIsSelectable) + + @pyqtSlot() + def moveData(self): + self._model.moveRows(QModelIndex(), 2, 4, QModelIndex(), 10) + + +def addChild(numChildren, nestingLevel): + result = [] + + if nestingLevel == 0: + return result + + for i in range(numChildren): + child = QStandardItem( + "Child num {}, nesting level {}".format(i + 1, nestingLevel)) + + if i == 0: + child.appendRow(addChild(numChildren, nestingLevel - 1)) + + result.append(child) + + return result + + +if __name__ == '__main__': + + QLoggingCategory.setFilterRules('qt.remoteobjects.debug=false\n' + 'qt.remoteobjects.warning=false') + + app = QApplication(sys.argv) + + sourceModel = QStandardItemModel() + sourceModel.setHorizontalHeaderLabels( + ["First Column with spacing", "Second Column with spacing"]) + + for i in range(10000): + firstItem = QStandardItem("FancyTextNumber {}".format(i)) + if i == 0: + firstItem.appendRow(addChild(2, 2)) + + secondItem = QStandardItem("FancyRow2TextNumber {}".format(i)) + if i % 2 == 0: + firstItem.setBackground(Qt.red) + + sourceModel.invisibleRootItem().appendRow([firstItem, secondItem]) + + # Needed by QMLModelViewClient. + roleNames = { + Qt.DisplayRole: b'_text', + Qt.BackgroundRole: b'_color' + } + sourceModel.setItemRoleNames(roleNames) + + roles = [Qt.DisplayRole, Qt.BackgroundRole] + + node = QRemoteObjectRegistryHost(QUrl('local:registry')) + + node2 = QRemoteObjectHost(QUrl('local:replica'), QUrl('local:registry')) + node2.enableRemoting(sourceModel, 'RemoteModel', roles) + + view = QTreeView() + view.setWindowTitle("SourceView") + view.setModel(sourceModel) + view.show() + + handler = TimerHandler(sourceModel) + QTimer.singleShot(5000, handler.changeData) + QTimer.singleShot(10000, handler.insertData) + QTimer.singleShot(11000, handler.changeFlags) + QTimer.singleShot(12000, handler.removeData) + QTimer.singleShot(13000, handler.moveData) + + sys.exit(app.exec_()) \ No newline at end of file diff --git a/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py b/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py new file mode 100644 index 0000000..20fd474 --- /dev/null +++ b/QtRemoteObjects/simpleswitch/directconnectdynamicclient.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the PyQt examples. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +## +############################################################################# + + +import sys + +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QUrl +from PyQt5.QtRemoteObjects import QRemoteObjectNode + + +class DynamicClient(QObject): + + # This signal is connected with server_slot() slot of the source object and + # echoes back the switch state received from the source. + echoSwitchState = pyqtSignal(bool) + + def __init__(self, replica, parent=None): + super().__init__(parent) + + self._replica = replica + self._clientSwitchState = False + + replica.initialized.connect(self.initConnection) + + @pyqtSlot(bool) + def recSwitchState(self, value): + self._clientSwitchState = self._replica.property('currState') + + print("Received source state", value, self._clientSwitchState) + + # Emit the signal to echo the received state back to the server. + self.echoSwitchState.emit(self._clientSwitchState) + + @pyqtSlot() + def initConnection(self): + # Connect the replica source signal currStateChanged() with the + # client's recSwitchState() slot to receive the source's current state. + self._replica.currStateChanged.connect(self.recSwitchState) + + # Connect the client's echoSwitchState() signal with replica's + # server_slot() to echo back the received state. + self.echoSwitchState.connect(self._replica.server_slot) + + +if __name__ == '__main__': + + app = QCoreApplication(sys.argv) + + # Create the remote object node. + repNode = QRemoteObjectNode() + + # Connect with the remote host node. + repNode.connectToNode(QUrl('local:replica')) + + # Acquire a replica of the source from the host node. + replica = repNode.acquireDynamic('SimpleSwitch') + + # Create the client switch object and pass the replica to it. + rswitch = DynamicClient(replica) + + sys.exit(app.exec_()) \ No newline at end of file diff --git a/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py b/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py new file mode 100644 index 0000000..acdd242 --- /dev/null +++ b/QtRemoteObjects/simpleswitch/directconnectdynamicserver.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the PyQt examples. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +## +############################################################################# + + +import sys + +from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QCoreApplication, + QObject, QTimer, QUrl) +from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost + + +class SimpleSwitch(QObject): + + def __init__(self, parent=None): + super().__init__(parent) + + self._currState = False + + self._stateChangeTimer = QTimer(self) + self._stateChangeTimer.timeout.connect(self._timeout) + self._stateChangeTimer.start(2000) + + print("Source node started") + + # PyQt does not support the use of static source types defined in .rep + # files. However we can manually specify a dynamic type that matches a + # .rep defined type by defining properties, signals and slots in the same + # order. We also have to account for any internals also generated by the + # .rep generator. At the moment this only includes an extra 'push' slot + # for each property (that never seems to get called). This allows this + # example to act as a server for Qt's C++ 'directconnectclient' example. + # It is not necessary when using with clients that use dynamic source types + # (written using either C++ or Python). + @pyqtSlot() + def pushCurrState(self, currState): + pass + + def _get_currState(self): + return self._currState + + def _set_currState(self, value): + # If the value has changed then update it and emit the notify signal. + if self._currState != value: + self._currState = value + self.currStateChanged.emit(value) + + # The property's notify signal. + currStateChanged = pyqtSignal(bool) + + # The property exposed to a remote client. + currState = pyqtProperty(bool, fget=_get_currState, fset=_set_currState, + notify=currStateChanged) + + # The slot exposed to a remote client. + @pyqtSlot(bool) + def server_slot(self, clientState): + # The switch state echoed back by the client. + print("Replica state is", clientState) + + def _timeout(self): + # Note that we don't decorate this callable so that it doesn't get + # exposed in a replica. + self.currState = not self.currState + + print("Source state is", self.currState) + + +if __name__ == '__main__': + + app = QCoreApplication(sys.argv) + + # Create the simple switch. + srcSwitch = SimpleSwitch() + + # Create the node that hosts the registry. This could be in a separate + # process. + regNode = QRemoteObjectRegistryHost(QUrl('local:registry')) + + # Create the host object node. This will connect to the registry node + # rather than to a client. + srcNode = QRemoteObjectHost(QUrl('local:replica'), QUrl('local:registry')) + + # Enable remoting. + srcNode.enableRemoting(srcSwitch, 'SimpleSwitch') + + sys.exit(app.exec_()) \ No newline at end of file diff --git a/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py b/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py new file mode 100644 index 0000000..4b0fe36 --- /dev/null +++ b/QtRemoteObjects/simpleswitch/registryconnecteddynamicclient.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the PyQt examples. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +## +############################################################################# + + +import sys + +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QUrl +from PyQt5.QtRemoteObjects import QRemoteObjectNode + + +class DynamicClient(QObject): + + # This signal is connected with server_slot() slot of the source object and + # echoes back the switch state received from the source. + echoSwitchState = pyqtSignal(bool) + + def __init__(self, replica, parent=None): + super().__init__(parent) + + self._replica = replica + self._clientSwitchState = False + + replica.initialized.connect(self.initConnection) + + @pyqtSlot(bool) + def recSwitchState(self, value): + self._clientSwitchState = self._replica.property('currState') + + print("Received source state", value, self._clientSwitchState) + + # Emit the signal to echo the received state back to the server. + self.echoSwitchState.emit(self._clientSwitchState) + + @pyqtSlot() + def initConnection(self): + # Connect the replica source signal currStateChanged() with the + # client's recSwitchState() slot to receive the source's current state. + self._replica.currStateChanged.connect(self.recSwitchState) + + # Connect the client's echoSwitchState() signal with replica's + # server_slot() to echo back the received state. + self.echoSwitchState.connect(self._replica.server_slot) + + +if __name__ == '__main__': + + app = QCoreApplication(sys.argv) + + # Create the remote object node. + repNode = QRemoteObjectNode(QUrl('local:registry')) + + # Acquire a replica of the source from the host node. + replica = repNode.acquireDynamic('SimpleSwitch') + + # Create the client switch object and pass the replica to it. + rswitch = DynamicClient(replica) + + sys.exit(app.exec_()) \ No newline at end of file diff --git a/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py b/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py new file mode 100644 index 0000000..acdd242 --- /dev/null +++ b/QtRemoteObjects/simpleswitch/registryconnecteddynamicserver.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + + +############################################################################# +## +## Copyright (C) 2018 Riverbank Computing Limited +## Copyright (C) 2017 Ford Motor Company +## +## This file is part of the PyQt examples. +## +## $QT_BEGIN_LICENSE:BSD$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## BSD License Usage +## Alternatively, 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$ +## +############################################################################# + + +import sys + +from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QCoreApplication, + QObject, QTimer, QUrl) +from PyQt5.QtRemoteObjects import QRemoteObjectHost, QRemoteObjectRegistryHost + + +class SimpleSwitch(QObject): + + def __init__(self, parent=None): + super().__init__(parent) + + self._currState = False + + self._stateChangeTimer = QTimer(self) + self._stateChangeTimer.timeout.connect(self._timeout) + self._stateChangeTimer.start(2000) + + print("Source node started") + + # PyQt does not support the use of static source types defined in .rep + # files. However we can manually specify a dynamic type that matches a + # .rep defined type by defining properties, signals and slots in the same + # order. We also have to account for any internals also generated by the + # .rep generator. At the moment this only includes an extra 'push' slot + # for each property (that never seems to get called). This allows this + # example to act as a server for Qt's C++ 'directconnectclient' example. + # It is not necessary when using with clients that use dynamic source types + # (written using either C++ or Python). + @pyqtSlot() + def pushCurrState(self, currState): + pass + + def _get_currState(self): + return self._currState + + def _set_currState(self, value): + # If the value has changed then update it and emit the notify signal. + if self._currState != value: + self._currState = value + self.currStateChanged.emit(value) + + # The property's notify signal. + currStateChanged = pyqtSignal(bool) + + # The property exposed to a remote client. + currState = pyqtProperty(bool, fget=_get_currState, fset=_set_currState, + notify=currStateChanged) + + # The slot exposed to a remote client. + @pyqtSlot(bool) + def server_slot(self, clientState): + # The switch state echoed back by the client. + print("Replica state is", clientState) + + def _timeout(self): + # Note that we don't decorate this callable so that it doesn't get + # exposed in a replica. + self.currState = not self.currState + + print("Source state is", self.currState) + + +if __name__ == '__main__': + + app = QCoreApplication(sys.argv) + + # Create the simple switch. + srcSwitch = SimpleSwitch() + + # Create the node that hosts the registry. This could be in a separate + # process. + regNode = QRemoteObjectRegistryHost(QUrl('local:registry')) + + # Create the host object node. This will connect to the registry node + # rather than to a client. + srcNode = QRemoteObjectHost(QUrl('local:replica'), QUrl('local:registry')) + + # Enable remoting. + srcNode.enableRemoting(srcSwitch, 'SimpleSwitch') + + sys.exit(app.exec_()) \ No newline at end of file diff --git a/README.md b/README.md index c9e6923..b591549 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,10 @@ https://pyqt5.com 社区是专门针对PyQt5学习和提升开设的博客网站 - [窗口抖动](QPropertyAnimation/ShakeWindow.py) - [窗口翻转动画(仿QQ)](QPropertyAnimation/FlipWidgetAnimation.py) - [折叠动画](Test/partner_625781186/2.折叠控件) + +- [RemoteObjects](QtRemoteObjects) + - [modelview](QtRemoteObjects/modelview) + - [simpleswitch](QtRemoteObjects/simpleswitch) - Others - [QFont](QFont)