You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PyQtGraph中重构自动缩放按钮至默认位置并自定义缩放范围

在PyQtGraph子图左下角添加自定义缩放按钮

问题描述

需要在PyQtGraph每个子图的左下角(默认'A'按钮所在位置)添加一个自定义自动缩放按钮,点击后缩放到特定坐标范围(显示比所有数据点更大的区域)。但直接通过QGraphicsProxyWidget添加按钮时,位置受坐标轴刻度影响,无法固定在默认按钮区域,且会处于绘图区域内部。

解决方案

PyQtGraph的PlotItem自带控制按钮布局ctrlLayout,默认的'A'、'Pan'等按钮都放在这个布局里。直接将自定义按钮添加到该布局中,就能和默认按钮保持相同的固定位置,不受坐标轴变化影响。

关键修改点

  1. 放弃使用QGraphicsProxyWidget将按钮添加到绘图场景,转而获取PlotItem的ctrlLayout
  2. 创建自定义按钮并绑定点击事件,将其添加到ctrlLayout的左侧(与'A'按钮同区域)

按钮添加代码示例

在创建子图的循环中,替换原按钮添加逻辑为:

# 创建自定义缩放按钮
auto_button = QtWidgets.QPushButton(f"Auto {i+1}")
auto_button.setFixedSize(60, 20)  # 按需调整尺寸
# 绑定点击事件,传入当前子图和自定义范围
auto_button.clicked.connect(
    lambda _, p=plot, i=i: self.set_custom_range(
        p, 
        xmin=self.xlim[i][0], 
        xmax=self.xlim[i][1], 
        ymin=self.ylim[i][0], 
        ymax=self.ylim[i][1]
    )
)
# 将按钮加入子图的控制布局
plot.ctrlLayout.addWidget(auto_button, 0, 0)  # 0行0列,与默认'A'按钮同位置区域

完整修改后的代码

import sys
import random
import time
import multiprocessing
import numpy as np
from PyQt5 import QtWidgets, QtCore
import pyqtgraph as pg

class StockPriceUpdater(multiprocessing.Process):
    def __init__(self, pipe, stock_name, prices):
        super().__init__()
        self.pipe = pipe
        self.stock_name = stock_name
        self.prices = prices

    def run(self):
        while True:
            new_price = self.prices[-1] + random.uniform(-0.5, 0.5)
            self.prices = np.append(self.prices, new_price)
            self.pipe.send((self.stock_name, self.prices.copy()))
            time.sleep(10)

class StockPriceApp(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.setup_multiprocessing()

    def initUI(self):
        self.setWindowTitle('Stock Prices')
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        layout = QtWidgets.QVBoxLayout(central_widget)

        self.graph_layout = pg.GraphicsLayoutWidget()
        layout.addWidget(self.graph_layout)

        self.plots = []
        self.curves = []
        self.xdata = [np.arange(10)] * 4
        
        self.ylim = {}
        self.xlim = {}
        self.ini_ydata = {}
        
        for i in range(4):
            plot = self.graph_layout.addPlot(row=i//2, col=i%2, title=f'Stock {i+1}')
            prices = 10 + np.cumsum(np.random.uniform(-0.5, 0.5, 10))
            
            self.update_axis_limits(plot, i, self.xdata[i], prices)
            self.ini_ydata[i] = prices
            curve = plot.plot(self.xdata[i], prices, pen='y')

            # 添加自定义缩放按钮到控制布局
            auto_button = QtWidgets.QPushButton(f"Auto {i+1}")
            auto_button.setFixedSize(60, 20)
            auto_button.clicked.connect(
                lambda _, p=plot, i=i: self.set_custom_range(
                    p, 
                    xmin=self.xlim[i][0], 
                    xmax=self.xlim[i][1], 
                    ymin=self.ylim[i][0], 
                    ymax=self.ylim[i][1]
                )
            )
            plot.ctrlLayout.addWidget(auto_button, 0, 0)

            self.plots.append(plot)
            self.curves.append(curve)
            
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update_plots)
        self.timer.start(1000)

    def setup_multiprocessing(self):
        self.pipes = {}
        self.processes = {}
        for i in range(4):
            stock_name = f'Stock{i+1}'
            parent_pipe, child_pipe = multiprocessing.Pipe()
            self.pipes[stock_name] = parent_pipe
            process = StockPriceUpdater(child_pipe, stock_name, self.ini_ydata[i])
            process.start()
            self.processes[stock_name] = process

    def update_plots(self):
        for i, (stock_name, pipe) in enumerate(self.pipes.items()):
            if pipe.poll():
                _, prices = pipe.recv()
                self.xdata[i] = np.append(self.xdata[i], self.xdata[i][-1] + 1)
                self.curves[i].setData(self.xdata[i], prices)
                self.update_axis_limits(self.plots[i], i, self.xdata[i], prices)

    def set_custom_range(self, plot, x_min, x_max, y_min, y_max):
        plot.getViewBox().setRange(xRange=[x_min, x_max], yRange=[y_min, y_max])

    def update_axis_limits(self, plot, stock, xvals, yvals):
        xmin = xvals.min()
        xmax = xvals.max()
        offset = (xmax - xmin) / 3 
        xmax = xmax + offset
        
        if isinstance(yvals, list):
            yvals = np.array(yvals)
            
        ymin = yvals.min()
        ymax = yvals.max()
        offset = (ymax - ymin) / 10
        
        ymax = ymax + offset
        ymin = ymin - offset
        
        self.ylim[stock] = (ymin, ymax)
        self.xlim[stock] = (xmin, xmax)

        plot.getViewBox().setRange(xRange=[xmin, xmax], yRange=[ymin, ymax], padding=0)

    def closeEvent(self, event):
        for process in self.processes.values():
            process.terminate()
        event.accept()

def main():
    app = QtWidgets.QApplication(sys.argv)
    stock_app = StockPriceApp()
    stock_app.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

说明

  • 自定义按钮会固定在子图左下角的控制按钮区域,与默认'A'按钮处于同一布局,不受坐标轴刻度变化影响
  • 点击按钮后会调用set_custom_range方法,使用预先计算的xlim和ylim范围完成缩放
  • 可根据需求调整按钮的尺寸、文本,适配界面风格

内容的提问来源于stack exchange,提问作者Xerium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 19:25:00