【使用PyQt6与Matplotlib编写交互式生成一元二次函数图形程序】


一、程序交互界面实现效果

在使用百度查询"一元二次函数"时,发现百度给出的关于一元二次函数的介绍内容中,有一个一元二次函数图形生成的交互式的工具不错。对该图形生成工具,百度原文说,想直观看看抛物线怎么变?试试这个工具。


图1 百度浏览器页面使用的抛物线生成工具

图1所示抛物线生成工具的交互性非常好,不仅可以通过a,b,c来控制抛物线的形状,还可以通过上下平移抛物线来改变常数项c的值。

于是想着自己来尝试实现这个工具。最省事的语言自然时Python语言了。至于使用Python语言的哪些库来绘制界面和图形,这里选择的是使用PyQt6来制作窗体界面,使用Matplotlib来绘制抛物线图形。

几番鼓捣,大部分功能终于实现,即可以通过调整过a,b,c来控制抛物线的形状,但通过上下平移抛物线来改变常数项c的值这项功能没有实现,有兴趣的读者可以自己去尝试实现之。因为本程序重在界面交互,所以下面截取了3个程序界面,见图2、图3、图4.

图2

图3

图4

二、如何在PyQt6中集成Matplotlib

在 PyQt6 中集成 Matplotlib 的核心在于使用FigureCanvasQTAgg将 Matplotlib 的图形对象转换为 Qt 的 Widget。以下是核心实现步骤。

核心步骤

  1. 安装依赖:确保安装了PyQt6和matplotlib。
  2. 导入后端:从matplotlib.backends.backend_qtagg导入FigureCanvasQTAgg。
  3. 创建画布类:创建一个继承自FigureCanvasQTAgg的类,在其中初始化Figure和Axes。
  4. 嵌入界面:将该画布实例作为普通 Widget 添加到 PyQt6 的布局中。
  5. 动态更新:通过修改数据并调用draw()或draw_idle()来刷新图表。

三、程序具体代码实现

1. 引入库

代码如下:

python 复制代码
import sys
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel, 
        QSlider, QVBoxLayout, QHBoxLayout)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QFont
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib as mpl
import numpy as np

2. 创建matplotlib画布类

实现代码如下:

python 复制代码
# 创建matplotlib画布类
class MplCanvas(FigureCanvas):
    def __init__(self, parent=None, width=6.4, height=4.5, dpi=100):
        # figsize以英寸为单位,实际像素 = figsize×dpi‌(默认 dpi=100,则 8×6 英寸 → 800×600 像素)
        self.figure = Figure(figsize=(width,height), dpi=dpi)
        self.ax = self.figure.add_subplot(111)
        # self.add_line()
        super().__init__(self.figure)
 
    def add_line(self, a=1, b=0, c=0):      # 初始曲线
        mpl.rc('mathtext', fontset = "cm")  # global fontset cm: Computer Modern(TeX) 
        if a != 0:
            symmetry_x = -b/(2*a)            # axis of symmetry
            vertex_y =  (4*a*c - b*b)/(4*a)     # 顶点的纵坐标
            x = np.linspace(symmetry_x-10, symmetry_x+10, 100)
            y = a*x**2 + b*x + c
            self.ax.cla()
            self.ax.plot(x, y, color="blue", label=f"$f(x)={a}x^2+{b}x+{c}$")
            if a>0:     # 抛物线开口向上
                self.ax.set_ylim(bottom = vertex_y-10)    # (bottom, top)
            else:
                self.ax.set_ylim(top = vertex_y+10)
        else:
            x = np.linspace(-10, 10, 100)
            y = b*x + c
            self.ax.cla()
            self.ax.plot(x, y, color="blue", label=f"$f(x)={b}x+{c}$")

        self.ax.grid(ls="--", alpha=0.4) 
        self.ax.legend(framealpha=0)
        self.figure.tight_layout()  # 解决子图重叠问题
        # self.figure.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1)  # 控制图形与画布边框之间的距离

3. 创建主窗口类

实现代码如下:

python 复制代码
class MainWindow(QMainWindow):
    def __init__(self, parent = None):
        super().__init__(parent)    #调用父类构造函数,创建窗体
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QSlider Demo")
        self.resize(640, 800)

        # slider1
        self.labelSlider1Value = QLabel("1")
        self.labelSlider1Value.setAlignment(Qt.AlignmentFlag.AlignRight)  # 设置QLabel内文本右对齐
        
        self.horizontalSlider1 = QSlider()
        self.horizontalSlider1.setOrientation(Qt.Orientation.Horizontal)
        self.horizontalSlider1.setObjectName("horizontalSlider1")

        self.horizontalSlider1.setMinimum(-5)         # 设置最小值
        self.horizontalSlider1.setMaximum(5)          # 设置最大值
        self.horizontalSlider1.setSingleStep(1)      # 步长
        self.horizontalSlider1.setValue(1)           # 设置当前值

        self.horizontalSlider1.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.horizontalSlider1.setTickInterval(1)    # 设置刻度间距
        self.horizontalSlider1.valueChanged.connect(self.valueChanged)  # 使用valueChanged信号

        hbox_layout1 = QHBoxLayout()     # 创建水平排列布局
        hbox_layout1.addWidget( QLabel("a (二次项系数)"))   # 在布局中插入组件
        hbox_layout1.addWidget(self.labelSlider1Value)

        # slider2
        self.labelSlider2Value = QLabel("0")
        self.labelSlider2Value.setAlignment(Qt.AlignmentFlag.AlignRight)  # 设置QLabel内文本右对齐
        
        self.horizontalSlider2 = QSlider()
        self.horizontalSlider2.setOrientation(Qt.Orientation.Horizontal)
        self.horizontalSlider2.setObjectName("horizontalSlider2")

        self.horizontalSlider2.setMinimum(-10)       # 设置最小值
        self.horizontalSlider2.setMaximum(10)        # 设置最大值
        self.horizontalSlider2.setSingleStep(1)      # 步长
        self.horizontalSlider2.setValue(0)           # 设置当前值

        self.horizontalSlider2.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.horizontalSlider2.setTickInterval(1)    # 设置刻度间距
        self.horizontalSlider2.valueChanged.connect(self.valueChanged)

        hbox_layout2 = QHBoxLayout()     # 创建水平排列布局
        hbox_layout2.addWidget(QLabel("b (一次项系数)"))   # 在布局中插入组件
        hbox_layout2.addWidget(self.labelSlider2Value)

        # slider3
        self.labelSlider3Value = QLabel("0")
        self.labelSlider3Value.setAlignment(Qt.AlignmentFlag.AlignRight)  # 设置QLabel内文本右对齐
        
        self.horizontalSlider3 = QSlider()
        self.horizontalSlider3.setOrientation(Qt.Orientation.Horizontal)
        self.horizontalSlider3.setObjectName("horizontalSlider3")

        self.horizontalSlider3.setMinimum(-100)         # 设置最小值
        self.horizontalSlider3.setMaximum(100)          # 设置最大值
        self.horizontalSlider3.setSingleStep(5)      # 步长
        self.horizontalSlider3.setValue(0)           # 设置当前值

        self.horizontalSlider3.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.horizontalSlider3.setTickInterval(5)    # 设置刻度间距
        self.horizontalSlider3.valueChanged.connect(self.valueChanged)

        hbox_layout3 = QHBoxLayout()     # 创建水平排列布局
        hbox_layout3.addWidget(QLabel("c (常数项)"))   # 在布局中插入组件
        hbox_layout3.addWidget(self.labelSlider3Value)

        hbox_layout4 = QHBoxLayout()
        hbox_layout4.addWidget(QLabel("提示:拖动滑块进行参数调整"))
        hbox_layout4.setAlignment(Qt.AlignmentFlag.AlignHCenter)

        self.canvas = MplCanvas(self, width=6.4, height=4.5, dpi=100)
        self.canvas.add_line(1, 0, 0)
       
        vbox_layout = QVBoxLayout()

        vbox_layout.addWidget(self.canvas)

        self.labelPrompt = QLabel("参数控制")
        self.labelPrompt.setFont(QFont('黑体', 16))
        vbox_layout.addWidget(self.labelPrompt)

        vbox_layout.addWidget(self.horizontalSlider1)
        vbox_layout.addLayout(hbox_layout1)

        vbox_layout.addWidget(self.horizontalSlider2)
        vbox_layout.addLayout(hbox_layout2)

        vbox_layout.addWidget(self.horizontalSlider3)
        vbox_layout.addLayout(hbox_layout3)

        vbox_layout.addLayout(hbox_layout4)
        
        # vbox_layout.addStretch(1)
        # self.setLayout(vbox_layout)     # 加入子布局
        container = QWidget()
        container.setLayout(vbox_layout)    # 加入子布局
        self.setCentralWidget(container)    # 设置主窗口的中心部件

    def valueChanged(self):
        current_a = self.horizontalSlider1.value()
        # print('当前刻度值=%s' % currentValue)
        self.labelSlider1Value.setText(str(current_a))

        current_b = self.horizontalSlider2.value()
        self.labelSlider2Value.setText(str(current_b))
        current_c = self.horizontalSlider3.value()
        self.labelSlider3Value.setText(str(current_c))

        self.canvas.add_line(current_a, current_b, current_c)
        # self.canvas.ax.relim()                     # 重置视图界限以适应新数据点
        # self.canvas.ax.autoscale_view()            # 自动缩放视图以适应新数据点
        self.canvas.draw()

4. 主程序代码

主程序实现代码如下:

python 复制代码
if __name__ == "__main__":            # 用于当前窗体测试
    app = QApplication(sys.argv)      # 建立application对象
    main_form = MainWindow()          # 创建窗体
    main_form.show()                  # 显示窗体
    sys.exit(app.exec())              # 运行程序

四、总结

以上代码实现了一个包含抛物线绘制和动态更新功能的相对完整 PyQt6 应用程序。

其中自定义的 MplCanvas继承自FigureCanvasQTAgg,封装了Matplotlib的Figure和Axes对象,使其能够像普通Qt控件一样被添加到布局中。

相关推荐
DogDaoDao3 天前
OpenCV 踩坑全指南
图像处理·人工智能·python·opencv·计算机视觉·matplotlib·rgb
电魂泡哥4 天前
Matplotlib.pyplot 完全入门指南
信息可视化·matplotlib
十年之少4 天前
matplotlib 与 PyQt5 结合使用——PyQt5
matplotlib·pyqt5
河阿里5 天前
Python数据可视化:Matplotlib从入门到精通
python·信息可视化·matplotlib
songyuc7 天前
Matplotlib&seaborn学习笔记
笔记·学习·matplotlib
威尔逊·柏斯科·希伯理7 天前
机器学习第一天(共12天)
人工智能·python·机器学习·conda·numpy·pandas·matplotlib
Cloud_Shy61816 天前
Python 数据分析基础入门:《Excel Python:飞速搞定数据分析与处理》学习笔记系列(第十一章 Python 包跟踪器 上篇)
python·数据分析·excel·pandas·matplotlib
键盘上的猫头鹰17 天前
Matplotlib可视化教程:从入门到精通
数据分析·matplotlib
深兰科技19 天前
深兰科技签约乌兹别克斯坦智慧城市项目,推动中国AI出海规模化
人工智能·beautifulsoup·numpy·智慧城市·fastapi·matplotlib·深兰科技