I have an interface on PyQt5, in which, by pressing the Start button, a graph is built, which I made using PyQtGraph. Three lines are drawn on the chart. Green and blue have a y-axis range of 0 to 200, while red has a range of 0 to 0.5. How can I make different scales for different lines, as well as designate two value scales on the Y-axis - from 0 to 200 and from 0 to 0.5?
from pyqtgraph import PlotWidget
import pyqtgraph
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, QThread, QTimer, QObject, pyqtSignal, QTimer
from PyQt5.QtWidgets import QHBoxLayout, QMainWindow, QPushButton, QVBoxLayout, QWidget, QApplication
import sys
import random
import numpy as np
def get_kl_test():
choices = [50, 50, 50, 51, 51, 51, 52, 52, 52]
list = [random.choice(choices) for i in range(11)]
return list
def get_iopd_test():
choices = [0.05, 0.1, 0.15, 0.2, 0.25, 0.3]
return random.choice(choices)
class Graph(PlotWidget):
def __init__(self):
super().__init__()
self.setBackground('white')
self.addLegend()
self.showGrid(x=True, y=True)
self.setYRange(0, 255, padding=0)
class ReadingWorker(QObject):
update_graph = pyqtSignal(list, list, list, list)
def __init__(self):
super().__init__()
self.time_from_start = 0
self.time_values = []
self.green_values = []
self.blue_values = []
self.red_values = []
def run(self):
self.read()
self.update_time()
def read(self):
ipd_values = get_kl_test()
iopd_value = get_iopd_test()
self.green_values.append(ipd_values[0])
self.blue_values.append(ipd_values[1])
self.red_values.append(iopd_value)
self.time_values.append(self.time_from_start)
self.update_graph.emit(
self.green_values, self.blue_values, self.red_values, self.time_values)
QTimer.singleShot(1000, self.read)
def update_time(self):
self.time_from_start += 1
QTimer.singleShot(1000, self.update_time)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.central_widget = QWidget(self)
self.setGeometry(50, 50, 1300, 700)
self.setCentralWidget(self.central_widget)
self.layout_main_window = QVBoxLayout()
self.central_widget.setLayout(self.layout_main_window)
self.layout_toolbar = QHBoxLayout()
self.layout_toolbar.addStretch(1)
self.btn_start = QPushButton("Старт")
self.btn_start.clicked.connect(self.start)
self.layout_toolbar.addWidget(self.btn_start)
self.layout_main_window.addLayout(self.layout_toolbar)
self.graph = Graph()
self.layout_main_window.addWidget(self.graph)
self.setup_graphs()
self.window_size = 50
def start(self):
self.reading_thread = QThread(parent=self)
self.reading_widget = ReadingWorker()
self.reading_widget.moveToThread(self.reading_thread)
self.reading_widget.update_graph.connect(self.draw_graph)
self.reading_thread.started.connect(self.reading_widget.run)
self.reading_thread.start()
def setup_graphs(self):
pen_ipd_1 = pyqtgraph.mkPen(color='green', width=4)
pen_ipd_2 = pyqtgraph.mkPen(color='blue', width=4, style=Qt.DashDotLine)
pen_iopd = pyqtgraph.mkPen(color='red', width=4, style=Qt.DashLine)
self.line_ipd_1 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_1, name='1')
self.line_ipd_2 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_2, name='2')
self.line_iopd = pyqtgraph.PlotCurveItem([], [], pen=pen_iopd, name='3')
self.graph.plotItem.addItem(self.line_ipd_1)
self.graph.plotItem.addItem(self.line_ipd_2)
self.graph.plotItem.addItem(self.line_iopd)
@QtCore.pyqtSlot(list, list, list, list)
def draw_graph(self, ipd_1_values, ipd_2_values, iopd_values, time_values):
x, y = self.line_ipd_1.getData()
x = np.append(x, time_values[-1])
self.line_ipd_1.setData(y=np.append(y, ipd_1_values[-1]), x=x)
_, y = self.line_ipd_2.getData()
self.line_ipd_2.setData(y=np.append(y, ipd_2_values[-1]), x=x)
_, y = self.line_iopd.getData()
self.line_iopd.setData(y=np.append(y, iopd_values[-1]), x=x)
if (len(x) > 0 and x[-1] -x [0] > self.window_size):
self.graph.plotItem.setXRange(x[-1]-self.window_size, x[-1])
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle('Fusion')
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
Check out the MultiplePlotAxes.py example.
To add another axis on the right change the setup_graphs
function and add update_views
:
def setup_graphs(self):
pen_ipd_1 = pyqtgraph.mkPen(color='green', width=4)
pen_ipd_2 = pyqtgraph.mkPen(color='blue', width=4, style=Qt.DashDotLine)
pen_iopd = pyqtgraph.mkPen(color='red', width=4, style=Qt.DashLine)
self.line_ipd_1 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_1, name='1')
self.line_ipd_2 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_2, name='2')
self.line_iopd = pyqtgraph.PlotCurveItem([], [], pen=pen_iopd, name='3')
self.graph.plotItem.addItem(self.line_ipd_1)
self.graph.plotItem.addItem(self.line_ipd_2)
self.vb = pyqtgraph.ViewBox()
self.pi = self.graph.plotItem
self.pi.showAxis('right')
self.pi.scene().addItem(self.vb)
self.pi.getAxis('right').linkToView(self.vb)
self.vb.setXLink(self.pi)
self.update_views()
self.pi.vb.sigResized.connect(self.update_views)
self.vb.addItem(self.line_iopd)
self.pi.setYRange(0,200)
self.vb.setYRange(0,0.5)
self.graph.plotItem.legend.addItem(self.line_iopd, self.line_iopd.name())
def update_views(self):
self.vb.setGeometry(self.pi.vb.sceneBoundingRect())
self.vb.linkedViewChanged(self.pi.vb, self.vb.XAxis)
Result:
Edit to simulatiously scale both y-axes (there might also be something build in, padding is really annoing here):
def setup_graphs(self):
pen_ipd_1 = pyqtgraph.mkPen(color='green', width=4)
pen_ipd_2 = pyqtgraph.mkPen(color='blue', width=4, style=Qt.DashDotLine)
pen_iopd = pyqtgraph.mkPen(color='red', width=4, style=Qt.DashLine)
self.line_ipd_1 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_1, name='1')
self.line_ipd_2 = pyqtgraph.PlotCurveItem([], [], pen=pen_ipd_2, name='2')
self.line_iopd = pyqtgraph.PlotCurveItem([], [], pen=pen_iopd, name='3')
self.graph.plotItem.addItem(self.line_ipd_1)
self.graph.plotItem.addItem(self.line_ipd_2)
self.vb = pyqtgraph.ViewBox()
self.pi = self.graph.plotItem
self.pi.showAxis('right')
self.pi.scene().addItem(self.vb)
self.pi.getAxis('right').linkToView(self.vb)
self.vb.setXLink(self.pi)
self.update_views()
self.pi.vb.sigResized.connect(self.update_views)
self.vb.addItem(self.line_iopd)
self.pi.setYRange(0,255, padding=0)
self.vb.setYRange(0,0.5, padding=0)
self.align = None
self.update_secondary()
self.pi.vb.sigYRangeChanged.connect(self.update_secondary)
self.graph.plotItem.legend.addItem(self.line_iopd, self.line_iopd.name())
def update_views(self):
self.vb.setGeometry(self.pi.vb.sceneBoundingRect())
self.vb.linkedViewChanged(self.pi.vb, self.vb.XAxis)
def update_secondary(self):
if self.align is None:
self.align = [self.pi.getAxis('left').range, self.pi.getAxis('right').range]
factor = (self.align[1][1]-self.align[1][0])/(self.align[0][1]-self.align[0][0])
newRangeLeft = self.pi.getAxis('left').range
newRangeRightMin = self.align[1][0]-(self.align[0][0]-newRangeLeft[0])*factor
newRangeRightMax = self.align[1][1]+(newRangeLeft[1]-self.align[0][1])*factor
self.vb.setYRange(newRangeRightMin, newRangeRightMax, padding=0)