QTimer 不调用该方法来为绘图设置动画
Posted
技术标签:
【中文标题】QTimer 不调用该方法来为绘图设置动画【英文标题】:QTimer doesn´t call the method to animate a plot 【发布时间】:2019-03-21 01:24:11 【问题描述】:我正在尝试在 PyQt5 中创建一个 GUI 应用程序,该应用程序从远程 GUI 连续接收数据并使用 pyqtgraph 绘制动画。 我正在使用 Python 套接字模块进行连接和传输数据。我正在为 GUI 和连接使用多线程。
图形用户界面和数据接收工作正常,但在处理接收到的数据进行绘图时,Qtimer 不会调用制作动画的方法。
import sys
import time
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
from PyQt5.QtCore import QTimer
import socket
from threading import Thread
from socketserver import ThreadingMixIn
import numpy as np
import pyqtgraph as pg
class Window(QMainWindow):
def __init__(self):
super().__init__()
loadUi('app.ui', self)
self.plotWidget.plotItem.showGrid(True, True, 0.2)
self.serverThread=ServerThread()
self.serverThread.start()
def plot(self, amplitude):
self.y = amplitude * np.random.normal(size=600)
self.plotWidget.setXRange (0, 2)
self.plotWidget.setYRange (-0.5, 1)
Color = pg.mkPen('g', width=1)
self.curve = self.plotWidget.plot(self.y, pen=Color)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update)
self.timer.start(20)
#method that performs the animation
def update(self):
self.y[:-1] = self.y[1:] # shift data in the array one sample left
self.curve.setData(self.y)
class ServerThread(Thread):
def __init__(self):
Thread.__init__(self)
self.window=window
def run(self):
TCP_IP = '0.0.0.0'
TCP_PORT = 5000
BUFFER_SIZE = 20
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, TCP_PORT))
threads = []
tcpServer.listen(1)
while True:
print("Multithreaded Python server : Waiting for connections from TCP clients..")
global conn
(conn, (ip,port)) = tcpServer.accept()
newthread = ClientThread(ip,port)
newthread.start()
threads.append(newthread)
for t in threads:
t.join()
class ClientThread(Thread):
def __init__(self,ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
print("[+] New server socket thread started for " + ip + ":" + str(port))
def run(self):
while True :
global conn
data = conn.recv(2048)
data_recv = data.decode('utf-8')
data = int (data_recv)
window.plot(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
GUI 显示没有动画的情节。问题可能是什么?进程是否被阻塞? QThread 中的 QTimer 正在被垃圾收集吗?我做错了什么?
如果有人有想法,请帮助我。
【问题讨论】:
【参考方案1】:我认为问题在于您直接从不是主/GUI 线程的线程调用 window.plot(data)
,并期望 QTimer 能够在主/GUI 线程中调用插槽方法。
Qt 不是这样工作的——如果你想让QTimer
在主线程中工作,你需要在主线程中创建它。
第二个问题是,您在每次调用 window.plot(data)
时都会创建一个新的 QTimer
,并将每个新的 QTimer
设置为每 20 毫秒发出一个信号。因此,即使 QTimer
功能正常工作,数十/数百个 QTimer
对象的稳定积累,每个对象每秒发出 50 次信号,也会很快让您的程序陷入无用状态。
我的修复建议是只创建一个QTimer
对象(可能在Window.__init__(self)
内),将connect()
放到您的update()
插槽中,并对其调用start(20)
一次;这足以让您的 self.update()
方法以 50Hz(CPU 功率允许)被调用。
剩下的问题是如何安全地将接收到的data
从网络线程传输到主线程。为此,可能最简单的方法是创建QEvent
的子类,它可以将收到的data
作为成员变量,并让您的网络线程在每次需要发送@987654337 时创建该子类的新对象@ 到主线程,并使用您的 Window
和 QEvent
-object 作为参数调用 QApplication.postEvent()
。然后在您的 Window
类中,覆盖 event(self, QEvent)
方法(只要您的 Window 对象接收到任何事件就会调用该方法)并在那里添加逻辑,以便如果事件参数是您的事件子类之一,它会抓住data
退出事件并处理它。 (这里的关键优势是 event(self, QEvent)
由主线程调用,而不是直接由您的网络线程调用,因此您可以在该上下文中安全地与主线程/GUI 对象进行交互)
【讨论】:
以上是关于QTimer 不调用该方法来为绘图设置动画的主要内容,如果未能解决你的问题,请参考以下文章
Flutter - 我可以用 Hero 包装每个小部件来为它们设置动画吗
不可见的 ViewHolder 动画在 RecyclerView 中延迟