MOO*_*OON 3 python pyqtgraph pyqt5
我正在尝试使用 PyQt5 和 pyqtgraph 拥有一个用户界面。我制作了两个复选框,每当我选择它们时,我想绘制代码中可用的两个数据集之一,每当我取消选择按钮时,我希望它清除相应的曲线。有两个带有文本的复选框A1,A2每个复选框都绘制一组数据。
我有两个问题:
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_())
Run Code Online (Sandbox Code Playgroud)
下面是我制作的一个示例,效果很好。self.num它可以重复使用来完成更多绘图,而无需增加代码,只需使用函数更改 的值并添加相应的数据add_data(x,y,ind),其中x和y是数据的值,ind是框的索引(从0到n-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_())
Run Code Online (Sandbox Code Playgroud)
请注意,在 de 内部PlotWidget,我使用该方法添加了绘图plot(),它返回PlotDataItem保存在调用之前创建的列表中的a self.plot_data。这样,您可以轻松地将其从其中删除Plot Widget并再次添加。此外,如果您的目标是一个更复杂的程序,例如,您可以在运行时更改每个框的数据,那么如果您setData()在PlotDataItem
正如我在一开始所说的,这应该适用于很多复选框,因为当复选框被选中/取消选中时调用的函数,首先将每个框的实际状态与前一个框(存储在self.state)中进行比较,并且只执行与该特定框对应的绘图上的变化。这样,您就可以避免每次选中/取消选中某个框时为每个复选框执行一个函数以及重新绘制所有 de 框(就像user8408080所做的那样)。我并不是说这不好,但如果增加复选框的数量和/或数据的复杂性,重新绘制所有数据的工作量将急剧增加。
唯一的问题是,当窗口太小而无法支持大量复选框(例如 96 个)时,您将不得不在另一个小部件而不是布局中组织复选框。
然后更改self.numto的值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)
Run Code Online (Sandbox Code Playgroud)