mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-07-27 08:49:01 +08:00
继续改
This commit is contained in:
parent
78661f450b
commit
9fc6b4577a
1714
MRobot_old.py
Normal file
1714
MRobot_old.py
Normal file
File diff suppressed because it is too large
Load Diff
22
app.py
22
app.py
@ -1,22 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
# 将当前工作目录设置为程序所在的目录,确保无论从哪里执行,其工作目录都正确设置为程序本身的位置,避免路径错误。
|
|
||||||
os.chdir(os.path.dirname(sys.executable) if getattr(sys, 'frozen', False)else os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
from PyQt5.QtWidgets import QApplication
|
|
||||||
from app.main_window import MainWindow
|
|
||||||
|
|
||||||
# 启用 DPI 缩放
|
|
||||||
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
|
||||||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) # 启用高 DPI 缩放
|
|
||||||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) # 使用高 DPI 图标
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
app.setAttribute(Qt.AA_DontCreateNativeWidgetSiblings) # 避免创建原生窗口小部件的兄弟窗口
|
|
||||||
|
|
||||||
w = MainWindow()
|
|
||||||
|
|
||||||
sys.exit(app.exec_()) # 启动应用程序并进入主事件循环
|
|
||||||
# 注意:在 PyQt5 中,exec_() 是一个阻塞调用,直到应用程序退出。
|
|
45
app/about_interface.py
Normal file
45
app/about_interface.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from qfluentwidgets import PrimaryPushSettingCard, FluentIcon
|
||||||
|
from qfluentwidgets import InfoBar, InfoBarPosition
|
||||||
|
|
||||||
|
from .function_fit_interface import FunctionFitInterface
|
||||||
|
from app.tools.check_update import check_update
|
||||||
|
|
||||||
|
__version__ = "1.0.0"
|
||||||
|
|
||||||
|
class AboutInterface(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("aboutInterface")
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setAlignment(Qt.AlignTop)
|
||||||
|
|
||||||
|
card = PrimaryPushSettingCard(
|
||||||
|
text="检查更新",
|
||||||
|
icon=FluentIcon.DOWNLOAD,
|
||||||
|
title="关于",
|
||||||
|
content=f"MRobot_Toolbox 当前版本:{__version__}",
|
||||||
|
)
|
||||||
|
card.clicked.connect(self.on_check_update_clicked)
|
||||||
|
layout.addWidget(card)
|
||||||
|
|
||||||
|
def on_check_update_clicked(self):
|
||||||
|
latest = check_update(__version__)
|
||||||
|
if latest:
|
||||||
|
InfoBar.success(
|
||||||
|
title="发现新版本",
|
||||||
|
content=f"检测到新版本:{latest},请前往官网或仓库下载更新。",
|
||||||
|
parent=self,
|
||||||
|
position=InfoBarPosition.TOP,
|
||||||
|
duration=5000
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
InfoBar.info(
|
||||||
|
title="已是最新版本",
|
||||||
|
content="当前已是最新版本,无需更新。",
|
||||||
|
parent=self,
|
||||||
|
position=InfoBarPosition.TOP,
|
||||||
|
duration=3000
|
||||||
|
)
|
@ -65,19 +65,16 @@ class DataInterface(QWidget):
|
|||||||
# 主标题
|
# 主标题
|
||||||
title = TitleLabel("MRobot 代码生成")
|
title = TitleLabel("MRobot 代码生成")
|
||||||
title.setAlignment(Qt.AlignCenter)
|
title.setAlignment(Qt.AlignCenter)
|
||||||
title.setStyleSheet("font-size: 36px; font-weight: bold; color: #2d7d9a;")
|
|
||||||
content_layout.addWidget(title)
|
content_layout.addWidget(title)
|
||||||
|
|
||||||
# 副标题
|
# 副标题
|
||||||
subtitle = BodyLabel("请选择您的由CUBEMX生成的工程路径(.ico所在的目录),然后开启代码之旅!")
|
subtitle = BodyLabel("请选择您的由CUBEMX生成的工程路径(.ico所在的目录),然后开启代码之旅!")
|
||||||
subtitle.setAlignment(Qt.AlignCenter)
|
subtitle.setAlignment(Qt.AlignCenter)
|
||||||
subtitle.setStyleSheet("font-size: 16px; color: #4a6fa5;")
|
|
||||||
content_layout.addWidget(subtitle)
|
content_layout.addWidget(subtitle)
|
||||||
|
|
||||||
# 简要说明
|
# 简要说明
|
||||||
desc = BodyLabel("支持自动配置和生成任务,自主选择模块代码倒入,自动识别cubemx配置!")
|
desc = BodyLabel("支持自动配置和生成任务,自主选择模块代码倒入,自动识别cubemx配置!")
|
||||||
desc.setAlignment(Qt.AlignCenter)
|
desc.setAlignment(Qt.AlignCenter)
|
||||||
desc.setStyleSheet("font-size: 14px; color: #6b7b8c;")
|
|
||||||
content_layout.addWidget(desc)
|
content_layout.addWidget(desc)
|
||||||
|
|
||||||
content_layout.addSpacing(18)
|
content_layout.addSpacing(18)
|
||||||
@ -85,14 +82,12 @@ class DataInterface(QWidget):
|
|||||||
# 选择项目路径按钮
|
# 选择项目路径按钮
|
||||||
self.choose_btn = PushButton(FluentIcon.FOLDER, "选择项目路径")
|
self.choose_btn = PushButton(FluentIcon.FOLDER, "选择项目路径")
|
||||||
self.choose_btn.setFixedWidth(200)
|
self.choose_btn.setFixedWidth(200)
|
||||||
self.choose_btn.setStyleSheet("font-size: 17px;")
|
|
||||||
self.choose_btn.clicked.connect(self.choose_project_folder)
|
self.choose_btn.clicked.connect(self.choose_project_folder)
|
||||||
content_layout.addWidget(self.choose_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
content_layout.addWidget(self.choose_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
# 更新代码库按钮
|
# 更新代码库按钮
|
||||||
self.update_template_btn = PushButton(FluentIcon.SYNC, "更新代码库")
|
self.update_template_btn = PushButton(FluentIcon.SYNC, "更新代码库")
|
||||||
self.update_template_btn.setFixedWidth(200)
|
self.update_template_btn.setFixedWidth(200)
|
||||||
self.update_template_btn.setStyleSheet("font-size: 17px;")
|
|
||||||
self.update_template_btn.clicked.connect(self.update_user_template)
|
self.update_template_btn.clicked.connect(self.update_user_template)
|
||||||
content_layout.addWidget(self.update_template_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
content_layout.addWidget(self.update_template_btn, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
|
@ -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 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 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):
|
class FunctionFitInterface(QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -10,56 +20,127 @@ class FunctionFitInterface(QWidget):
|
|||||||
self.setObjectName("functionFitInterface")
|
self.setObjectName("functionFitInterface")
|
||||||
|
|
||||||
main_layout = QHBoxLayout(self)
|
main_layout = QHBoxLayout(self)
|
||||||
main_layout.setContentsMargins(32, 32, 32, 32)
|
|
||||||
main_layout.setSpacing(24)
|
main_layout.setSpacing(24)
|
||||||
|
|
||||||
# 左侧:数据输入区
|
# 左侧:数据输入区
|
||||||
left_layout = QVBoxLayout()
|
left_layout = QVBoxLayout()
|
||||||
left_layout.setSpacing(16)
|
left_layout.setSpacing(16)
|
||||||
|
|
||||||
left_layout.addWidget(TitleLabel("数据输入/导入"))
|
self.dataTable = TableWidget(self)
|
||||||
self.dataEdit = QTextEdit()
|
self.dataTable.setColumnCount(2)
|
||||||
self.dataEdit.setPlaceholderText("输入数据,每行格式:x,y")
|
self.dataTable.setHorizontalHeaderLabels(["x", "y"])
|
||||||
left_layout.addWidget(self.dataEdit)
|
self.dataTable.setColumnWidth(0, 125)
|
||||||
|
self.dataTable.setColumnWidth(1, 125)
|
||||||
|
left_layout.addWidget(self.dataTable)
|
||||||
|
|
||||||
btn_layout = QHBoxLayout()
|
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)
|
import_btn.clicked.connect(self.import_excel)
|
||||||
export_btn = QPushButton("导出 Excel")
|
export_btn = PushButton("导出 Excel")
|
||||||
export_btn.clicked.connect(self.export_excel)
|
export_btn.clicked.connect(self.export_excel)
|
||||||
btn_layout.addWidget(import_btn)
|
btn_layout.addWidget(import_btn)
|
||||||
btn_layout.addWidget(export_btn)
|
btn_layout.addWidget(export_btn)
|
||||||
left_layout.addLayout(btn_layout)
|
left_layout.addLayout(btn_layout)
|
||||||
|
|
||||||
fit_btn = QPushButton("拟合并绘图")
|
self.dataTable.setMinimumWidth(280)
|
||||||
fit_btn.clicked.connect(self.fit_and_plot)
|
self.dataTable.setMaximumWidth(280)
|
||||||
left_layout.addWidget(fit_btn)
|
|
||||||
|
|
||||||
main_layout.addLayout(left_layout, 1)
|
main_layout.addLayout(left_layout, 1)
|
||||||
|
|
||||||
|
self.add_row()
|
||||||
|
|
||||||
# 右侧:图像展示区
|
# 右侧:图像展示区
|
||||||
right_layout = QVBoxLayout()
|
right_layout = QVBoxLayout()
|
||||||
right_layout.setSpacing(16)
|
right_layout.setSpacing(12)
|
||||||
right_layout.addWidget(TitleLabel("函数拟合图像"))
|
right_layout.addWidget(SubtitleLabel("函数图像预览"))
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
self.figure = Figure(figsize=(5, 4))
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
||||||
|
|
||||||
self.figure, self.ax = plt.subplots()
|
|
||||||
self.canvas = FigureCanvas(self.figure)
|
self.canvas = FigureCanvas(self.figure)
|
||||||
right_layout.addWidget(self.canvas, stretch=1)
|
right_layout.addWidget(self.canvas, stretch=1)
|
||||||
|
|
||||||
self.resultLabel = BodyLabel("")
|
self.resultLabel = BodyLabel("")
|
||||||
|
self.resultLabel.setWordWrap(True) # 自动换行
|
||||||
right_layout.addWidget(self.resultLabel)
|
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)
|
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):
|
def import_excel(self):
|
||||||
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx *.xls)")
|
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx *.xls)")
|
||||||
if path:
|
if path:
|
||||||
df = pd.read_excel(path)
|
df = pd.read_excel(path)
|
||||||
text = "\n".join(f"{row[0]},{row[1]}" for row in df.values)
|
self.dataTable.setRowCount(0) # 清空原有数据
|
||||||
self.dataEdit.setText(text)
|
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):
|
def export_excel(self):
|
||||||
path, _ = QFileDialog.getSaveFileName(self, "导出 Excel", "", "Excel Files (*.xlsx)")
|
path, _ = QFileDialog.getSaveFileName(self, "导出 Excel", "", "Excel Files (*.xlsx)")
|
||||||
@ -70,11 +151,16 @@ class FunctionFitInterface(QWidget):
|
|||||||
df.to_excel(path, index=False)
|
df.to_excel(path, index=False)
|
||||||
|
|
||||||
def parse_data(self):
|
def parse_data(self):
|
||||||
lines = self.dataEdit.toPlainText().strip().split('\n')
|
|
||||||
data = []
|
data = []
|
||||||
for line in lines:
|
row_count = self.dataTable.rowCount()
|
||||||
|
for row in range(row_count):
|
||||||
try:
|
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])
|
data.append([x, y])
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
@ -84,18 +170,84 @@ class FunctionFitInterface(QWidget):
|
|||||||
data = self.parse_data()
|
data = self.parse_data()
|
||||||
if not data:
|
if not data:
|
||||||
self.resultLabel.setText("数据格式错误或为空")
|
self.resultLabel.setText("数据格式错误或为空")
|
||||||
|
self.codeLabel.setText("")
|
||||||
return
|
return
|
||||||
import numpy as np
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
x = np.array([d[0] for d in data])
|
x = np.array([d[0] for d in data])
|
||||||
y = np.array([d[1] for d in data])
|
y = np.array([d[1] for d in data])
|
||||||
# 简单线性拟合
|
degree = self.spinBox.value()
|
||||||
coeffs = np.polyfit(x, y, 1)
|
coeffs = np.polyfit(x, y, degree)
|
||||||
y_fit = np.polyval(coeffs, x)
|
|
||||||
self.ax.clear()
|
# 用更密集的横坐标画拟合曲线
|
||||||
self.ax.scatter(x, y, label="原始数据")
|
x_fit = np.linspace(x.min(), x.max(), 100)
|
||||||
self.ax.plot(x, y_fit, color='r', label=f"拟合: y={coeffs[0]:.3f}x+{coeffs[1]:.3f}")
|
y_fit = np.polyval(coeffs, x_fit)
|
||||||
self.ax.legend()
|
|
||||||
|
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.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
|
||||||
|
)
|
@ -10,9 +10,10 @@ with redirect_stdout(None):
|
|||||||
|
|
||||||
from .home_interface import HomeInterface
|
from .home_interface import HomeInterface
|
||||||
from .serial_terminal_interface import SerialTerminalInterface
|
from .serial_terminal_interface import SerialTerminalInterface
|
||||||
from .function_fit_interface import FunctionFitInterface
|
|
||||||
from .part_library_interface import PartLibraryInterface
|
from .part_library_interface import PartLibraryInterface
|
||||||
from .data_interface import DataInterface
|
from .data_interface import DataInterface
|
||||||
|
from .mini_tool_interface import MiniToolInterface
|
||||||
|
from .about_interface import AboutInterface
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
|
|
||||||
@ -47,17 +48,20 @@ class MainWindow(FluentWindow):
|
|||||||
def initInterface(self):
|
def initInterface(self):
|
||||||
self.homeInterface = HomeInterface(self)
|
self.homeInterface = HomeInterface(self)
|
||||||
self.serialTerminalInterface = SerialTerminalInterface(self)
|
self.serialTerminalInterface = SerialTerminalInterface(self)
|
||||||
self.functionFitInterface = FunctionFitInterface(self)
|
|
||||||
self.partLibraryInterface = PartLibraryInterface(self)
|
self.partLibraryInterface = PartLibraryInterface(self)
|
||||||
self.dataInterface = DataInterface(self)
|
self.dataInterface = DataInterface(self)
|
||||||
|
self.miniToolInterface = MiniToolInterface(self)
|
||||||
|
|
||||||
|
|
||||||
def initNavigation(self):
|
def initNavigation(self):
|
||||||
self.addSubInterface(self.homeInterface, FIF.HOME, self.tr('主页'))
|
self.addSubInterface(self.homeInterface, FIF.HOME, self.tr('主页'))
|
||||||
|
self.addSubInterface(self.dataInterface, FIF.CODE, self.tr('代码生成'))
|
||||||
self.addSubInterface(self.serialTerminalInterface, FIF.COMMAND_PROMPT,self.tr('串口助手'))
|
self.addSubInterface(self.serialTerminalInterface, FIF.COMMAND_PROMPT,self.tr('串口助手'))
|
||||||
self.addSubInterface(self.functionFitInterface, FIF.ROBOT, self.tr('函数拟合'))
|
|
||||||
self.addSubInterface(self.partLibraryInterface, FIF.DOWNLOAD, self.tr('零件库'))
|
self.addSubInterface(self.partLibraryInterface, FIF.DOWNLOAD, self.tr('零件库'))
|
||||||
self.addSubInterface(self.dataInterface, FIF.DOWNLOAD, self.tr('代码生成'))
|
self.addSubInterface(self.miniToolInterface, FIF.LIBRARY, self.tr('迷你工具箱'))
|
||||||
|
self.addSubInterface(AboutInterface(self), FIF.INFO, self.tr('关于'), position=NavigationItemPosition.BOTTOM)
|
||||||
|
|
||||||
|
|
||||||
# self.navigationInterface.addWidget(
|
# self.navigationInterface.addWidget(
|
||||||
# 'startGameButton',
|
# 'startGameButton',
|
||||||
# NavigationBarPushButton(FIF.PLAY, '启动游戏', isSelectable=False),
|
# NavigationBarPushButton(FIF.PLAY, '启动游戏', isSelectable=False),
|
||||||
|
82
app/mini_tool_interface.py
Normal file
82
app/mini_tool_interface.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QStackedWidget, QSizePolicy
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from qfluentwidgets import PushSettingCard, FluentIcon, TabBar
|
||||||
|
|
||||||
|
from .function_fit_interface import FunctionFitInterface
|
||||||
|
|
||||||
|
class MiniToolInterface(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setObjectName("minitoolInterface")
|
||||||
|
self.vBoxLayout = QVBoxLayout(self)
|
||||||
|
self.vBoxLayout.setAlignment(Qt.AlignTop)
|
||||||
|
self.vBoxLayout.setContentsMargins(10, 0, 10, 10) # 设置外边距
|
||||||
|
|
||||||
|
# 顶部标签栏,横向拉伸
|
||||||
|
self.tabBar = TabBar(self)
|
||||||
|
self.tabBar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
self.vBoxLayout.addWidget(self.tabBar) # 移除 Qt.AlignLeft
|
||||||
|
|
||||||
|
self.stackedWidget = QStackedWidget(self)
|
||||||
|
self.vBoxLayout.addWidget(self.stackedWidget) # 加入布局
|
||||||
|
|
||||||
|
|
||||||
|
# 初始主页面
|
||||||
|
self.mainPage = QWidget(self)
|
||||||
|
mainLayout = QVBoxLayout(self.mainPage)
|
||||||
|
mainLayout.setAlignment(Qt.AlignTop) # 卡片靠顶部
|
||||||
|
self.card = PushSettingCard(
|
||||||
|
text="▶启动",
|
||||||
|
icon=FluentIcon.UNIT,
|
||||||
|
title="曲线拟合工具",
|
||||||
|
content="简单的曲线拟合工具,支持多种函数类型",
|
||||||
|
)
|
||||||
|
mainLayout.addWidget(self.card)
|
||||||
|
self.mainPage.setLayout(mainLayout)
|
||||||
|
|
||||||
|
# 添加主页面到堆叠窗口
|
||||||
|
self.addSubInterface(self.mainPage, "mainPage", "工具箱主页")
|
||||||
|
|
||||||
|
self.setLayout(self.vBoxLayout)
|
||||||
|
|
||||||
|
# 信号连接
|
||||||
|
self.stackedWidget.currentChanged.connect(self.onCurrentIndexChanged)
|
||||||
|
# self.tabBar.tabAddRequested.connect(self.onAddNewTab)
|
||||||
|
self.tabBar.tabCloseRequested.connect(self.onCloseTab)
|
||||||
|
self.card.clicked.connect(self.open_fit_tab)
|
||||||
|
|
||||||
|
def addSubInterface(self, widget: QWidget, objectName: str, text: str):
|
||||||
|
widget.setObjectName(objectName)
|
||||||
|
self.stackedWidget.addWidget(widget)
|
||||||
|
self.tabBar.addTab(
|
||||||
|
routeKey=objectName,
|
||||||
|
text=text,
|
||||||
|
onClick=lambda: self.stackedWidget.setCurrentWidget(widget)
|
||||||
|
)
|
||||||
|
|
||||||
|
def onCurrentIndexChanged(self, index):
|
||||||
|
widget = self.stackedWidget.widget(index)
|
||||||
|
self.tabBar.setCurrentTab(widget.objectName())
|
||||||
|
|
||||||
|
def onAddNewTab(self):
|
||||||
|
pass # 可自定义添加新标签页逻辑
|
||||||
|
|
||||||
|
def onCloseTab(self, index: int):
|
||||||
|
item = self.tabBar.tabItem(index)
|
||||||
|
widget = self.findChild(QWidget, item.routeKey())
|
||||||
|
self.stackedWidget.removeWidget(widget)
|
||||||
|
self.tabBar.removeTab(index)
|
||||||
|
widget.deleteLater()
|
||||||
|
|
||||||
|
def open_fit_tab(self):
|
||||||
|
# 检查是否已存在标签页,避免重复添加
|
||||||
|
for i in range(self.stackedWidget.count()):
|
||||||
|
widget = self.stackedWidget.widget(i)
|
||||||
|
if widget.objectName() == "fitPage":
|
||||||
|
self.stackedWidget.setCurrentWidget(widget)
|
||||||
|
self.tabBar.setCurrentTab("fitPage")
|
||||||
|
return
|
||||||
|
fit_page = FunctionFitInterface(self)
|
||||||
|
self.addSubInterface(fit_page, "fitPage", "曲线拟合")
|
||||||
|
self.stackedWidget.setCurrentWidget(fit_page)
|
||||||
|
self.tabBar.setCurrentTab("fitPage")
|
20
app/tools/check_update.py
Normal file
20
app/tools/check_update.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import requests
|
||||||
|
from packaging.version import parse as vparse
|
||||||
|
|
||||||
|
def check_update(local_version, repo="goldenfishs/MRobot"):
|
||||||
|
"""
|
||||||
|
检查 GitHub 上是否有新版本
|
||||||
|
:param local_version: 当前版本号字符串,如 "1.0.2"
|
||||||
|
:param repo: 仓库名,格式 "用户名/仓库名"
|
||||||
|
:return: 最新版本号字符串(如果有新版本),否则 None
|
||||||
|
"""
|
||||||
|
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, timeout=5)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
latest = resp.json()["tag_name"].lstrip("v")
|
||||||
|
if vparse(latest) > vparse(local_version):
|
||||||
|
return latest
|
||||||
|
except Exception as e:
|
||||||
|
print(f"检查更新失败: {e}")
|
||||||
|
return None
|
Loading…
Reference in New Issue
Block a user