继续改

This commit is contained in:
2025-07-25 02:54:03 +08:00
parent 78661f450b
commit 9fc6b4577a
10 changed files with 2070 additions and 1770 deletions

View File

@@ -1,8 +1,18 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QFileDialog, QLabel
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QTableWidgetItem, QApplication
from PyQt5.QtCore import Qt
from qfluentwidgets import TitleLabel, BodyLabel
from qfluentwidgets import TitleLabel, BodyLabel, TableWidget, PushButton, SubtitleLabel, SpinBox, ComboBox, InfoBar,InfoBarPosition, FluentIcon
import pandas as pd
import io
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWebEngineWidgets import QWebEngineView
import plotly.graph_objs as go
import plotly.io as pio
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'Source Han Sans', 'STHeiti', 'Heiti TC']
matplotlib.rcParams['axes.unicode_minus'] = False
class FunctionFitInterface(QWidget):
def __init__(self, parent=None):
@@ -10,56 +20,127 @@ class FunctionFitInterface(QWidget):
self.setObjectName("functionFitInterface")
main_layout = QHBoxLayout(self)
main_layout.setContentsMargins(32, 32, 32, 32)
main_layout.setSpacing(24)
# 左侧:数据输入区
left_layout = QVBoxLayout()
left_layout.setSpacing(16)
left_layout.addWidget(TitleLabel("数据输入/导入"))
self.dataEdit = QTextEdit()
self.dataEdit.setPlaceholderText("输入数据每行格式x,y")
left_layout.addWidget(self.dataEdit)
self.dataTable = TableWidget(self)
self.dataTable.setColumnCount(2)
self.dataTable.setHorizontalHeaderLabels(["x", "y"])
self.dataTable.setColumnWidth(0, 125)
self.dataTable.setColumnWidth(1, 125)
left_layout.addWidget(self.dataTable)
btn_layout = QHBoxLayout()
import_btn = QPushButton("导入 Excel")
add_row_btn = PushButton("添加一行")
add_row_btn.clicked.connect(self.add_row)
del_row_btn = PushButton("删除选中行") # 新增按钮
del_row_btn.clicked.connect(self.delete_selected_row) # 绑定槽函数
btn_layout.addWidget(add_row_btn)
btn_layout.addWidget(del_row_btn) # 添加到布局
left_layout.addLayout(btn_layout)
btn_layout = QHBoxLayout()
import_btn = PushButton("导入 Excel")
import_btn.clicked.connect(self.import_excel)
export_btn = QPushButton("导出 Excel")
export_btn = PushButton("导出 Excel")
export_btn.clicked.connect(self.export_excel)
btn_layout.addWidget(import_btn)
btn_layout.addWidget(export_btn)
left_layout.addLayout(btn_layout)
fit_btn = QPushButton("拟合并绘图")
fit_btn.clicked.connect(self.fit_and_plot)
left_layout.addWidget(fit_btn)
self.dataTable.setMinimumWidth(280)
self.dataTable.setMaximumWidth(280)
main_layout.addLayout(left_layout, 1)
self.add_row()
# 右侧:图像展示区
right_layout = QVBoxLayout()
right_layout.setSpacing(16)
right_layout.addWidget(TitleLabel("函数拟合图像"))
right_layout.setSpacing(12)
right_layout.addWidget(SubtitleLabel("函数图像预览"))
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
self.figure, self.ax = plt.subplots()
self.figure = Figure(figsize=(5, 4))
self.canvas = FigureCanvas(self.figure)
right_layout.addWidget(self.canvas, stretch=1)
self.resultLabel = BodyLabel("")
self.resultLabel.setWordWrap(True) # 自动换行
right_layout.addWidget(self.resultLabel)
# 拟合阶数和输出语言选择(合并到同一行)
options_layout = QHBoxLayout()
self.spinBox = SpinBox()
self.spinBox.setRange(1, 10)
self.spinBox.setValue(2)
options_layout.addWidget(SubtitleLabel("拟合阶数"))
options_layout.addWidget(self.spinBox)
self.langBox = ComboBox()
self.langBox.addItems(["C/C++", "Python"])
options_layout.addWidget(SubtitleLabel("输出语言"))
options_layout.addWidget(self.langBox)
right_layout.addLayout(options_layout)
# 代码显示和复制按钮
self.codeLabel = BodyLabel("")
self.codeLabel.setWordWrap(True) # 自动换行
right_layout.addWidget(self.codeLabel)
btn_layout = QHBoxLayout() # 新增一行布局
fit_btn = PushButton(FluentIcon.UNIT,"拟合并绘图")
fit_btn.clicked.connect(self.fit_and_plot)
btn_layout.addWidget(fit_btn)
copy_btn = PushButton(FluentIcon.COPY, "复制代码")
copy_btn.clicked.connect(self.copy_code)
btn_layout.addWidget(copy_btn)
right_layout.addLayout(btn_layout)
main_layout.addLayout(right_layout, 2)
# 默认显示空图像
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.set_xlabel('x')
ax.set_ylabel('y')
self.canvas.draw()
def add_row(self):
row = self.dataTable.rowCount()
self.dataTable.insertRow(row)
# 可选:初始化为空字符串
self.dataTable.setItem(row, 0, QTableWidgetItem(""))
self.dataTable.setItem(row, 1, QTableWidgetItem(""))
def delete_selected_row(self):
selected = self.dataTable.selectedItems()
if selected:
rows = set(item.row() for item in selected)
for row in sorted(rows, reverse=True):
self.dataTable.removeRow(row)
def import_excel(self):
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx *.xls)")
if path:
df = pd.read_excel(path)
text = "\n".join(f"{row[0]},{row[1]}" for row in df.values)
self.dataEdit.setText(text)
self.dataTable.setRowCount(0) # 清空原有数据
for row_data in df.values.tolist():
row = self.dataTable.rowCount()
self.dataTable.insertRow(row)
for col, value in enumerate(row_data):
item = QTableWidgetItem(str(value))
self.dataTable.setItem(row, col, item)
def export_excel(self):
path, _ = QFileDialog.getSaveFileName(self, "导出 Excel", "", "Excel Files (*.xlsx)")
@@ -70,11 +151,16 @@ class FunctionFitInterface(QWidget):
df.to_excel(path, index=False)
def parse_data(self):
lines = self.dataEdit.toPlainText().strip().split('\n')
data = []
for line in lines:
row_count = self.dataTable.rowCount()
for row in range(row_count):
try:
x, y = map(float, line.split(','))
x_item = self.dataTable.item(row, 0)
y_item = self.dataTable.item(row, 1)
if x_item is None or y_item is None:
continue
x = float(x_item.text())
y = float(y_item.text())
data.append([x, y])
except Exception:
continue
@@ -84,18 +170,84 @@ class FunctionFitInterface(QWidget):
data = self.parse_data()
if not data:
self.resultLabel.setText("数据格式错误或为空")
self.codeLabel.setText("")
return
import numpy as np
import matplotlib.pyplot as plt
x = np.array([d[0] for d in data])
y = np.array([d[1] for d in data])
# 简单线性拟合
coeffs = np.polyfit(x, y, 1)
y_fit = np.polyval(coeffs, x)
self.ax.clear()
self.ax.scatter(x, y, label="原始数据")
self.ax.plot(x, y_fit, color='r', label=f"拟合: y={coeffs[0]:.3f}x+{coeffs[1]:.3f}")
self.ax.legend()
degree = self.spinBox.value()
coeffs = np.polyfit(x, y, degree)
# 用更密集的横坐标画拟合曲线
x_fit = np.linspace(x.min(), x.max(), 100)
y_fit = np.polyval(coeffs, x_fit)
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.scatter(x, y, color='blue', label='原始数据')
ax.plot(x_fit, y_fit, color='red', label=f'拟合: {degree}')
ax.set_title('函数图像')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend()
self.canvas.draw()
self.resultLabel.setText(f"拟合公式: y = {coeffs[0]:.3f}x + {coeffs[1]:.3f}")
formula = self.poly_formula(coeffs)
self.resultLabel.setText(f"拟合公式: {formula}")
lang = self.langBox.currentText()
code = self.generate_code(coeffs, lang)
self.codeLabel.setText(code)
def poly_formula(self, coeffs):
terms = []
degree = len(coeffs) - 1
for i, c in enumerate(coeffs):
power = degree - i
if abs(c) < 1e-8:
continue
if power == 0:
terms.append(f"{c:.6g}")
elif power == 1:
terms.append(f"{c:.6g}*x")
else:
terms.append(f"{c:.6g}*x^{power}")
return " + ".join(terms)
def generate_code(self, coeffs, lang):
degree = len(coeffs) - 1
if lang == "C/C++":
code = "double poly(double x) {\n return "
elif lang == "Python":
code = "def poly(x):\n return "
else:
code = ""
terms = []
for i, c in enumerate(coeffs):
power = degree - i
if abs(c) < 1e-8:
continue
if power == 0:
terms.append(f"{c:.6g}")
elif power == 1:
terms.append(f"{c:.6g}*x")
else:
terms.append(f"{c:.6g}*pow(x,{power})" if lang == "C/C++" else f"{c:.6g}*x**{power}")
code += " + ".join(terms)
code += ";\n}" if lang == "C/C++" else ""
return code
def copy_code(self):
clipboard = QApplication.clipboard()
clipboard.setText(self.codeLabel.text())
# 弹出提示
InfoBar.success(
title='复制成功',
content="代码已复制到剪贴板!",
orient=Qt.Horizontal,
isClosable=True,
position=InfoBarPosition.TOP,
duration=2000,
parent=self
)