目录
效果:
代码:
import sys
import pandas as pd
from PyQt5 import QtCore,QtWidgets
from PyQt5.QtCore import Qt
from typing import Any,Dict
import pyqtgraph as pg
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
自定义坐标轴
class RotateAxisItem(pg.AxisItem):
def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
p.setRenderHint(p.Antialiasing,False)
p.setRenderHint(p.TextAntialiasing,True)
## draw long line along axis
pen,p1,p2 = axisSpec
p.setPen(pen)
p.drawLine(p1,p2)
p.translate(0.5,0) ## resolves some damn pixel ambiguity
## draw ticks
for pen,p1,p2 in tickSpecs:
p.setPen(pen)
p.drawLine(p1,p2)
## draw all text
# if self.tickFont is not None:
# p.setFont(self.tickFont)
p.setPen(self.pen())
for rect,flags,text in textSpecs:
# this is the important part
p.save()
p.translate(rect.x(),rect.y())
p.rotate(-30)
p.drawText(-rect.width(),rect.height(),rect.width(),rect.height(),flags,text)
# restoring the painter is *required*!!!
p.restore()
单Y轴与双Y轴折线图控件
class PyQtGraphWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.line_type: int = 1
self.init_data()
self.init_ui()
def init_data(self):
# 单根曲线的颜色值
self.color_one = (30,144,255)
# 第二根曲线的颜色值
self.color_two = (255,140,0)
self.p2 = None
pass
def init_ui(self):
self.title_label = QtWidgets.QLabel('折线图')
self.title_label.setAlignment(Qt.AlignCenter)
# 使用自定义的坐标轴,使得横坐标可以倾斜显示
xax = RotateAxisItem(orientation='bottom')
xax.setHeight(h=50)
self.pw = pg.PlotWidget(axisItems={'bottom': xax})
# 以下两句作用:鼠标滚轮缩放对折线图只作用在x轴上,x,y坐标轴上的数值会跟随缩放
# 只在x轴缩放折线图可以避免画布中空白,找不到图
self.pw.setMouseEnabled(x=True,y=False)
self.pw.setAutoVisible(x=False,y=True)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.title_label)
layout.addWidget(self.pw)
self.setLayout(layout)
pass
def set_data(self, data: Dict[str, Any]):
'''单根y轴'''
self.line_type = 1
if data is None:
self.pw.clear()
return
if self.p2 is not None:
'''
如果前一次显示的是双y轴,
【暂且把self.pw叫做主视图,self.p2叫做右侧视图】
1. 要把主视图右侧坐标轴隐藏
2. 要把主视图右侧跟随变动的信号断开
3. 将右侧视图(ViewBox)删除
4. 将右侧视图(ViewBox)清空
5. 将右侧视图置空
'''
self.pw.hideAxis('right')
self.vb.sigResized.disconnect(self.updateViews)
self.pw.scene().removeItem(self.p2)
self.p2.clear()
self.legend2.clear()
self.curve2.clear()
self.vb.clear()
self.p2 = None
self.legend2 = None
self.curve2 = None
# 将上一次视图清空
self.pw.clear()
self.pw.addLegend()
title_str = data['title_str']
self.title_label.setText(title_str)
xTick = [data['xTick00']]
x = data['x']
y = data['y']
y_name = data['y_name']
self.pw.setLabel('left', y_name)
self.y_datas = y
self.x_data = xTick
self.x_Tick = data['xTick']
self.y_name = y_name
xax = self.pw.getAxis('bottom')
xax.setTicks(xTick)
self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.label = pg.TextItem()
self.pw.addItem(self.vLine, ignoreBounds=True)
self.pw.addItem(self.hLine, ignoreBounds=True)
self.pw.addItem(self.label, ignoreBounds=True)
self.vb = self.pw.getViewBox()
self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
# 显示整条折线图
self.pw.enableAutoRange()
pass
def set_data_2y(self, data: Dict[str, Any]):
'''双y轴'''
self.line_type = 2
if data is None:
self.pw.clear()
return
if self.p2 is not None:
self.pw.hideAxis('right')
self.vb.sigResized.disconnect(self.updateViews)
self.pw.scene().removeItem(self.p2)
self.p2.clear()
self.legend2.clear()
self.curve2.clear()
self.vb.clear()
self.p2 = None
self.legend2 = None
self.curve2 = None
self.pw.clear()
self.pw.addLegend()
title_str = data['title_str']
self.title_label.setText(title_str)
xTick = [data['xTick00']]
x = data['x']
y = data['y']
y_name = data['y_name']
self.pw.setLabel('left', y_name)
self.y_datas = y
self.x_data = xTick
self.x_Tick = data['xTick']
self.y_name = y_name
xax = self.pw.getAxis('bottom')
xax.setTicks(xTick)
self.pw.plot(x, y, connect='finite', pen=pg.mkPen({'color': self.color_one, 'width': 4}), name=self.y_name)
self.vLine = pg.InfiniteLine(angle=90, movable=False)
self.hLine = pg.InfiniteLine(angle=0, movable=False)
self.label = pg.TextItem()
self.pw.addItem(self.vLine, ignoreBounds=True)
self.pw.addItem(self.hLine, ignoreBounds=True)
self.pw.addItem(self.label, ignoreBounds=True)
# 第二根y轴 start
y2 = data['y2']
y_name2 = data['y_name2']
self.y_datas2 = y2
self.pw.setLabel('right', y_name2)
self.p2 = pg.ViewBox()
self.p2.enableAutoRange()
self.p2.setMouseEnabled(x=True, y=False)
self.p2.setAutoVisible(x=False, y=True)
self.pw.scene().addItem(self.p2)
self.pw.getAxis('right').linkToView(self.p2)
self.p2.setXLink(self.pw)
self.curve2 = pg.PlotCurveItem(y2, pen=pg.mkPen({'color': self.color_two, 'width': 4}), connect='finite')
self.p2.addItem(self.curve2)
self.legend2 = pg.LegendItem(offset=(0., 1.))
self.legend2.setParentItem(self.p2)
power = pg.PlotDataItem(antialias=True, pen=pg.mkPen({'color': self.color_two, 'width': 4}))
self.legend2.addItem(power, y_name2)
# 第二根y轴 end
self.vb = self.pw.getViewBox()
self.vb.sigResized.connect(self.updateViews)
self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
self.pw.enableAutoRange()
pass
def updateViews(self):
self.p2.setGeometry(self.pw.getViewBox().sceneBoundingRect())
self.p2.linkedViewChanged(self.pw.getViewBox(), self.p2.XAxis)
pass
def mouseMoved(self,evt):
pos = evt[0]
if self.pw.sceneBoundingRect().contains(pos):
mousePoint = self.vb.mapSceneToView(pos)
index = int(mousePoint.x())
if index >= 0 and index < len(self.y_datas):
x_str = self.x_Tick[index][1]
y_str_html = ''
y_str = str(self.y_datas[index])
y_str_html += ' ' + y_str
if self.line_type == 2:
y_str2 = str(self.y_datas2[index])
y_str_html += ' '+y_str2
html_str = '<p style="color:black;font-size:18px;font-weight:bold;"> ' + x_str +' '+y_str_html+ '</p>'
self.label.setHtml(html_str)
self.label.setPos(mousePoint.x(), mousePoint.y())
self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y())
pass
使用:
单Y轴数据
if __name__ == '__main__':
pre_dir = r'E:/temp000/'
# oneY start
df = pd.read_csv(pre_dir +'oneY.csv',encoding='utf-8')
df['count'] = range(len(df))
df['val'] = df['val'].astype('float')
title_str = '单根Y轴示例'
x = df['count'].values.tolist()
x_date = df['date'].values.tolist()
y = df['val'].values.tolist()
y_name = '值'
xTick = []
for i_x,i_xdate in zip(x,x_date):
xTick.append((i_x,i_xdate))
xTick00 = []
for i in range(0,len(xTick),int(len(xTick)/20)):
xTick00.append(xTick[i])
res_data = {
"x":x,
"y":y,
"xTick":xTick,
"xTick00":xTick00,
"title_str":title_str,
"y_name":y_name
}
QtCore.QCoreApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
app = QtWidgets.QApplication(sys.argv)
temp_widget = PyQtGraphWidget()
temp_widget.showMaximized()
temp_widget.set_data(res_data)
app.exec()
# oneY end
pass
鼠标滑轮滚动缩放,左下角“A”点击后,可以显示整条折线
双Y轴数据
if __name__ == '__main__':
pre_dir = r'E:/temp055/'
# twoY start
df = pd.read_csv(pre_dir +'twoY.csv',encoding='utf-8')
df['count'] = range(len(df))
df['val_left'] = df['val_left'].astype('float')
df['val_right'] = df['val_right'].astype('float')
title_str = '双Y轴示例'
x = df['count'].values.tolist()
x_date = df['date'].values.tolist()
y = df['val_left'].values.tolist()
y2 = df['val_right'].values.tolist()
y_name = '值1'
y_name2 = '值2'
xTick = []
for i_x,i_xdate in zip(x,x_date):
xTick.append((i_x,i_xdate))
xTick00 = []
for i in range(0,len(xTick),int(len(xTick)/20)):
xTick00.append(xTick[i])
res_data = {
"x":x,
"y":y,
"y2":y2,
"xTick":xTick,
"xTick00":xTick00,
"title_str":title_str,
"y_name":y_name,
"y_name2":y_name2
}
QtCore.QCoreApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
app = QtWidgets.QApplication(sys.argv)
temp_widget = PyQtGraphWidget()
temp_widget.showMaximized()
temp_widget.set_data_2y(res_data)
app.exec()
# twoY end
pass
鼠标滑轮滚动缩放,左下角“A”点击后,可以显示整条折线
数据:
链接:https://pan.baidu.com/s/1VsXqXpvpcFq5oVKs4S9r7w
提取码:w8fi