SIT/ui/dialogs/codegen_dialog.py

249 lines
8.2 KiB
Python
Raw Permalink Normal View History

2026-01-29 09:22:54 +00:00
"""
代码生成对话框
用于配置和生成代码
"""
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
QLineEdit, QComboBox, QPushButton, QLabel,
QMessageBox, QGroupBox, QFileDialog, QTabWidget,
QTextEdit, QCheckBox
)
from PyQt5.QtCore import Qt, pyqtSignal
from pathlib import Path
from models.message import Message
from config import ProtocolType, SerializationType, LanguageStandard
from controllers.codegen_controller import CodeGenController
from ui.widgets.code_editor_widget import CodeEditorWidget
from utils.logger import get_logger
logger = get_logger(__name__)
class CodeGenDialog(QDialog):
"""代码生成对话框"""
code_generated = pyqtSignal(dict) # 生成的代码文件字典
def __init__(self, parent=None, source_message: Message = None,
target_message: Message = None):
super().__init__(parent)
self.source_message = source_message
self.target_message = target_message
self.codegen_controller = CodeGenController()
self.generated_files = {}
self.init_ui()
self.setWindowTitle("代码生成")
def init_ui(self):
"""初始化UI"""
self.setModal(True)
self.setMinimumSize(900, 700)
# 主布局
main_layout = QVBoxLayout(self)
# 配置组
config_group = QGroupBox("生成配置")
config_layout = QFormLayout(config_group)
# 源消息
self.source_message_label = QLabel(
self.source_message.full_name if self.source_message else "未选择"
)
config_layout.addRow("源消息:", self.source_message_label)
# 目标消息
self.target_message_label = QLabel(
self.target_message.full_name if self.target_message else "未选择"
)
config_layout.addRow("目标消息:", self.target_message_label)
# 传输协议
self.protocol_combo = QComboBox()
for protocol in ProtocolType:
self.protocol_combo.addItem(protocol.value, protocol)
config_layout.addRow("传输协议*:", self.protocol_combo)
# 序列化格式
self.serialization_combo = QComboBox()
for serialization in SerializationType:
self.serialization_combo.addItem(serialization.value, serialization)
config_layout.addRow("序列化格式*:", self.serialization_combo)
# 语言标准
self.language_standard_combo = QComboBox()
for standard in LanguageStandard:
self.language_standard_combo.addItem(standard.value, standard)
self.language_standard_combo.setCurrentText("C++17")
config_layout.addRow("语言标准:", self.language_standard_combo)
# 输出目录
output_layout = QHBoxLayout()
self.output_dir_edit = QLineEdit()
self.output_dir_edit.setPlaceholderText("选择输出目录...")
self.browse_btn = QPushButton("浏览...")
self.browse_btn.clicked.connect(self.on_browse_output_dir)
output_layout.addWidget(self.output_dir_edit)
output_layout.addWidget(self.browse_btn)
config_layout.addRow("输出目录:", output_layout)
# 选项
self.include_test_checkbox = QCheckBox("生成测试代码")
config_layout.addRow("选项:", self.include_test_checkbox)
main_layout.addWidget(config_group)
# 生成按钮
gen_btn_layout = QHBoxLayout()
self.preview_btn = QPushButton("预览代码")
self.preview_btn.clicked.connect(self.on_preview)
self.generate_btn = QPushButton("生成并保存")
self.generate_btn.clicked.connect(self.on_generate)
gen_btn_layout.addWidget(self.preview_btn)
gen_btn_layout.addWidget(self.generate_btn)
gen_btn_layout.addStretch()
main_layout.addLayout(gen_btn_layout)
# 代码预览标签页
self.preview_tabs = QTabWidget()
main_layout.addWidget(self.preview_tabs)
# 底部按钮
button_layout = QHBoxLayout()
self.close_btn = QPushButton("关闭")
self.close_btn.clicked.connect(self.accept)
button_layout.addStretch()
button_layout.addWidget(self.close_btn)
main_layout.addLayout(button_layout)
def on_browse_output_dir(self):
"""浏览输出目录"""
dir_path = QFileDialog.getExistingDirectory(
self,
"选择输出目录",
"",
QFileDialog.ShowDirsOnly
)
if dir_path:
self.output_dir_edit.setText(dir_path)
def validate_config(self) -> tuple[bool, str]:
"""验证配置"""
if not self.source_message:
return False, "请先选择源消息"
if not self.target_message:
return False, "请先选择目标消息"
return True, ""
def build_config(self, include_output_dir: bool = True) -> dict:
"""构建配置字典"""
config = {
'source_message': self.source_message,
'target_message': self.target_message,
'protocol': self.protocol_combo.currentData().value,
'serialization': self.serialization_combo.currentData().value,
'language_standard': self.language_standard_combo.currentData().value,
}
if include_output_dir and self.output_dir_edit.text().strip():
config['output_dir'] = self.output_dir_edit.text().strip()
return config
def on_preview(self):
"""预览代码"""
# 验证配置
is_valid, error_msg = self.validate_config()
if not is_valid:
QMessageBox.warning(self, "验证失败", error_msg)
return
# 生成代码(不保存到文件)
config = self.build_config(include_output_dir=False)
try:
success, msg, files = self.codegen_controller.preview_code(config)
if success:
self.generated_files = files
self.display_preview(files)
QMessageBox.information(self, "成功", f"代码预览生成成功\n生成了 {len(files)} 个文件")
else:
QMessageBox.warning(self, "失败", msg)
except Exception as e:
QMessageBox.critical(self, "错误", f"代码生成失败: {str(e)}")
logger.error(f"Code generation failed: {e}")
def on_generate(self):
"""生成并保存代码"""
# 验证配置
is_valid, error_msg = self.validate_config()
if not is_valid:
QMessageBox.warning(self, "验证失败", error_msg)
return
# 检查输出目录
if not self.output_dir_edit.text().strip():
QMessageBox.warning(self, "警告", "请选择输出目录")
return
# 生成代码
config = self.build_config(include_output_dir=True)
try:
success, msg, files = self.codegen_controller.generate_code(config)
if success:
self.generated_files = files
self.display_preview(files)
self.code_generated.emit(files)
QMessageBox.information(
self,
"成功",
f"代码生成成功!\n"
f"生成了 {len(files)} 个文件\n"
f"保存位置: {self.output_dir_edit.text()}"
)
else:
QMessageBox.warning(self, "失败", msg)
except Exception as e:
QMessageBox.critical(self, "错误", f"代码生成失败: {str(e)}")
logger.error(f"Code generation failed: {e}")
def display_preview(self, files: dict):
"""显示代码预览"""
# 清空现有标签页
self.preview_tabs.clear()
# 为每个文件创建标签页
for filename, content in files.items():
# 创建代码编辑器
if filename.endswith(('.h', '.hpp', '.cpp', '.c')):
editor = CodeEditorWidget('cpp')
else:
editor = QTextEdit()
editor.setPlainText(content)
editor.setReadOnly(True)
# 添加标签页
self.preview_tabs.addTab(editor, filename)
logger.info(f"Displayed preview for {len(files)} files")