合并前的最终提交

This commit is contained in:
Robofish 2025-08-18 02:08:55 +08:00
parent 898c5dfb2b
commit 266868ad71

View File

@ -1,92 +1,209 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QSizePolicy, QTreeWidget, QTreeWidgetItem, QStackedWidget from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QSizePolicy, QTreeWidget, QTreeWidgetItem, QStackedWidget
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QThread, pyqtSignal
from qfluentwidgets import TitleLabel, BodyLabel, PushButton, TreeWidget, FluentIcon, InfoBar from qfluentwidgets import TitleLabel, BodyLabel, PushButton, TreeWidget, FluentIcon, InfoBar
from app.tools.analyzing_ioc import analyzing_ioc from app.tools.analyzing_ioc import analyzing_ioc
from app.code_page.bsp_interface import bsp from app.code_page.bsp_interface import bsp
from app.data_interface import DataInterface from app.data_interface import DataInterface
<<<<<<< HEAD
from app.code_page.bsp_interface import bsp
from app.code_page.component_interface import component
from app.code_page.device_interface import device
=======
from app.tools.code_generator import CodeGenerator from app.tools.code_generator import CodeGenerator
>>>>>>> temp-merge-branch
import os import os
import csv import csv
import sys import sys
import importlib import importlib
def resource_path(relative_path):
"""获取资源文件的绝对路径,兼容打包和开发环境"""
try:
if hasattr(sys, '_MEIPASS'):
base_path = sys._MEIPASS
return os.path.join(base_path, relative_path)
current_dir = os.path.dirname(os.path.abspath(__file__))
while current_dir != os.path.dirname(current_dir):
if os.path.basename(current_dir) == 'MRobot':
break
current_dir = os.path.dirname(current_dir)
return os.path.join(current_dir, relative_path)
except Exception as e:
print(f"资源路径获取失败: {e}")
current_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(current_dir, "..", relative_path)
class CodeGenerateThread(QThread):
finished = pyqtSignal(str, str) # (状态, 信息)
def __init__(self, project_path, page_cache, component_manager, stack):
super().__init__()
self.project_path = project_path
self.page_cache = page_cache
self.component_manager = component_manager
self.stack = stack
def run(self):
try:
csv_path = resource_path("assets/User_code/config.csv")
csv_path = os.path.abspath(csv_path)
if not os.path.exists(csv_path):
self.finished.emit("error", f"未找到配置文件: {csv_path}")
return
all_class_names = []
try:
with open(csv_path, newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
row = [cell.strip() for cell in row if cell.strip()]
if not row:
continue
main_title = row[0]
for sub in row[1:]:
class_name = f"{main_title}_{sub}".replace("-", "_")
all_class_names.append(class_name)
except Exception as e:
self.finished.emit("error", f"读取配置文件时出错: {str(e)}")
return
try:
test_file = os.path.join(self.project_path, "test_write_permission.tmp")
with open(test_file, 'w') as f:
f.write("test")
os.remove(test_file)
except Exception as e:
self.finished.emit("error", f"无法写入项目目录,请检查权限: {str(e)}")
return
try:
from app.code_page.bsp_interface import get_bsp_page
from app.code_page.component_interface import get_component_page, ComponentManager
from app.code_page.device_interface import get_device_page
except Exception as e:
self.finished.emit("error", f"模块导入失败: {str(e)}")
return
if not self.component_manager:
self.component_manager = ComponentManager()
bsp_pages = []
component_pages = []
device_pages = []
for class_name in all_class_names:
page = None
try:
if class_name in self.page_cache:
page = self.page_cache[class_name]
else:
if class_name.startswith('bsp_'):
periph_name = class_name[len('bsp_'):]
page = get_bsp_page(periph_name, self.project_path)
elif class_name.startswith('component_'):
comp_name = class_name[len('component_'):]
page = get_component_page(comp_name, self.project_path, self.component_manager)
if page and hasattr(page, 'component_name'):
self.component_manager.register_component(page.component_name, page)
elif class_name.startswith('device_'):
device_name = class_name[len('device_'):]
page = get_device_page(device_name, self.project_path)
if page:
self.page_cache[class_name] = page
self.stack.addWidget(page)
except Exception:
continue
if page:
if hasattr(page, '_generate_bsp_code_internal') and page not in bsp_pages:
bsp_pages.append(page)
elif hasattr(page, '_generate_component_code_internal') and page not in component_pages:
component_pages.append(page)
elif hasattr(page, '_generate_device_code_internal') and page not in device_pages:
device_pages.append(page)
try:
bsp_result = bsp.generate_bsp(self.project_path, bsp_pages) if bsp_pages else ""
except Exception:
bsp_result = ""
try:
component_result = component.generate_component(self.project_path, component_pages) if component_pages else ""
except Exception:
component_result = ""
try:
device_result = device.generate_device(self.project_path, device_pages) if device_pages else ""
except Exception:
device_result = ""
if not bsp_result and not component_result and not device_result:
self.finished.emit("warning", "未生成任何代码,请检查是否启用了相关模块!")
return
results = []
if bsp_result:
results.append(f"BSP: {bsp_result}")
if component_result:
results.append(f"Component: {component_result}")
if device_result:
results.append(f"Device: {device_result}")
combined_result = "\n".join(results)
self.finished.emit("success", combined_result)
except Exception as e:
self.finished.emit("error", f"代码生成过程中出现错误: {str(e)}")
class CodeGenerateInterface(QWidget): class CodeGenerateInterface(QWidget):
def __init__(self, project_path, parent=None): def __init__(self, project_path, parent=None):
super().__init__(parent) super().__init__(parent)
self.setObjectName("CodeGenerateInterface") self.setObjectName("CodeGenerateInterface")
self.project_path = project_path self.project_path = project_path
# 初始化页面缓存
self.page_cache = {} self.page_cache = {}
self.component_manager = None
self._init_ui() self._init_ui()
def _init_ui(self): def _init_ui(self):
main_layout = QVBoxLayout(self) main_layout = QVBoxLayout(self)
main_layout.setAlignment(Qt.AlignTop) main_layout.setAlignment(Qt.AlignTop)
main_layout.setContentsMargins(10, 10, 10, 10) main_layout.setContentsMargins(10, 10, 10, 10)
top_layout = self._create_top_layout() top_layout = self._create_top_layout()
main_layout.addLayout(top_layout) main_layout.addLayout(top_layout)
content_layout = QHBoxLayout() content_layout = QHBoxLayout()
content_layout.setContentsMargins(0, 10, 0, 0) content_layout.setContentsMargins(0, 10, 0, 0)
main_layout.addLayout(content_layout) main_layout.addLayout(content_layout)
# 左侧树形列表使用qfluentwidgets的TreeWidget
self.tree = TreeWidget() self.tree = TreeWidget()
self.tree.setHeaderHidden(True) self.tree.setHeaderHidden(True)
self.tree.setMaximumWidth(250) self.tree.setMaximumWidth(250)
self.tree.setBorderRadius(8) self.tree.setBorderRadius(8)
self.tree.setBorderVisible(True) self.tree.setBorderVisible(True)
content_layout.addWidget(self.tree) content_layout.addWidget(self.tree)
# 右侧内容区
self.stack = QStackedWidget() self.stack = QStackedWidget()
content_layout.addWidget(self.stack) content_layout.addWidget(self.stack)
self._load_csv_and_build_tree() self._load_csv_and_build_tree()
self.tree.itemClicked.connect(self.on_tree_item_clicked) self.tree.itemClicked.connect(self.on_tree_item_clicked)
def _create_top_layout(self): def _create_top_layout(self):
"""创建顶部横向布局"""
top_layout = QHBoxLayout() top_layout = QHBoxLayout()
top_layout.setAlignment(Qt.AlignTop) top_layout.setAlignment(Qt.AlignTop)
# 项目名称标签
project_name = os.path.basename(self.project_path) project_name = os.path.basename(self.project_path)
name_label = BodyLabel(f"项目名称: {project_name}") name_label = BodyLabel(f"项目名称: {project_name}")
name_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) name_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
top_layout.addWidget(name_label) top_layout.addWidget(name_label)
# FreeRTOS状态标签
freertos_label = BodyLabel(f"FreeRTOS: {self._get_freertos_status()}") freertos_label = BodyLabel(f"FreeRTOS: {self._get_freertos_status()}")
freertos_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) freertos_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
top_layout.addWidget(freertos_label) top_layout.addWidget(freertos_label)
# 自动生成FreeRTOS任务按钮
auto_task_btn = PushButton(FluentIcon.SEND, "自动生成FreeRTOS任务") auto_task_btn = PushButton(FluentIcon.SEND, "自动生成FreeRTOS任务")
auto_task_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) auto_task_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
auto_task_btn.clicked.connect(self.on_freertos_task_btn_clicked) auto_task_btn.clicked.connect(self.on_freertos_task_btn_clicked)
top_layout.addWidget(auto_task_btn, alignment=Qt.AlignRight) top_layout.addWidget(auto_task_btn, alignment=Qt.AlignRight)
# 配置并生成FreeRTOS任务按钮
freertos_task_btn = PushButton(FluentIcon.SETTING, "配置并生成FreeRTOS任务") freertos_task_btn = PushButton(FluentIcon.SETTING, "配置并生成FreeRTOS任务")
freertos_task_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) freertos_task_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
freertos_task_btn.clicked.connect(self.on_task_code_btn_clicked) freertos_task_btn.clicked.connect(self.on_task_code_btn_clicked)
top_layout.addWidget(freertos_task_btn, alignment=Qt.AlignRight) top_layout.addWidget(freertos_task_btn, alignment=Qt.AlignRight)
self.generate_btn = PushButton(FluentIcon.PROJECTOR,"生成代码")
# 生成代码按钮 self.generate_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
generate_btn = PushButton(FluentIcon.PROJECTOR,"生成代码") self.generate_btn.clicked.connect(self.generate_code)
generate_btn.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) top_layout.addWidget(self.generate_btn, alignment=Qt.AlignRight)
generate_btn.clicked.connect(self.generate_code)
top_layout.addWidget(generate_btn, alignment=Qt.AlignRight)
return top_layout return top_layout
def on_task_code_btn_clicked(self): def on_task_code_btn_clicked(self):
# 检查是否开启 FreeRTOS
ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')] ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')]
if ioc_files: if ioc_files:
ioc_path = os.path.join(self.project_path, ioc_files[0]) ioc_path = os.path.join(self.project_path, ioc_files[0])
@ -106,12 +223,9 @@ class CodeGenerateInterface(QWidget):
duration=3000 duration=3000
) )
return return
# 直接弹出任务配置对话框并生成代码
dlg = DataInterface() dlg = DataInterface()
dlg.project_path = self.project_path dlg.project_path = self.project_path
result = dlg.open_task_config_dialog() result = dlg.open_task_config_dialog()
# 生成任务成功后弹出 InfoBar 提示
if getattr(dlg, "task_generate_success", False): if getattr(dlg, "task_generate_success", False):
InfoBar.success( InfoBar.success(
title="任务生成成功", title="任务生成成功",
@ -121,7 +235,6 @@ class CodeGenerateInterface(QWidget):
) )
def on_freertos_task_btn_clicked(self): def on_freertos_task_btn_clicked(self):
# 检查是否开启 FreeRTOS
ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')] ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')]
if ioc_files: if ioc_files:
ioc_path = os.path.join(self.project_path, ioc_files[0]) ioc_path = os.path.join(self.project_path, ioc_files[0])
@ -141,8 +254,6 @@ class CodeGenerateInterface(QWidget):
duration=3000 duration=3000
) )
return return
# 自动生成FreeRTOS任务代码
from app.data_interface import DataInterface from app.data_interface import DataInterface
di = DataInterface() di = DataInterface()
di.project_path = self.project_path di.project_path = self.project_path
@ -154,6 +265,8 @@ class CodeGenerateInterface(QWidget):
duration=2000 duration=2000
) )
<<<<<<< HEAD
=======
def generate_code(self): def generate_code(self):
@ -228,8 +341,8 @@ class CodeGenerateInterface(QWidget):
) )
>>>>>>> temp-merge-branch
def _get_freertos_status(self): def _get_freertos_status(self):
"""获取FreeRTOS状态"""
ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')] ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')]
if ioc_files: if ioc_files:
ioc_path = os.path.join(self.project_path, ioc_files[0]) ioc_path = os.path.join(self.project_path, ioc_files[0])
@ -237,7 +350,12 @@ class CodeGenerateInterface(QWidget):
return "未找到.ioc文件" return "未找到.ioc文件"
def _load_csv_and_build_tree(self): def _load_csv_and_build_tree(self):
<<<<<<< HEAD
csv_path = resource_path("assets/User_code/config.csv")
csv_path = os.path.abspath(csv_path)
=======
csv_path = os.path.join(CodeGenerator.get_assets_dir("User_code"), "config.csv") csv_path = os.path.join(CodeGenerator.get_assets_dir("User_code"), "config.csv")
>>>>>>> temp-merge-branch
print(f"加载CSV路径: {csv_path}") print(f"加载CSV路径: {csv_path}")
if not os.path.exists(csv_path): if not os.path.exists(csv_path):
print(f"配置文件未找到: {csv_path}") print(f"配置文件未找到: {csv_path}")
@ -266,39 +384,45 @@ class CodeGenerateInterface(QWidget):
if widget: if widget:
self.stack.setCurrentWidget(widget) self.stack.setCurrentWidget(widget)
def generate_code(self):
self.generate_btn.setEnabled(False)
self.thread = CodeGenerateThread(self.project_path, self.page_cache, self.component_manager, self.stack)
self.thread.finished.connect(self.on_code_generate_finished)
self.thread.start()
def on_code_generate_finished(self, status, msg):
self.generate_btn.setEnabled(True)
if status == "success":
InfoBar.success(title="代码生成成功", content=msg, parent=self, duration=5000)
elif status == "warning":
InfoBar.warning(title="无代码生成", content=msg, parent=self, duration=3000)
else:
InfoBar.error(title="生成失败", content=msg, parent=self, duration=3000)
def _get_or_create_page(self, class_name): def _get_or_create_page(self, class_name):
"""获取或创建页面"""
if class_name in self.page_cache: if class_name in self.page_cache:
return self.page_cache[class_name] return self.page_cache[class_name]
# 如果是第一次创建组件页面,初始化组件管理器
if not hasattr(self, 'component_manager'):
from app.code_page.component_interface import ComponentManager
self.component_manager = ComponentManager()
try: try:
from app.code_page.bsp_interface import get_bsp_page
from app.code_page.component_interface import get_component_page, ComponentManager
from app.code_page.device_interface import get_device_page
page = None
if class_name.startswith('bsp_'): if class_name.startswith('bsp_'):
# BSP页面 periph_name = class_name[len('bsp_'):]
from app.code_page.bsp_interface import get_bsp_page
# 提取外设名,如 bsp_error_detect -> error_detect
periph_name = class_name[len('bsp_'):] # 移除 .replace("_", " ")
page = get_bsp_page(periph_name, self.project_path) page = get_bsp_page(periph_name, self.project_path)
elif class_name.startswith('component_'): elif class_name.startswith('component_'):
from app.code_page.component_interface import get_component_page comp_name = class_name[len('component_'):]
comp_name = class_name[len('component_'):] # 移除 .replace("_", " ") if not self.component_manager:
self.component_manager = ComponentManager()
page = get_component_page(comp_name, self.project_path, self.component_manager) page = get_component_page(comp_name, self.project_path, self.component_manager)
self.component_manager.register_component(page.component_name, page) if page and hasattr(page, 'component_name'):
self.component_manager.register_component(page.component_name, page)
elif class_name.startswith('device_'): elif class_name.startswith('device_'):
# Device页面 device_name = class_name[len('device_'):]
from app.code_page.device_interface import get_device_page
device_name = class_name[len('device_'):] # 移除 device_ 前缀
page = get_device_page(device_name, self.project_path) page = get_device_page(device_name, self.project_path)
else: if page:
print(f"未知的页面类型: {class_name}") self.page_cache[class_name] = page
return None self.stack.addWidget(page)
self.page_cache[class_name] = page
self.stack.addWidget(page)
return page return page
except Exception as e: except Exception as e:
print(f"创建页面 {class_name} 失败: {e}") print(f"创建页面 {class_name} 失败: {e}")