今天在做分析工具的界面,绘图用的pyqtgraph,因为我想要做交互功能,捕获事件必不可少,但是在实际开发过程中遇到一些问题。
1.参照官网的例子,代码如下
"""
Demonstrates some customized mouse interaction by drawing a crosshair that follows
the mouse.
"""
import numpy as np
import pyqtgraph as pg
#generate layout
app = pg.mkQApp("Crosshair Example")
win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('pyqtgraph example: crosshair')
label = pg.LabelItem(justify='right')
win.addItem(label)
p1 = win.addPlot(row=1, col=0)
# customize the averaged curve that can be activated from the context menu:
p1.avgPen = pg.mkPen('#FFFFFF')
p1.avgShadowPen = pg.mkPen('#8080DD', width=10)
p2 = win.addPlot(row=2, col=0)
region = pg.LinearRegionItem()
region.setZValue(10)
# Add the LinearRegionItem to the ViewBox, but tell the ViewBox to exclude this
# item when doing auto-range calculations.
p2.addItem(region, ignoreBounds=True)
#pg.dbg()
p1.setAutoVisible(y=True)
#create numpy arrays
#make the numbers large to show that the range shows data from 10000 to all the way 0
data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)
data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000), 10) + 3000 * np.random.random(size=10000)
p1.plot(data1, pen="r")
p1.plot(data2, pen="g")
p2d = p2.plot(data1, pen="w")
# bound the LinearRegionItem to the plotted data
region.setClipItem(p2d)
def update():
region.setZValue(10)
minX, maxX = region.getRegion()
p1.setXRange(minX, maxX, padding=0)
region.sigRegionChanged.connect(update)
def updateRegion(window, viewRange):
rgn = viewRange[0]
region.setRegion(rgn)
p1.sigRangeChanged.connect(updateRegion)
region.setRegion([1000, 2000])
#cross hair
vLine = pg.InfiniteLine(angle=90, movable=False)
hLine = pg.InfiniteLine(angle=0, movable=False)
p1.addItem(vLine, ignoreBounds=True)
p1.addItem(hLine, ignoreBounds=True)
vb = p1.vb
def mouseMoved(evt):
pos = evt[0] ## using signal proxy turns original arguments into a tuple
if p1.sceneBoundingRect().contains(pos):
mousePoint = vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index > 0 and index < len(data1):
label.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>, <span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(), data1[index], data2[index]))
vLine.setPos(mousePoint.x())
hLine.setPos(mousePoint.y())
proxy = pg.SignalProxy(p1.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved)
#p1.scene().sigMouseMoved.connect(mouseMoved)
if __name__ == '__main__':
pg.exec()
首先,它这边是用的GraphicsLayoutWidget这个小部件,因为我之前绘图用的plotwidget这个小部件,所以我首先换了一下官网的部件,但是我之前返回的是GraphicsLayoutWidget的实例,但是现在换成GraphicsLayoutWidget,我还需要返回GraphicsLayoutWidget.plotItem这个对象,用到里面vb属性,但是我忘了返回GraphicsLayoutWidget,而是直接返回了子控件plotitem,导致父控件被销毁,再次调用子控件时就报错了。所以这里改用类的方法写,把父控件赋值给类属性,不报错了。
2.鼠标移动事件不起作用。还是跟对象销毁有关,因为例子用的面向过程写的,而且我也不知道哪些变量有用,哪些没用,所以在改写成类的时候,有些变量没有保留,导致鼠标移动事件没有生效。所以我觉得以后要是改写,面向过程的变量尽量识别出哪些有用,哪些没有用,或者不知道有用没用就把它设置成类属性,然后保存。改好后的代码如下:
import sys
import cgitb
import os
import time
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
QHBoxLayout, QVBoxLayout,QLabel, QComboBox,QPushButton,
QDateEdit, QSpacerItem,QFrame, QSizePolicy, QSplitter,
QRadioButton, QGroupBox,QCheckBox,QLineEdit, QAction,QFileDialog)
from PyQt5.QtCore import Qt, QDate, QRect
from PyQt5 import QtCore
from pyqt5_graph import plt_init
import pyqtgraph as pg
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.data1 = np.arange(100)
self.data2 = np.arange(100)
self._init_ui()
self.show()
def _init_ui(self):
self.setWindowTitle('分析工具')
self.resize(800,600)
qwg = QWidget()
#全局布局
wlayout = QVBoxLayout()
h1_wlayout = QHBoxLayout()
h2_wlayout = QHBoxLayout()
h3_wlayout = QHBoxLayout()
h4_wlayout = QHBoxLayout()
self.statusBar().showMessage("状态栏")
#第一层
self._first_layer(h1_wlayout)
self._second_layer(h2_wlayout)
self._third_layer(h3_wlayout)
self._fouth_layer(h4_wlayout)
wlayout.addLayout(h1_wlayout)
wlayout.addLayout(h2_wlayout)
wlayout.addLayout(h3_wlayout)
wlayout.addLayout(h4_wlayout)
wlayout.addWidget(self.statusBar())
qwg.setLayout(wlayout)
self.setCentralWidget(qwg)
def _first_layer(self,h1_wlayout):
self.h1_text = QLineEdit()
self.h1_text.setText("数据文件")
self.h1_data_button = QPushButton("选择文件")
self.h1_data_button.pressed.connect(self.select_data_file)
h1_wlayout.addItem(QSpacerItem(20,20))
h1_wlayout.addWidget(self.h1_text,0)
h1_wlayout.addWidget(self.h1_data_button,0)
self.label = QLabel("dsffa")
h1_wlayout.addWidget(self.label)
def _second_layer(self,h2_wlayout):
self.h2_text = QLineEdit()
self.h2_text.setText("模板文件")
self.h2_data_button = QPushButton("选择文件")
self.h2_data_button.pressed.connect(self.select_template_file)
h2_wlayout.addItem(QSpacerItem(20,20))
h2_wlayout.addWidget(self.h2_text,0)
h2_wlayout.addWidget(self.h2_data_button,0)
def _third_layer(self,h3_wlayout):
self.h3_text = QLineEdit()
self.h3_text.setText("导出目录")
self.h3_data_button = QPushButton("选择文件夹")
self.h3_data_button.pressed.connect(self.select_export_dir)
h3_wlayout.addItem(QSpacerItem(20,20))
h3_wlayout.addWidget(self.h3_text,0)
h3_wlayout.addWidget(self.h3_data_button,0)
def _fouth_layer(self,h4_wlayout):
self.pltwgt = self.plot_init()
#cross hair
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.pltwgt.addItem(self.vLine, ignoreBounds=True)
self.pltwgt.addItem(self.hLine, ignoreBounds=True)
self.vb = self.pltwgt.vb
self.pltwgt.scene().sigMouseClicked.connect(self.plot_click)
#self.pltwgt.scene().sigMouseMoved.connect(self.mouseover)
#proxy对象一定要保留,不然鼠标移动事件会失效,我猜是不保留就会销毁了
self.proxy = pg.SignalProxy(self.pltwgt.scene().sigMouseMoved, rateLimit=60, slot=self.mouse_move)
h4_wlayout.addWidget(self.win,0)
def plot_click(self,event):
print(event)
def plot_init(self):
pg.setConfigOption("background", "w")
#plt = pg.PlotWidget()
#win对象一定要保留,不保留plt对象没了容器,后面就没法添加子控件了
self.win = pg.GraphicsLayoutWidget(show=True)
plt = self.win.addPlot(row=1,col=0)
self.win.setMouseTracking(True)
plt.addLegend(size=(150, 80))
plt.showGrid(x=True, y=True, alpha=0.5)
plt.plot(x=np.arange(100),y=np.arange(100),pen='r')
plt.setAutoVisible(y=True)
return plt
def mouse_move(self,evt):
print("entern mouse move")
# 参数pos 是像素坐标,需要 转化为 刻度坐标
pos = evt[0] ## using signal proxy turns original arguments into a tuple
if self.pltwgt.sceneBoundingRect().contains(pos):
mousePoint = self.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index > 0 and index < len(self.data1):
self.label.setText("<span style='font-size: 12pt'>x=%0.1f, <span style='color: red'>y1=%0.1f</span>, <span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(), self.data1[index], self.data2[index]))
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
def select_data_file(self):
filename=QFileDialog.getOpenFileNames(self,'选择文件',os.getcwd(), "All Files(*);;eg Files(*.xlsx *.csv *.MDF)")
self.f_list = filename[0]
file_str = ",".join(self.f_list)
self.h1_text.setText(file_str)
def select_template_file(self):
filename=QFileDialog.getOpenFileNames(self,'选择文件',os.getcwd(), "xlsx Files(*.xlsx);;All Files(*)")
self.template_list = filename[0]
self.h2_text.setText(self.template_list[0])
def select_export_dir(self):
filename=QFileDialog.getExistingDirectory(None,"选取文件夹","C:/")
print(filename)
self.h3_text.setText(filename)
if __name__ == '__main__':
cgitb.enable(format="text")
app = QApplication(sys.argv)
win = MainWindow()
sys.exit(app.exec_())
主要改动的代码在_fouth_layer/plot_init两个方法,一个win变量要保留,一个时proxy也要保留,不保留就会报错或者事件不起作用。