如何在一个简单的图上实现 'playhead' 种特征?
How to implement 'playhead' kind of feature on a simple graph?
这是我想要实现的目标:
- 我有一个使用 Pyqtgraph 绘制的 x 与 y 数据的简单图
- 在此图上叠加我想要一种 'playhead' 类型的线条,它在 x 轴上移动(类似于视频编辑器中的时间轴滑块)
- 这条线在 x 轴上的位置应该从回调函数中动态接收
请看下面的图片以便清楚地理解。
您可以创建无限垂直线对象,这是 pyqtgraph
的一部分:InfiniteLine
。
import pyqtgraph as pg
v_line = pg.InfiniteLine(angle=90, movable=True)
此对象具有 属性 可通过拖动移动,每次移动线时它都会发出一个信号:sigPositionChanged
。您可以将此信号与一个函数连接,该函数 returns 其位置值,或数据的最接近的 x 值及其相应的 y 值。
v_line.sigPositionChanged.connect(some_function)
您在评论中说过,位置取决于函数的输入或输出。然后你可以使用 InfiniteLine
对象的 setPos()
方法改变位置。
v_line.setPos(10)
下面是我做的一个例子,大家可以从代码中引导,更好的理解。
本例中可以手动拖动垂直线,它只能“放置”在x数据的值中,x和y值将显示在框中。
此外,您只能更改与 x 值对应的框的值,当您按 Return/Enter 键时,该行的位置将转到数据中最接近的 x 值。
代码如下:
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
class MyApp(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
lay_1 = QtGui.QVBoxLayout()
lay_2 = QtGui.QHBoxLayout()
self.setLayout(lay_1)
label_1 = QtGui.QLabel('Marker Value : x = ')
label_2 = QtGui.QLabel(', y = ')
self.box_1 = QtGui.QDoubleSpinBox()
self.box_1.setButtonSymbols(2)
self.box_2 = QtGui.QDoubleSpinBox()
self.box_2.setButtonSymbols(2)
self.box_2.setReadOnly(True)
spacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.graf = pg.PlotWidget()
self.plt = self.graf.plot()
self.v_line = pg.InfiniteLine(angle=90, movable=True)
lay_1.addLayout(lay_2)
lay_1.addWidget(self.graf)
lay_2.addWidget(label_1)
lay_2.addWidget(self.box_1)
lay_2.addWidget(label_2)
lay_2.addWidget(self.box_2)
lay_2.addItem(spacer)
self.graf.addItem(self.v_line)
x = np.arange(100)/10*np.pi
y = np.sin(x)*np.exp(-x/10)*10
self.box_1.setRange(x.min(), x.max())
self.box_2.setRange(y.min(), y.max())
self.data = [x, y]
self.plt.setData(*self.data)
self.v_line.sigPositionChanged.connect(self.upd_drag)
self.box_1.lineEdit().returnPressed.connect(self.box_edited)
def get_xy(self, x_val):
'''For filtering the position of the vertical lines, selecting
limits and only prefered positions related to the x-data'''
x_data = self.data[0]
y_data = self.data[1]
x_min = min(x_data)
x_max = max(x_data)
if x_val < x_min:
x_val = x_min
elif x_val > x_max:
x_val = x_max
i = np.abs(x_data-x_val).argmin()
x_val = x_data[i]
y_val = y_data[i]
return x_val, y_val
def box_edited(self):
pos = self.box_1.value()
self.upd_v_line(pos)
def upd_drag(self, line):
pos = line.pos()[0]
self.upd_v_line(pos)
def upd_v_line(self, pos):
x, y = self.get_xy(pos)
self.v_line.setPos(x)
self.box_1.setValue(x)
self.box_2.setValue(y)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
这是我想要实现的目标:
- 我有一个使用 Pyqtgraph 绘制的 x 与 y 数据的简单图
- 在此图上叠加我想要一种 'playhead' 类型的线条,它在 x 轴上移动(类似于视频编辑器中的时间轴滑块)
- 这条线在 x 轴上的位置应该从回调函数中动态接收
请看下面的图片以便清楚地理解。
您可以创建无限垂直线对象,这是 pyqtgraph
的一部分:InfiniteLine
。
import pyqtgraph as pg
v_line = pg.InfiniteLine(angle=90, movable=True)
此对象具有 属性 可通过拖动移动,每次移动线时它都会发出一个信号:sigPositionChanged
。您可以将此信号与一个函数连接,该函数 returns 其位置值,或数据的最接近的 x 值及其相应的 y 值。
v_line.sigPositionChanged.connect(some_function)
您在评论中说过,位置取决于函数的输入或输出。然后你可以使用 InfiniteLine
对象的 setPos()
方法改变位置。
v_line.setPos(10)
下面是我做的一个例子,大家可以从代码中引导,更好的理解。 本例中可以手动拖动垂直线,它只能“放置”在x数据的值中,x和y值将显示在框中。
此外,您只能更改与 x 值对应的框的值,当您按 Return/Enter 键时,该行的位置将转到数据中最接近的 x 值。
代码如下:
import sys
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
class MyApp(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
lay_1 = QtGui.QVBoxLayout()
lay_2 = QtGui.QHBoxLayout()
self.setLayout(lay_1)
label_1 = QtGui.QLabel('Marker Value : x = ')
label_2 = QtGui.QLabel(', y = ')
self.box_1 = QtGui.QDoubleSpinBox()
self.box_1.setButtonSymbols(2)
self.box_2 = QtGui.QDoubleSpinBox()
self.box_2.setButtonSymbols(2)
self.box_2.setReadOnly(True)
spacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.graf = pg.PlotWidget()
self.plt = self.graf.plot()
self.v_line = pg.InfiniteLine(angle=90, movable=True)
lay_1.addLayout(lay_2)
lay_1.addWidget(self.graf)
lay_2.addWidget(label_1)
lay_2.addWidget(self.box_1)
lay_2.addWidget(label_2)
lay_2.addWidget(self.box_2)
lay_2.addItem(spacer)
self.graf.addItem(self.v_line)
x = np.arange(100)/10*np.pi
y = np.sin(x)*np.exp(-x/10)*10
self.box_1.setRange(x.min(), x.max())
self.box_2.setRange(y.min(), y.max())
self.data = [x, y]
self.plt.setData(*self.data)
self.v_line.sigPositionChanged.connect(self.upd_drag)
self.box_1.lineEdit().returnPressed.connect(self.box_edited)
def get_xy(self, x_val):
'''For filtering the position of the vertical lines, selecting
limits and only prefered positions related to the x-data'''
x_data = self.data[0]
y_data = self.data[1]
x_min = min(x_data)
x_max = max(x_data)
if x_val < x_min:
x_val = x_min
elif x_val > x_max:
x_val = x_max
i = np.abs(x_data-x_val).argmin()
x_val = x_data[i]
y_val = y_data[i]
return x_val, y_val
def box_edited(self):
pos = self.box_1.value()
self.upd_v_line(pos)
def upd_drag(self, line):
pos = line.pos()[0]
self.upd_v_line(pos)
def upd_v_line(self, pos):
x, y = self.get_xy(pos)
self.v_line.setPos(x)
self.box_1.setValue(x)
self.box_2.setValue(y)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())