繁体   English   中英

Python - 在 QThread 中更改 plot 参数

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM