[英]Python - Change plot parameters in QThread
我正在寻找解决以下问题的解决方案:我的程序以所有数据的 plot 开始,稍后当我启动 function 时,工作人员正在根据时间绘制相同的图表。 所以有两条线,第一条红色线显示 plot 的外观,然后是第一张图之后的 plot,由工人完成。
不幸的是,第二个 plot 很薄。 我在示例中创建了一个名为“plotsize”的变量。 这可以改变第一个 plot,但我不知道如何在工作线程中改变第二个的特性。 我正在使用 QThread。
这是我的示例代码,两个数据。 GUI 文件的名称只是 GUI.py
#GUI.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(739, 532)
MainWindow.setStyleSheet("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.WidgetPlot = PlotWidget(self.centralwidget)
self.WidgetPlot.setGeometry(QtCore.QRect(100, 40, 541, 341))
self.WidgetPlot.setObjectName("WidgetPlot")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(330, 420, 93, 28))
self.pushButton.setObjectName("pushButton")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Main Window"))
self.pushButton.setText(_translate("MainWindow", "Start"))
from pyqtgraph import PlotWidget
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
这里的脚本:
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 2
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QObject):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self):
super().__init__()
def start(self):
self._run()
def _run(self):
self._count = 0
self.x = plot_time[:0]
self.y = plot_value[:0]
self.widgetplot.emit(self.x, self.y)
self._start_time = datetime.datetime.now()
while 0 <= self._count < 100:
self._count_prev = self._count
QtCore.QThread.usleep(10000)
self._diff = datetime.datetime.now() - self._start_time
self._count = int((self._diff.total_seconds() * 10))
if(self._count != self._count_prev):
print(self._count)
self.x = plot_time[:self._count]
self.y = plot_value[:self._count]
self.widgetplot.emit(self.x, self.y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
self.thread = QtCore.QThread(self)
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.start)
self.worker.finished.connect(self.thread.quit)
self.worker.widgetplot.connect(self.WidgetPlot.plot)
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
global plot_time
plot_time = []
global plot_value
plot_value = []
for i in range(100):
plot_time.append(i)
plot_value.append(i)
self.start()
def preview_plot(self):
self.WidgetPlot.clear()
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
self.preview_line = self.WidgetPlot.plot(plot_time, plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
def start(self):
self.preview_plot()
self.thread.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
提前谢谢了! 安德鲁
您目前有一个信号通过self.worker.widgetplot.connect(self.WidgetPlot.plot)
连接到plot
方法,但是如果您制作持久的 plot,您将有更好的时间 - 使用特定的笔组,在您想要的厚度 - 然后将您的widgetplot
信号连接到该图的setData
方法。
# ... Ui_MainWindow.__init__
self.plot_curve = self.WidgetPlot.plot(pen=mkPen("w", width=plotsize))
# ... in my_class.__init__
self.worker.widgetplot.connect(self.plot_curve.setData)
根据我从您的请求中推断出的内容(即您只想使用两种不同的大小来重叠 plot 行)我已经通过一系列改进重写了您的代码(以我的拙见)。
在问题的核心,我所做的是在您的 preview_line顶部创建一个单独的pyqtgraph.PlotDataItem
曲线preview_line
并为其处理不同的大小(完全随机,请根据需要调整大小)。 因此,这都是关于使用
self.second_line.setData(x, y)
而不是每次都用self.WidgetPlot.plot(...)
重新创建它
所有细节都在评论中。
#PROGRAM/SCRIPT
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication
import sys
import GUI
import datetime
import pyqtgraph
import time
plotsize = 20
# =============================================================================
# Threading for not freezing the GUI while running
# =============================================================================
class Worker(QtCore.QThread):
progress = QtCore.pyqtSignal(int)
finished = QtCore.pyqtSignal()
widgetplot = QtCore.pyqtSignal(list, list)
def __init__(self, plot_time, plot_value):
QtCore.QThread.__init__(self, objectName='WorkerThread')
# AS LONG AS YOU DON'T MODIFY plot_time and plot_value INTO THIS THREAD, they are thread-safe.
# If you intend to modify them in the future (into this thread), you need to implement a mutex/lock system to avoid races
# Note that lists are mutable: you're storing the reference to the actual object that will always be edited into the MAIN THREAD
self.plot_time = plot_time
self.plot_value = plot_value
def run(self):
# This is naturally a LOCAL variable
# self._count = 0
_count = 0
# --- This is useless, it plots ([], [])
# self.widgetplot.emit(self.x, self.y)
# self.x = plot_time[:0]
# self.y = plot_value[:0]
# -----------------------------------
_start_time = datetime.datetime.now()
while 0 <= _count < 100:
# Use local variable!
_count_prev = _count
QtCore.QThread.usleep(10000)
_diff = datetime.datetime.now() - _start_time
_count = int((_diff.total_seconds() * 10))
if(_count != _count_prev):
print(_count)
x = self.plot_time[:_count]
y = self.plot_value[:_count]
# Since plot_time and plot_value are managed by main thread, you would just need to emit _count variable.
# But I'll stick with your code
self.widgetplot.emit(x, y)
class my_class(QtWidgets.QMainWindow, GUI.Ui_MainWindow):
def __init__(self, parent=None):
super(my_class, self).__init__(parent)
self.setupUi(self)
# In your version, the range is constant irrespective of the data being updated. It can be moved here
self.WidgetPlot.setXRange(0, 105, padding=0)
self.WidgetPlot.setYRange(0, 105, padding=0)
# Create and store ONE line, you will change just the underlying data, not the object itself. WAY MORE efficient
# Different pen with different plot size. Is this what you're seeking?
self.second_line = pyqtgraph.PlotDataItem(pen=pyqtgraph.mkPen('w', width=plotsize*2))
# Here class variable initialization goes
self.plot_time = []
self.plot_value = []
# You don't need `moveToThread`. You can just subclass QThread
self.worker_thread = Worker(self.plot_time, self.plot_value)
# I changed the name just to highlight the fact it is just an update and not a plot rebuilt
self.worker_thread.widgetplot.connect(self.update_second_line_plot)
self.pushButton.clicked.connect(self.my_function)
self.WidgetPlot.setMouseEnabled(x=False, y=False)
font=QtGui.QFont()
font.setPixelSize(20)
font.setBold(True)
self.WidgetPlot.getAxis("bottom").setTickFont(font)
self.WidgetPlot.getAxis("left").setTickFont(font)
def my_function(self):
# Use class variable instead, see the __init__
# ...Not efficient
# for i in range(100):
# plot_time.append(i)
# plot_value.append(i)
# Better:
_l = list(range(100))
self.plot_time.extend(_l)
self.plot_value.extend(_l)
# DON'T DO self.plot_time = list(range(100)) --> it will recreate a new object, but this one is shared with the worker thread!
self.start()
def update_second_line_plot(self, plot_time, plot_value):
# Just update the UNDERLYING data of your curve
self.second_line.setData(plot_time, plot_value)
def start(self):
# First plot preview_plot. done ONCE
self.WidgetPlot.plot(self.plot_time, self.plot_value, pen=pyqtgraph.mkPen('r', width=plotsize))
# Add NOW the new line to be drawn ON TOP of the preview one
self.WidgetPlot.addItem(self.second_line)
# It automatically will do the job. You don't need any plotfunction
self.worker_thread.start()
def main():
app = QApplication(sys.argv)
form = my_class()
form.show()
app.exec_()
if __name__ == '__main__':
main()
结果:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.