【使用PyQt6与Matplotlib编写交互式生成一元二次函数图形程序】
目录
一、程序交互界面实现效果
二、如何在PyQt6中集成Matplotlib
三、程序具体代码实现
1. 引入库
2. 创建matplotlib画布类
3. 创建主窗口类
4. 主程序代码
四、总结
一、程序交互界面实现效果
在使用百度搜索引擎查询“一元二次函数”时,发现百度给出的关于一元二次函数的介绍内容中,有一个一元二次函数图形生成的交互式的工具不错。对该图形生成工具,百度网页上的原文如是说:想直观看看抛物线怎么变?试试这个工具。
图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对象。以下是核心实现步骤。
核心步骤
安装依赖:确保安装了PyQt6和matplotlib。
导入后端:从matplotlib.backends.backend_qtagg导入FigureCanvasQTAgg。
创建画布类:创建一个继承自FigureCanvasQTAgg的类,在其中初始化Figure和Axes。
嵌入界面:将该画布实例作为普通 Widget 添加到 PyQt6 的布局中。
动态更新:通过修改数据并调用draw()或draw_idle()来刷新图表。
三、程序具体代码实现
1. 引入库
代码如下:
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 np2. 创建matplotlib画布类
实现代码如下:
# 创建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. 创建主窗口类
实现代码如下:
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. 主程序代码
主程序实现代码如下:
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控件一样被添加到布局中。
