Compare commits

...

3 Commits

Author SHA1 Message Date
RB
79da21bca0 添加启动页 2025-05-25 15:54:03 +08:00
RB
b879e0ae94 回退一下 2025-05-25 15:24:59 +08:00
RB
214ac00e90 添加机械零件库 2025-05-25 11:55:32 +08:00
4 changed files with 396 additions and 6 deletions

View File

@ -18,6 +18,18 @@ import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWebEngineWidgets import QWebEngineView # 新增
from PyQt5.QtCore import QUrl
from PyQt5.QtCore import QThread
from PyQt5.QtWebEngineWidgets import QWebEngineProfile
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from PyQt5.QtWidgets import QSplashScreen
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QPixmap
def resource_path(relative_path):
"""兼容PyInstaller打包后资源路径"""
@ -1092,7 +1104,7 @@ class SerialAssistant(QWidget):
def send_data(self):
if self.ser and self.ser.is_open:
data = self.send_edit.text()
data = self.send_edit.toPlainText()
try:
if self.hex_send_chk.isChecked():
# 支持 0x11 0x22 33 44 格式
@ -1920,6 +1932,179 @@ class GenerateMRobotCode(QWidget):
except Exception as e:
self.log(f"生成 init.c 文件时出错: {e}")
# --------- 功能五:零件库 ---------
class CustomWebView(QWebEngineView):
def __init__(self, parent=None, popup_list=None):
super().__init__(parent)
self.popup_list = popup_list
self._progress_dialog = None
self.page().profile().downloadRequested.connect(self.handle_download)
self.setStyleSheet("""
QWebEngineView {
border-radius: 12px;
background: #f8fbfd;
border: 1px solid #d6eaf8;
}
""")
def handle_download(self, download_item):
from PyQt5.QtWidgets import QFileDialog, QProgressDialog
# 防止重复弹窗
if hasattr(download_item, "_handled") and download_item._handled:
return
download_item._handled = True
suggested = download_item.suggestedFileName()
path, _ = QFileDialog.getSaveFileName(self, "保存文件", suggested)
if not path:
download_item.cancel()
return
download_item.setPath(path)
download_item.accept()
# 创建进度对话框
self._progress_dialog = QProgressDialog(f"正在下载: {suggested}", "取消", 0, 100, self)
self._progress_dialog.setWindowTitle("下载进度")
self._progress_dialog.setWindowModality(Qt.WindowModal)
self._progress_dialog.setMinimumDuration(0)
self._progress_dialog.setValue(0)
self._progress_dialog.canceled.connect(download_item.cancel)
self._progress_dialog.show()
def on_progress(received, total):
if total > 0:
percent = int(received * 100 / total)
self._progress_dialog.setValue(percent)
else:
self._progress_dialog.setValue(0)
download_item.downloadProgress.connect(on_progress)
def on_finished():
self._progress_dialog.setValue(100)
self._progress_dialog.close()
if self.parent() and isinstance(self.parent(), CustomWebView):
self.parent().close()
elif self.popup_list and self in self.popup_list:
self.close()
self.popup_list.remove(self)
download_item.finished.connect(on_finished)
def createWindow(self, _type):
popup = CustomWebView(popup_list=self.popup_list)
popup.setAttribute(Qt.WA_DeleteOnClose)
popup.setWindowTitle("下载")
popup.resize(900, 600)
popup.show()
if self.popup_list is not None:
self.popup_list.append(popup)
return popup
class MachineryLibrary(QWidget):
def __init__(self):
super().__init__()
self.popup_windows = []
self.setFont(QFont("微软雅黑", 15))
self.setStyleSheet("""
QWidget {
background: #f8fbfd;
border-radius: 16px;
padding: 20px;
}
""")
self.init_ui()
def init_ui(self):
main_layout = QVBoxLayout(self)
main_layout.setSpacing(18)
main_layout.setContentsMargins(32, 32, 32, 32)
# 标题区
title = QLabel("MRobot 零件库")
title.setFont(QFont("微软雅黑", 22, QFont.Bold))
title.setAlignment(Qt.AlignCenter)
title.setStyleSheet("color: #2980b9; letter-spacing: 2px; margin-bottom: 2px;")
main_layout.addWidget(title)
desc = QLabel("零件库账号Engineer无密码")
desc.setFont(QFont("微软雅黑", 13))
desc.setAlignment(Qt.AlignCenter)
desc.setStyleSheet("color: #34495e; margin-bottom: 8px;")
main_layout.addWidget(desc)
# 加载提示
self.loading_label = QLabel("正在加载零件库网页,请稍候...")
self.loading_label.setAlignment(Qt.AlignCenter)
self.loading_label.setFont(QFont("微软雅黑", 14))
self.loading_label.setStyleSheet("color: #888; margin-bottom: 8px;")
main_layout.addWidget(self.loading_label)
# 网页视图
self.webview = CustomWebView(parent=self, popup_list=self.popup_windows)
self.webview.setAttribute(Qt.WA_TranslucentBackground, True)
self.webview.setMinimumHeight(480)
self.webview.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.webview.loadFinished.connect(self.on_webview_loaded)
main_layout.addWidget(self.webview, stretch=10)
# 刷新按钮
btn_row = QHBoxLayout()
btn_row.addStretch(1)
self.refresh_btn = QPushButton("刷新零件库")
self.refresh_btn.setFont(QFont("微软雅黑", 13, QFont.Bold))
self.refresh_btn.setFixedWidth(140)
self.refresh_btn.setStyleSheet("""
QPushButton {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #eaf6fb, stop:1 #d6eaf8);
color: #2980b9;
border-radius: 14px;
font-size: 15px;
font-weight: 600;
padding: 8px 0;
border: 1.5px solid #d6eaf8;
}
QPushButton:hover {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
stop:0 #f8fffe, stop:1 #cfe7fa);
color: #1a6fae;
border: 2px solid #b5d0ea;
}
QPushButton:pressed {
background: #e3f0fa;
color: #2471a3;
border: 2px solid #a4cbe3;
}
""")
self.refresh_btn.clicked.connect(self.reload_webview)
btn_row.addWidget(self.refresh_btn)
btn_row.addStretch(1)
main_layout.addLayout(btn_row)
# 自动加载网页
QTimer.singleShot(200, lambda: self.webview.setUrl(QUrl("http://alist.qutrobot.top")))
self.webview.show()
# 定时刷新(可选,防止页面假死)
self.refresh_timer = QTimer(self)
self.refresh_timer.setInterval(100)
self.refresh_timer.timeout.connect(self.webview.update)
self.refresh_timer.start()
def reload_webview(self):
self.loading_label.show()
self.webview.setUrl(QUrl("http://alist.qutrobot.top"))
def on_webview_loaded(self):
self.loading_label.hide()
def closeEvent(self, event):
self.refresh_timer.stop()
super().closeEvent(event)
# --------- 主工具箱UI ---------
class ToolboxUI(QWidget):
def __init__(self):
@ -1977,7 +2162,7 @@ class ToolboxUI(QWidget):
left_layout.addWidget(logo_label)
# 按钮区
self.button_names = ["主页", "曲线拟合", "Mini串口助手(BUG)", "MR架构配置(开发中)","软件指南"]
self.button_names = ["主页", "曲线拟合", "Mini串口助手", "MR架构配置", "零件库", "软件指南"]
self.buttons = []
for idx, name in enumerate(self.button_names):
btn = QPushButton(name)
@ -2050,7 +2235,8 @@ class ToolboxUI(QWidget):
1: PolyFitApp(), # 多项式拟合
2: SerialAssistant(), # 串口助手
3: GenerateMRobotCode(), # MRobot架构生成
4: DownloadPage(), # 下载页面
4: MachineryLibrary(), # 零件库
5: DownloadPage(), # 下载页面
}
for i in range(len(self.button_names)):
self.stack.addWidget(self.page_widgets[i])
@ -2077,7 +2263,77 @@ class ToolboxUI(QWidget):
self.output_box.append(f"已切换到功能:{self.button_names[idx]}")
if __name__ == "__main__":
from PyQt5.QtWidgets import QSplashScreen, QGraphicsDropShadowEffect
from PyQt5.QtCore import Qt, QTimer, QRect
from PyQt5.QtGui import QPixmap, QPainter, QColor, QFont, QLinearGradient, QBrush
class CustomSplash(QSplashScreen):
def __init__(self, pixmap):
super().__init__(QPixmap(640, 400))
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.logo = pixmap.scaled(360, 240, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.title = "MRobot 工具箱"
self.subtitle = "欢迎使用 MRobot正在启动中..."
self.title_color = QColor("#2471a3")
self.subtitle_color = QColor("#2980b9")
self.bg_gradient = QLinearGradient(0, 0, 0, 400)
self.bg_gradient.setColorAt(0, QColor("#f8fbfd"))
self.bg_gradient.setColorAt(1, QColor("#eaf6fb"))
self.border_color = QColor("#2980b9")
self.setFixedSize(640, 400)
# 阴影
effect = QGraphicsDropShadowEffect(self)
effect.setBlurRadius(36)
effect.setOffset(0, 10)
effect.setColor(QColor(80, 120, 180, 80))
self.setGraphicsEffect(effect)
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# 渐变背景
painter.setBrush(QBrush(self.bg_gradient))
painter.setPen(Qt.NoPen)
painter.drawRoundedRect(0, 0, self.width(), self.height(), 44, 44)
# 边框
pen = painter.pen()
pen.setColor(self.border_color)
pen.setWidth(3)
painter.setPen(pen)
painter.drawRoundedRect(2, 2, self.width()-4, self.height()-4, 44, 44)
# LOGO
logo_x = (self.width() - self.logo.width()) // 2
logo_y = 80
painter.drawPixmap(logo_x, logo_y, self.logo)
# 主标题
# painter.setFont(QFont("微软雅黑", 24, QFont.Bold))
# painter.setPen(self.title_color)
# painter.drawText(QRect(0, 200, self.width(), 48), Qt.AlignCenter, self.title)
# 副标题
painter.setFont(QFont("微软雅黑", 12))
painter.setPen(self.subtitle_color)
painter.drawText(QRect(0, 250, self.width(), 36), Qt.AlignCenter, self.subtitle)
# 版权
painter.setFont(QFont("微软雅黑", 10))
painter.setPen(QColor("#b2bec3"))
painter.drawText(QRect(0, 360, self.width(), 30), Qt.AlignCenter, "© 2025 MRobot. All rights reserved.")
app = QApplication(sys.argv)
win = ToolboxUI()
win.show()
# 立即显示Splash
logo_pix = QPixmap(resource_path("mr_tool_img/MRobot.png"))
splash = CustomSplash(logo_pix)
splash.show()
app.processEvents() # 强制立即刷新Splash
# 异步加载主窗口
def load_main():
win = ToolboxUI()
splash.finish(win)
win.show()
QTimer.singleShot(100, load_main)
sys.exit(app.exec_())

1
MRobot_repo Submodule

@ -0,0 +1 @@
Subproject commit c4731883f241d4748d45ed168520a345a042326e

View File

@ -88,4 +88,6 @@
```bash
pyinstaller --onefile --windowed
pyinstaller MR_Toolbox.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img"
pyinstaller MR_Toolbox.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img"
pyinstaller MR_Tool.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img" --add-data "src;src" --add-data "User;User"

131
src/freertos.c Normal file
View File

@ -0,0 +1,131 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "task/user_task.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
osThreadId_t initTaskHandle; // 定义 Task_Init 的任务句柄
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
initTaskHandle = osThreadNew(Task_Init, NULL, &attr_init); // 创建初始化任务
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
// for(;;)
// {
// osDelay(1);
// }
osThreadTerminate(osThreadGetId()); // 结束自身
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */