python - 如何更新pyqtgraph中的绘图?

标签 python pyqt5 pyqtgraph

我正在尝试创建一个使用 PyQt5 和 pyqtgraph 的用户界面。我做了两个复选框,每当我选择它们时,我想绘制代码中可用的两个数据集之一,并且每当我取消选择一个按钮时,我希望它清除相应的曲线。有两个带有文本 A1A2 的复选框,每个复选框都绘制一组数据。

我有两个问题:

1- 如果我选择 A1 它会绘制与 A1 关联的数据,只要我不选择 A2,通过取消选择 A1 我可以清除与 A1 相关的数据。 但是,如果我选中 A1 框,然后选中 A2 框,则取消选择 A1 不会清除关联的绘图。在这种情况下,如果我选择绘制随机数据,而不是像 sin 这样的确定性曲线,我会看到通过选择任一按钮都会添加新数据,但无法将其删除。

2- 真正的应用程序有 96 个按钮,每个按钮都应该与一个数据集相关联。我认为我编写代码的方式效率低下,因为我需要为一个按钮和数据集复制相同的代码 96 次。有没有办法将我在下面介绍的玩具代码推广到任意数量的复选框?或者,为每个按钮使用/复制几乎相同的代码是通常且正确的方法吗?

代码是:

from PyQt5 import QtWidgets, uic, QtGui
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
import numpy as np
import sys
import string
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore

app = QtWidgets.QApplication(sys.argv)

x = np.linspace(0, 3.14, 100)
y1 = np.sin(x)#Data number 1 associated to checkbox A1
y2 = np.cos(x)#Data number 2 associated to checkbox A2

#This function is called whenever the state of checkboxes changes
def todo():
    if cbx1.isChecked():
        global curve1
        curve1 = plot.plot(x, y1, pen = 'r')
    else:
        try:
            plot.removeItem(curve1)
        except NameError:
            pass
    if cbx2.isChecked():
        global curve2
        curve2 = plot.plot(x, y2, pen = 'y')
    else:
        try:
            plot.removeItem(curve2)
        except NameError:
            pass  
#A widget to hold all of my future widgets
widget_holder = QtGui.QWidget()

#Checkboxes named A1 and A2
cbx1 = QtWidgets.QCheckBox()
cbx1.setText('A1')
cbx1.stateChanged.connect(todo)

cbx2 = QtWidgets.QCheckBox()
cbx2.setText('A2')
cbx2.stateChanged.connect(todo)

#Making a pyqtgraph plot widget
plot = pg.PlotWidget()

#Setting the layout
layout = QtGui.QGridLayout()
widget_holder.setLayout(layout)

#Adding the widgets to the layout
layout.addWidget(cbx1, 0,0)
layout.addWidget(cbx2, 0, 1)
layout.addWidget(plot, 1,0, 3,1)

widget_holder.adjustSize()
widget_holder.show()

sys.exit(app.exec_())


    

最佳答案

下面是我做的一个很好的例子。 可以重复使用做更多的绘图而不增加代码,只需要改变self.num的值并使用函数add_data(x,y,ind)添加相应的数据>,其中 xy 是数据的值,ind 是框的索引(从 0n-1)。

import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui

class MyApp(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.central_layout = QtGui.QVBoxLayout()
        self.plot_boxes_layout = QtGui.QHBoxLayout()
        self.boxes_layout = QtGui.QVBoxLayout()
        self.setLayout(self.central_layout)
        
        # Lets create some widgets inside
        self.label = QtGui.QLabel('Plots and Checkbox bellow:')
        
        # Here is the plot widget from pyqtgraph
        self.plot_widget = pg.PlotWidget()
        
        # Now the Check Boxes (lets make 3 of them)
        self.num = 6
        self.check_boxes = [QtGui.QCheckBox(f"Box {i+1}") for i in range(self.num)]
        
        # Here will be the data of the plot
        self.plot_data = [None for _ in range(self.num)]
        
        # Now we build the entire GUI
        self.central_layout.addWidget(self.label)
        self.central_layout.addLayout(self.plot_boxes_layout)
        self.plot_boxes_layout.addWidget(self.plot_widget)
        self.plot_boxes_layout.addLayout(self.boxes_layout)
        for i in range(self.num):
            self.boxes_layout.addWidget(self.check_boxes[i])
            # This will conect each box to the same action
            self.check_boxes[i].stateChanged.connect(self.box_changed)
            
        # For optimization let's create a list with the states of the boxes
        self.state = [False for _ in range(self.num)]
        
        # Make a list to save the data of each box
        self.box_data = [[[0], [0]] for _ in range(self.num)] 
        x = np.linspace(0, 3.14, 100)
        self.add_data(x, np.sin(x), 0)
        self.add_data(x, np.cos(x), 1)
        self.add_data(x, np.sin(x)+np.cos(x), 2)
        self.add_data(x, np.sin(x)**2, 3)
        self.add_data(x, np.cos(x)**2, 4)
        self.add_data(x, x*0.2, 5)
        

    def add_data(self, x, y, ind):
        self.box_data[ind] = [x, y]
        if self.plot_data[ind] is not None:
            self.plot_data[ind].setData(x, y)

    def box_changed(self):
        for i in range(self.num):
            if self.check_boxes[i].isChecked() != self.state[i]:
                self.state[i] = self.check_boxes[i].isChecked()
                if self.state[i]:
                    if self.plot_data[i] is not None:
                        self.plot_widget.addItem(self.plot_data[i])
                    else:
                        self.plot_data[i] = self.plot_widget.plot(*self.box_data[i])
                else:
                    self.plot_widget.removeItem(self.plot_data[i])
                break
        
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

请注意,在 PlotWidget 内部,我使用 plot() 方法添加绘图,它返回一个保存在列表中的 PlotDataItem在调用 self.plot_data 之前创建。 有了这个,您可以轻松地将其从 Plot Widget 中删除并重新添加。此外,如果您的目标是一个更复杂的程序,例如,您可以在运行时更改每个框的数据,如果您使用 setData() 方法,该图将更新而不会出现重大问题在 PlotDataItem

正如我一开始所说,这应该适用于很多复选框,因为当复选框被选中/未选中时调用的函数,首先将每个框的实际状态与前一个进行比较(存储在 self.state) 并且只对与该特定框相对应的图进行更改。这样,您就可以避免为每个复选框执行一个功能,并在每次选中/取消选中一个框时重新绘制所有 de 框(如 user8408080 所做的)。我不是说它不好,但是如果你增加复选框的数量和/或数据的复杂性,重新绘制所有数据的工作量会急剧增加。

唯一的问题是当窗口太小而无法支持大量复选框(例如 96 个)时,您将不得不在另一个小部件而不是布局中组织复选框。

现在是上面代码的一些截图: enter image description here

然后将 self.num 的值改为 6 并向其中添加一些随机数据:

self.add_data(x, np.sin(x)**2, 3)
self.add_data(x, np.cos(x)**2, 4)
self.add_data(x, x*0.2, 5)

enter image description here

关于python - 如何更新pyqtgraph中的绘图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65395378/

相关文章:

python - PyQt5 GUI 退出时为 "Python has stopped working"

python - 使用 pyqtgraph 实时绘制许多子图

python - Socket编程--客户端与服务端--接收到损坏的图像

python - 将字符串传递给函数时出现问题

python - 关闭事件不叫键盘事件和消息框

python - pyqtgraph线的鼠标坐标

python - 如何将 RGB ImageItem 添加到 pyqtgraph ViewBox? ValueError : could not broadcast input array from shape (256, 256,4) 变为形状 (256,256)

python - 在 Python 中迭代 XML 树中的孙子

python - 使用函数绘制 matplotlib 子图

python - 循环不适用于 sql 更新语句 (mysqldb)