Files
server_manager/src/views/tabs/default_kv_tab.py
T
2026-06-09 14:01:20 +08:00

300 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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)}")