# -*- coding: utf-8 -*- """Main tab views.""" from util.runtime import * from util.utils import node_host_only, _server_id_sort_key from models.config import Config, apply_terminal_launch_config from controllers.workers import ( CommandRunner, GroupedNodeStatusCheckWorker, NodeStatusCheckWorker, StopServerWorker, ) from views.common import ( NoWheelComboBox, NoWheelDateTimeEdit, NoWheelSpinBox, ServerIdTableItem, apply_button_style, make_line_icon, set_button_icon, ) from views.console import OutputConsole from views.dialogs import ( ClearDatabaseDialog, KvConfigEditDialog, RemoteConnectDialog, ServerSelectDialog, ViewAccountsDialog, ) class DefaultKvTab(QWidget): """默认配置编辑页面 - 编辑 .server_manager/config/default.config""" def __init__(self, config: Config, console: OutputConsole): super().__init__() self.config = config self.console = console self.default_kv_path = None self.kv_data = {} self.init_ui() self.load_default_kv() def init_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # 标题和说明 title = QLabel("默认配置管理 (.server_manager/config/default.config)") title.setStyleSheet("font-size: 16px; font-weight: bold; color: #25c889;") layout.addWidget(title) desc = QLabel("此文件定义创建服务器时的默认值,支持 ${key} 占位符替换") desc.setStyleSheet("color: #aab4c3;") layout.addWidget(desc) # 分割器 splitter = QSplitter(Qt.Orientation.Horizontal) # 左侧:配置分类列表 left_widget = QWidget() left_layout = QVBoxLayout(left_widget) left_layout.setContentsMargins(0, 0, 0, 0) left_label = QLabel("配置分类") left_label.setStyleSheet("font-weight: bold;") left_layout.addWidget(left_label) self.category_list = QListWidget() self.category_list.setMaximumWidth(200) self.category_list.currentRowChanged.connect(self._on_category_changed) left_layout.addWidget(self.category_list) # 新增配置按钮 add_btn = QPushButton("+ 新增配置项") add_btn.clicked.connect(self._add_config_item) apply_button_style(add_btn, "outline") set_button_icon(add_btn, "plus", "#4f8cff", 16) left_layout.addWidget(add_btn) splitter.addWidget(left_widget) # 右侧:配置编辑区 right_widget = QWidget() right_layout = QVBoxLayout(right_widget) right_layout.setContentsMargins(0, 0, 0, 0) right_label = QLabel("配置项") right_label.setStyleSheet("font-weight: bold;") right_layout.addWidget(right_label) # 配置表格 scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setFrameShape(QFrame.Shape.NoFrame) self.config_widget = QWidget() self.config_layout = QFormLayout(self.config_widget) self.config_layout.setSpacing(10) scroll.setWidget(self.config_widget) right_layout.addWidget(scroll) splitter.addWidget(right_widget) splitter.setSizes([200, 600]) layout.addWidget(splitter) # 底部按钮 btn_layout = QHBoxLayout() reload_btn = QPushButton("重新加载") reload_btn.clicked.connect(self.load_default_kv) apply_button_style(reload_btn, "outline") set_button_icon(reload_btn, "refresh", "#4f8cff", 16) btn_layout.addWidget(reload_btn) btn_layout.addStretch() save_btn = QPushButton("保存配置") apply_button_style(save_btn, "primary") set_button_icon(save_btn, "save", "#242932", 16) save_btn.clicked.connect(self.save_default_kv) btn_layout.addWidget(save_btn) layout.addLayout(btn_layout) def load_default_kv(self): """加载 default.config 配置""" server_root = self.config.get('workspace', 'server_root', default='') if not server_root: self.console.append_output("[警告] 请先配置服务器根目录\n") return load_path = find_existing_default_config_path(server_root) self.default_kv_path = get_default_config_path(server_root) if not load_path: self.console.append_output(f"[警告] 默认配置文件不存在: {self.default_kv_path}\n") return # 读取配置文件 self.kv_data = {} self.categories = {} current_category = "其他" with open(load_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() if line.startswith('# ===') and line.endswith('==='): # 分类标题 current_category = line.replace('# ===', '').replace('===', '').strip() if current_category not in self.categories: self.categories[current_category] = [] elif '=' in line and not line.startswith('#'): key, value = line.split('=', 1) key = key.strip() value = value.strip() self.kv_data[key] = value if current_category not in self.categories: self.categories[current_category] = [] self.categories[current_category].append(key) # 更新分类列表 self.category_list.clear() for cat in self.categories.keys(): self.category_list.addItem(cat) if self.category_list.count() > 0: self.category_list.setCurrentRow(0) self.console.append_output(f"[信息] 已加载默认配置: {self.default_kv_path}\n") def _on_category_changed(self, row): """分类切换时更新配置项""" if row < 0: return category = self.category_list.item(row).text() keys = self.categories.get(category, []) # 清空现有配置项 while self.config_layout.count(): item = self.config_layout.takeAt(0) if item.widget(): item.widget().deleteLater() # 添加配置项 self.config_inputs = {} for key in keys: value = self.kv_data.get(key, '') input_layout = QHBoxLayout() input_field = QLineEdit(value) input_field.setMinimumHeight(28) input_layout.addWidget(input_field) # 删除按钮 del_btn = QPushButton("×") del_btn.setFixedSize(28, 28) apply_button_style(del_btn, "danger", compact=True) set_button_icon(del_btn, "trash", "#ff5c6a", 14) del_btn.clicked.connect(lambda checked, k=key: self._delete_config_item(k)) input_layout.addWidget(del_btn) input_widget = QWidget() input_widget.setLayout(input_layout) self.config_layout.addRow(f"{key}:", input_widget) self.config_inputs[key] = input_field def _add_config_item(self): """新增配置项""" from PyQt6.QtWidgets import QInputDialog key, ok1 = QInputDialog.getText(self, "新增配置项", "配置键名:") if not ok1 or not key: return value, ok2 = QInputDialog.getText(self, "新增配置项", f"{key} 的值:") if not ok2: return # 获取当前分类 current_row = self.category_list.currentRow() if current_row >= 0: category = self.category_list.item(current_row).text() else: category = "其他" # 添加到数据 self.kv_data[key] = value if category not in self.categories: self.categories[category] = [] if key not in self.categories[category]: self.categories[category].append(key) # 刷新显示 self._on_category_changed(current_row) self.console.append_output(f"[信息] 已添加配置项: {key}={value}\n") def _delete_config_item(self, key): """删除配置项""" reply = QMessageBox.question( self, "确认删除", f"确定要删除配置项 '{key}' 吗?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply != QMessageBox.StandardButton.Yes: return # 从数据中删除 if key in self.kv_data: del self.kv_data[key] for cat, keys in self.categories.items(): if key in keys: keys.remove(key) break # 刷新显示 current_row = self.category_list.currentRow() self._on_category_changed(current_row) self.console.append_output(f"[信息] 已删除配置项: {key}\n") def save_default_kv(self): """保存配置到 default.config""" if not self.default_kv_path: QMessageBox.warning(self, "警告", "请先加载配置文件") return # 更新数据 if hasattr(self, 'config_inputs'): for key, input_field in self.config_inputs.items(): self.kv_data[key] = input_field.text() # 生成文件内容 lines = [ "# 默认配置文件 (kv.config 的模板)", "# 此文件定义所有可配置的参数和默认值", "# 创建服务器时会基于此模板生成差异化配置", "# 占位符格式: ${key} 对应此文件中的 key=value", "" ] for category, keys in self.categories.items(): if keys: lines.append(f"# === {category} ===") for key in keys: value = self.kv_data.get(key, '') lines.append(f"{key}={value}") lines.append("") # 写入文件 try: with open(self.default_kv_path, 'w', encoding='utf-8') as f: f.write('\n'.join(lines)) self.console.append_output(f"[成功] 配置已保存: {self.default_kv_path}\n") QMessageBox.information(self, "成功", "默认配置已保存") except Exception as e: self.console.append_output(f"[错误] 保存失败: {str(e)}\n") QMessageBox.critical(self, "错误", f"保存失败: {str(e)}")