mirror of
https://github.com/goldenfishs/MRobot.git
synced 2026-02-04 18:00:19 +08:00
256 lines
9.0 KiB
Python
256 lines
9.0 KiB
Python
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout
|
||
from qfluentwidgets import (
|
||
BodyLabel, CheckBox, SubtitleLabel, PushButton, FluentIcon,
|
||
InfoBar, InfoBarPosition, CardWidget, TitleLabel
|
||
)
|
||
from PyQt5.QtCore import Qt
|
||
from app.tools.code_generator import CodeGenerator
|
||
import os
|
||
import shutil
|
||
import csv
|
||
|
||
|
||
def get_module_page(module_type, subtype, project_path, parent=None):
|
||
"""获取模块配置页面"""
|
||
return ModulePage(module_type, subtype, project_path, parent)
|
||
|
||
|
||
class ModulePage(QWidget):
|
||
"""单个模块配置页面"""
|
||
|
||
def __init__(self, module_type, subtype, project_path, parent=None):
|
||
super().__init__(parent)
|
||
self.module_type = module_type
|
||
self.subtype = subtype
|
||
self.project_path = project_path
|
||
|
||
# 获取模块路径
|
||
module_dir = CodeGenerator.get_assets_dir("User_code/module")
|
||
if subtype:
|
||
self.module_path = os.path.join(module_dir, module_type, subtype)
|
||
self.module_key = module_type # 使用类型名作为目标文件夹
|
||
else:
|
||
self.module_path = os.path.join(module_dir, module_type)
|
||
self.module_key = module_type
|
||
|
||
# 加载描述
|
||
self.descriptions = self._load_descriptions()
|
||
|
||
self._init_ui()
|
||
self._check_generated_status()
|
||
|
||
def _load_descriptions(self):
|
||
"""从 describe.csv 加载模块描述"""
|
||
descriptions = {}
|
||
describe_path = os.path.join(
|
||
CodeGenerator.get_assets_dir("User_code/module"),
|
||
"describe.csv"
|
||
)
|
||
|
||
if os.path.exists(describe_path):
|
||
try:
|
||
with open(describe_path, 'r', encoding='utf-8') as f:
|
||
reader = csv.DictReader(f)
|
||
for row in reader:
|
||
module_name = row.get('module_name', '').strip()
|
||
description = row.get('description', '').strip()
|
||
if module_name and description:
|
||
descriptions[module_name] = description
|
||
except Exception as e:
|
||
print(f"读取模块描述失败: {e}")
|
||
|
||
return descriptions
|
||
|
||
def _init_ui(self):
|
||
"""初始化界面"""
|
||
layout = QVBoxLayout(self)
|
||
layout.setSpacing(16)
|
||
layout.setContentsMargins(48, 48, 48, 48)
|
||
|
||
# 标题
|
||
if self.subtype:
|
||
title_text = f"{self.module_type} / {self.subtype}"
|
||
else:
|
||
title_text = self.module_type
|
||
|
||
title = TitleLabel(title_text)
|
||
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
layout.addWidget(title)
|
||
|
||
# 描述
|
||
desc_text = self.descriptions.get(self.module_key, "模块功能说明")
|
||
desc = BodyLabel(desc_text)
|
||
desc.setWordWrap(True)
|
||
desc.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
layout.addWidget(desc)
|
||
|
||
layout.addSpacing(24)
|
||
|
||
# 文件列表卡片
|
||
files_card = CardWidget()
|
||
files_layout = QVBoxLayout(files_card)
|
||
files_layout.setContentsMargins(16, 16, 16, 16)
|
||
|
||
files_title = SubtitleLabel("包含文件")
|
||
files_layout.addWidget(files_title)
|
||
|
||
files = self._get_module_files()
|
||
if files:
|
||
for file in files:
|
||
file_label = BodyLabel(f"• {file}")
|
||
files_layout.addWidget(file_label)
|
||
else:
|
||
files_layout.addWidget(BodyLabel("未找到文件"))
|
||
|
||
layout.addWidget(files_card)
|
||
|
||
# 状态显示
|
||
self.status_label = BodyLabel()
|
||
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
layout.addWidget(self.status_label)
|
||
|
||
layout.addSpacing(24)
|
||
|
||
# 按钮
|
||
btn_layout = QHBoxLayout()
|
||
btn_layout.addStretch()
|
||
|
||
self.generate_btn = PushButton(FluentIcon.SAVE, "生成模块代码")
|
||
self.generate_btn.clicked.connect(self._generate_code)
|
||
btn_layout.addWidget(self.generate_btn)
|
||
|
||
btn_layout.addStretch()
|
||
layout.addLayout(btn_layout)
|
||
|
||
layout.addStretch()
|
||
|
||
def _get_module_files(self):
|
||
"""获取模块文件列表"""
|
||
files = []
|
||
try:
|
||
if os.path.exists(self.module_path):
|
||
for item in os.listdir(self.module_path):
|
||
if item.endswith(('.c', '.h')):
|
||
files.append(item)
|
||
except Exception as e:
|
||
print(f"读取模块文件失败: {e}")
|
||
|
||
return sorted(files)
|
||
|
||
def _check_generated_status(self):
|
||
"""检查模块是否已生成"""
|
||
dst_dir = os.path.join(self.project_path, "User/module", self.module_key)
|
||
|
||
if os.path.exists(dst_dir):
|
||
has_code = any(
|
||
f.endswith(('.c', '.h'))
|
||
for f in os.listdir(dst_dir)
|
||
if os.path.isfile(os.path.join(dst_dir, f))
|
||
)
|
||
if has_code:
|
||
self.status_label.setText("✓ 模块已生成")
|
||
self.status_label.setStyleSheet("color: green; font-weight: bold;")
|
||
self.generate_btn.setEnabled(False)
|
||
return
|
||
|
||
self.status_label.setText("○ 模块未生成")
|
||
self.status_label.setStyleSheet("color: orange;")
|
||
|
||
def _generate_code(self):
|
||
"""生成模块代码"""
|
||
try:
|
||
# 首先生成 config(如果不存在)
|
||
self._generate_config()
|
||
|
||
# 目标目录:展平到 User/module/{module_key}
|
||
dst_dir = os.path.join(self.project_path, "User/module", self.module_key)
|
||
|
||
# 检查是否已存在
|
||
if os.path.exists(dst_dir):
|
||
has_code = any(
|
||
f.endswith(('.c', '.h'))
|
||
for f in os.listdir(dst_dir)
|
||
if os.path.isfile(os.path.join(dst_dir, f))
|
||
)
|
||
if has_code:
|
||
InfoBar.warning(
|
||
title="已存在",
|
||
content="模块代码已存在,不会覆盖",
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.TOP,
|
||
duration=2000,
|
||
parent=self
|
||
)
|
||
return
|
||
|
||
# 创建目录
|
||
os.makedirs(dst_dir, exist_ok=True)
|
||
|
||
# 复制所有文件
|
||
file_count = 0
|
||
for item in os.listdir(self.module_path):
|
||
src_file = os.path.join(self.module_path, item)
|
||
dst_file = os.path.join(dst_dir, item)
|
||
|
||
if os.path.isfile(src_file):
|
||
if os.path.exists(dst_file):
|
||
continue
|
||
shutil.copy2(src_file, dst_file)
|
||
file_count += 1
|
||
print(f"生成文件: {dst_file}")
|
||
|
||
if file_count > 0:
|
||
InfoBar.success(
|
||
title="生成成功",
|
||
content=f"已生成 {file_count} 个文件",
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.TOP,
|
||
duration=2000,
|
||
parent=self
|
||
)
|
||
self._check_generated_status()
|
||
else:
|
||
InfoBar.warning(
|
||
title="无需生成",
|
||
content="所有文件已存在",
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.TOP,
|
||
duration=2000,
|
||
parent=self
|
||
)
|
||
|
||
except Exception as e:
|
||
print(f"生成模块失败: {e}")
|
||
InfoBar.error(
|
||
title="生成失败",
|
||
content=str(e),
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.TOP,
|
||
duration=3000,
|
||
parent=self
|
||
)
|
||
|
||
def _generate_config(self):
|
||
"""生成 config 文件(如果不存在)"""
|
||
config_dir = os.path.join(self.project_path, "User/module")
|
||
config_c = os.path.join(config_dir, "config.c")
|
||
config_h = os.path.join(config_dir, "config.h")
|
||
|
||
os.makedirs(config_dir, exist_ok=True)
|
||
|
||
template_dir = CodeGenerator.get_assets_dir("User_code/module")
|
||
template_c = os.path.join(template_dir, "config.c")
|
||
template_h = os.path.join(template_dir, "config.h")
|
||
|
||
if not os.path.exists(config_c) and os.path.exists(template_c):
|
||
shutil.copy2(template_c, config_c)
|
||
print(f"生成 config.c")
|
||
|
||
if not os.path.exists(config_h) and os.path.exists(template_h):
|
||
shutil.copy2(template_h, config_h)
|
||
print(f"生成 config.h")
|