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

图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. 引入库
代码如下:
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控件一样被添加到布局中。