SIT/ui/dialogs/message_dialog.py

360 lines
13 KiB
Python

"""
消息编辑对话框
用于创建和编辑消息
"""
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
QLineEdit, QComboBox, QTextEdit, QPushButton,
QLabel, QMessageBox, QGroupBox, QListWidget,
QListWidgetItem, QSplitter, QWidget
)
from PyQt5.QtCore import Qt, pyqtSignal
from models.message import Message
from models.field import Field
from config import ProtocolType, SerializationType
from controllers.field_controller import FieldController
from utils.logger import get_logger
logger = get_logger(__name__)
class MessageDialog(QDialog):
"""消息编辑对话框"""
message_saved = pyqtSignal(Message)
def __init__(self, parent=None, message: Message = None):
super().__init__(parent)
self.message = message
self.field_controller = FieldController()
self.selected_fields = []
self.init_ui()
if message:
self.load_message_data(message)
self.setWindowTitle("编辑消息")
else:
self.setWindowTitle("新建消息")
# 加载可用字段
self.load_available_fields()
def init_ui(self):
"""初始化UI"""
self.setModal(True)
self.setMinimumSize(800, 600)
# 主布局
main_layout = QVBoxLayout(self)
# 基本信息组
basic_group = QGroupBox("基本信息")
basic_layout = QFormLayout(basic_group)
# 完整名称
self.full_name_edit = QLineEdit()
self.full_name_edit.setPlaceholderText("例如: Radar.Receiver.v3")
basic_layout.addRow("完整名称*:", self.full_name_edit)
# 系统名
self.system_name_edit = QLineEdit()
self.system_name_edit.setPlaceholderText("例如: Radar")
basic_layout.addRow("系统名*:", self.system_name_edit)
# 消息类型
self.message_type_edit = QLineEdit()
self.message_type_edit.setPlaceholderText("例如: Receiver")
basic_layout.addRow("消息类型*:", self.message_type_edit)
# 版本号
self.version_edit = QLineEdit()
self.version_edit.setPlaceholderText("例如: v3")
basic_layout.addRow("版本号*:", self.version_edit)
# 协议
self.protocol_combo = QComboBox()
for protocol in ProtocolType:
self.protocol_combo.addItem(protocol.value, protocol)
basic_layout.addRow("传输协议*:", self.protocol_combo)
# 序列化
self.serialization_combo = QComboBox()
for serialization in SerializationType:
self.serialization_combo.addItem(serialization.value, serialization)
basic_layout.addRow("序列化格式*:", self.serialization_combo)
# 描述
self.description_edit = QTextEdit()
self.description_edit.setPlaceholderText("消息的详细描述...")
self.description_edit.setMaximumHeight(60)
basic_layout.addRow("描述:", self.description_edit)
main_layout.addWidget(basic_group)
# 字段选择组
field_group = QGroupBox("字段配置")
field_layout = QVBoxLayout(field_group)
# 创建分割器
splitter = QSplitter(Qt.Horizontal)
# 左侧:可用字段
left_widget = QWidget()
left_layout = QVBoxLayout(left_widget)
left_layout.setContentsMargins(0, 0, 0, 0)
left_label = QLabel("可用字段:")
left_layout.addWidget(left_label)
# 搜索框
self.field_search_edit = QLineEdit()
self.field_search_edit.setPlaceholderText("搜索字段...")
self.field_search_edit.textChanged.connect(self.filter_available_fields)
left_layout.addWidget(self.field_search_edit)
self.available_fields_list = QListWidget()
self.available_fields_list.setSelectionMode(QListWidget.ExtendedSelection)
left_layout.addWidget(self.available_fields_list)
splitter.addWidget(left_widget)
# 中间:操作按钮
middle_widget = QWidget()
middle_layout = QVBoxLayout(middle_widget)
middle_layout.setContentsMargins(5, 0, 5, 0)
middle_layout.addStretch()
self.add_field_btn = QPushButton(">>")
self.add_field_btn.setMaximumWidth(50)
self.add_field_btn.clicked.connect(self.on_add_fields)
middle_layout.addWidget(self.add_field_btn)
self.remove_field_btn = QPushButton("<<")
self.remove_field_btn.setMaximumWidth(50)
self.remove_field_btn.clicked.connect(self.on_remove_fields)
middle_layout.addWidget(self.remove_field_btn)
middle_layout.addStretch()
splitter.addWidget(middle_widget)
# 右侧:已选字段
right_widget = QWidget()
right_layout = QVBoxLayout(right_widget)
right_layout.setContentsMargins(0, 0, 0, 0)
right_label = QLabel("已选字段:")
right_layout.addWidget(right_label)
self.selected_fields_list = QListWidget()
self.selected_fields_list.setSelectionMode(QListWidget.ExtendedSelection)
right_layout.addWidget(self.selected_fields_list)
# 排序按钮
order_layout = QHBoxLayout()
self.move_up_btn = QPushButton("上移")
self.move_up_btn.clicked.connect(self.on_move_up)
self.move_down_btn = QPushButton("下移")
self.move_down_btn.clicked.connect(self.on_move_down)
order_layout.addWidget(self.move_up_btn)
order_layout.addWidget(self.move_down_btn)
order_layout.addStretch()
right_layout.addLayout(order_layout)
splitter.addWidget(right_widget)
splitter.setStretchFactor(0, 1)
splitter.setStretchFactor(1, 0)
splitter.setStretchFactor(2, 1)
field_layout.addWidget(splitter)
main_layout.addWidget(field_group)
# 提示信息
hint_label = QLabel("* 标记为必填项,消息至少需要包含一个字段")
hint_label.setStyleSheet("color: #666666; font-size: 11px;")
main_layout.addWidget(hint_label)
# 按钮
button_layout = QHBoxLayout()
self.save_btn = QPushButton("保存")
self.save_btn.clicked.connect(self.on_save)
self.save_btn.setDefault(True)
self.cancel_btn = QPushButton("取消")
self.cancel_btn.clicked.connect(self.reject)
button_layout.addStretch()
button_layout.addWidget(self.save_btn)
button_layout.addWidget(self.cancel_btn)
main_layout.addLayout(button_layout)
# 连接信号:自动生成完整名称
self.system_name_edit.textChanged.connect(self.update_full_name)
self.message_type_edit.textChanged.connect(self.update_full_name)
self.version_edit.textChanged.connect(self.update_full_name)
def update_full_name(self):
"""自动更新完整名称"""
system_name = self.system_name_edit.text().strip()
message_type = self.message_type_edit.text().strip()
version = self.version_edit.text().strip()
if system_name and message_type and version:
full_name = f"{system_name}.{message_type}.{version}"
self.full_name_edit.setText(full_name)
def load_available_fields(self):
"""加载可用字段"""
fields, _ = self.field_controller.get_all_fields(page=1, page_size=1000)
self.available_fields_list.clear()
for field in fields:
item = QListWidgetItem(f"{field.full_name} ({field.type.value})")
item.setData(Qt.UserRole, field)
self.available_fields_list.addItem(item)
def filter_available_fields(self, text: str):
"""过滤可用字段"""
for i in range(self.available_fields_list.count()):
item = self.available_fields_list.item(i)
item.setHidden(text.lower() not in item.text().lower())
def load_message_data(self, message: Message):
"""加载消息数据"""
self.full_name_edit.setText(message.full_name)
self.system_name_edit.setText(message.system_name)
self.message_type_edit.setText(message.message_type)
self.version_edit.setText(message.version)
# 设置协议
index = self.protocol_combo.findData(message.protocol)
if index >= 0:
self.protocol_combo.setCurrentIndex(index)
# 设置序列化
index = self.serialization_combo.findData(message.serialization)
if index >= 0:
self.serialization_combo.setCurrentIndex(index)
if message.description:
self.description_edit.setText(message.description)
# 加载已选字段
self.selected_fields = message.fields.copy()
for field in self.selected_fields:
item = QListWidgetItem(f"{field.full_name} ({field.type.value})")
item.setData(Qt.UserRole, field)
self.selected_fields_list.addItem(item)
def on_add_fields(self):
"""添加字段"""
selected_items = self.available_fields_list.selectedItems()
for item in selected_items:
field = item.data(Qt.UserRole)
# 检查是否已存在
if any(f.id == field.id for f in self.selected_fields):
continue
self.selected_fields.append(field)
new_item = QListWidgetItem(f"{field.full_name} ({field.type.value})")
new_item.setData(Qt.UserRole, field)
self.selected_fields_list.addItem(new_item)
def on_remove_fields(self):
"""移除字段"""
selected_items = self.selected_fields_list.selectedItems()
for item in selected_items:
field = item.data(Qt.UserRole)
# 从列表中移除
if field in self.selected_fields:
self.selected_fields.remove(field)
# 从UI中移除
row = self.selected_fields_list.row(item)
self.selected_fields_list.takeItem(row)
def on_move_up(self):
"""上移字段"""
current_row = self.selected_fields_list.currentRow()
if current_row > 0:
item = self.selected_fields_list.takeItem(current_row)
self.selected_fields_list.insertItem(current_row - 1, item)
self.selected_fields_list.setCurrentRow(current_row - 1)
# 更新字段列表
self.selected_fields[current_row], self.selected_fields[current_row - 1] = \
self.selected_fields[current_row - 1], self.selected_fields[current_row]
def on_move_down(self):
"""下移字段"""
current_row = self.selected_fields_list.currentRow()
if current_row < self.selected_fields_list.count() - 1:
item = self.selected_fields_list.takeItem(current_row)
self.selected_fields_list.insertItem(current_row + 1, item)
self.selected_fields_list.setCurrentRow(current_row + 1)
# 更新字段列表
self.selected_fields[current_row], self.selected_fields[current_row + 1] = \
self.selected_fields[current_row + 1], self.selected_fields[current_row]
def validate_input(self) -> tuple[bool, str]:
"""验证输入"""
if not self.full_name_edit.text().strip():
return False, "完整名称不能为空"
if not self.system_name_edit.text().strip():
return False, "系统名不能为空"
if not self.message_type_edit.text().strip():
return False, "消息类型不能为空"
if not self.version_edit.text().strip():
return False, "版本号不能为空"
if len(self.selected_fields) == 0:
return False, "消息至少需要包含一个字段"
return True, ""
def on_save(self):
"""保存消息"""
# 验证输入
is_valid, error_msg = self.validate_input()
if not is_valid:
QMessageBox.warning(self, "验证失败", error_msg)
return
# 构建消息对象
message_data = {
'full_name': self.full_name_edit.text().strip(),
'system_name': self.system_name_edit.text().strip(),
'message_type': self.message_type_edit.text().strip(),
'version': self.version_edit.text().strip(),
'protocol': self.protocol_combo.currentData().value,
'serialization': self.serialization_combo.currentData().value,
'description': self.description_edit.toPlainText().strip() or None,
'fields': [f.to_dict() for f in self.selected_fields],
}
if self.message:
message_data['id'] = self.message.id
try:
message = Message.from_dict(message_data)
self.message_saved.emit(message)
self.accept()
except Exception as e:
QMessageBox.critical(self, "错误", f"创建消息对象失败: {str(e)}")
logger.error(f"Failed to create message object: {e}")