如何使 qml 对象的属性“动态更新"?通过套接字连接?
问题描述
我正在使用 PyQt5 和 Qml 创建客户端应用程序.这是我的 Qml 文件的简化示例:
I am using PyQt5 and Qml to create a client application. This is a simplified sample of my Qml file:
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: Screen.width/2
height: Screen.height/2
Rectangle {
id: rectangle
x: 187
y: 92
width: 200
height: 200
color: "blue"
}
}
客户端应用程序必须从服务器接收上述矩形的属性.为了做到这一点,我在.py"文件中实现了一个套接字连接.client.py 文件应该从服务器实时接收信息.我受到聊天应用程序的启发,我使用 (while True:{}) 循环来执行此操作:
The client app must receive the properties of the above rectangle from the server. In order to do that, I implemented a socket connection in the ".py" file. the client.py file ought to receive information in real-time from the server. I was inspired by chat application programs and I used a (while True:{}) loop to do this:
from PyQt5.QtQml import QQmlApplicationEngine, QQmlProperty
from PyQt5.QtQuick import QQuickWindow, QQuickView
from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtWidgets import QApplication
import sys, socket
def run():
myApp = QApplication(sys.argv)
myEngine = QQmlApplicationEngine()
myEngine.load('mainViewofHoomanApp.qml')
Win = myEngine.rootObjects()[0]
rect = Win.findChild(QObject, "rectangle")
rect.setProperty("height", 10) # Here I am accessing the properties of the rectangle
if not myEngine.rootObjects():
return -1
return myApp.exec_()
if __name__ == "__main__":
sys.exit(run())
而且是socket连接的格式:
And it is the format of socket connection:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Server_IPAddress = '192.168.1.163'
Port = 5000
client_socket.connect((Server_IPAddress,Port))
while True:
message = client_socket.recv(1024)
# Then the code extracts the parameters from the message
# and converts it to integer, and saves it in realT_width variable:
rect.setProperty("height", realT_width variable)
我对如何将这两个代码合并在一起感到困惑.如果我在编写 myApp.exec_() 命令后调用套接字连接,则 QML 文件将不再对参数更改命令作出反应.另一方面,如果我在 QML 执行之前编写套接字连接,那么 while 循环将不允许执行后面的代码行.
I am confused about how to merge these two codes together. If I call the socket connection after write the myApp.exec_() command, then the QML file will no longer react to the parameter change commands. On the other hand, if I write the socket connection before the QML execution, then the while loop will not allow the latter code lines to be executed.
解决方案
阻塞任务必须在另一个线程中执行,这样它们才不会冻结 GUI,在这种情况下我会假设下一个是服务器所以你必须先启动它.
The blocking tasks must be executed in another thread so that they do not freeze the GUI, in this case I will assume that the next one is the server so you must launch it first.
server.py
import socket
import time
import random
HOST = '127.0.0.1'
PORT = 65432
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
v = random.randint(10, 100)
conn.sendall(str(v).encode())
time.sleep(1.)
所以我将创建一个 QObject,我可以在其中创建信号,将在辅助线程上运行的套接字获得的信息发送到我在 我的另一个答案.
So I will create a QObject where I can create the signals that will send the information obtained by the socket that runs on a secondary thread to the handler that I publish in my other answer.
client.py
import os
import sys
import threading
import socket
from PyQt5 import QtCore, QtGui, QtQml
from functools import partial
class SocketWorker(QtCore.QObject):
heightChanged = QtCore.pyqtSignal(float)
@QtCore.pyqtSlot()
def process(self):
HOST = '127.0.0.1' # The server's hostname or IP address
PORT = 65432 # The port used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
while True:
data = s.recv(1024)
print('Received', repr(data))
try:
height = float(data)
self.heightChanged.emit(height)
except ValueError:
print("error")
class RectangleManager(QtCore.QObject):
widthChanged = QtCore.pyqtSignal(float)
heightChanged = QtCore.pyqtSignal(float)
def __init__(self, parent=None):
super(RectangleManager, self).__init__(parent)
self._width = 100
self._height = 100
def getWidth(self):
return self._width
def setWidth(self, w):
if self._width != w:
self._width = w
self.widthChanged.emit(w)
def getHeight(self):
return self._height
def setHeight(self, h):
if self._height != h:
self._height = h
self.heightChanged.emit(h)
width = QtCore.pyqtProperty(float, fget=getWidth, fset=setWidth, notify=widthChanged)
height = QtCore.pyqtProperty(float, fget=getHeight, fset=setHeight, notify=heightChanged)
def run():
myApp = QtGui.QGuiApplication(sys.argv)
myEngine = QtQml.QQmlApplicationEngine()
manager = RectangleManager()
myEngine.rootContext().setContextProperty("r_manager", manager)
directory = os.path.dirname(os.path.abspath(__file__))
myEngine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
if not myEngine.rootObjects():
return -1
worker = SocketWorker()
threading.Thread(target=worker.process, daemon=True).start()
worker.heightChanged.connect(manager.setHeight)
return myApp.exec_()
if __name__ == "__main__":
sys.exit(run())
<小时>
main.qml
import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: Screen.width/2
height: Screen.height/2
Rectangle {
id: rectangle
x: 187
y: 92
width: r_manager.width
height: r_manager.height
color: "blue"
}
}
相关文章