300 lines
11 KiB
Python
300 lines
11 KiB
Python
# -*- 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)}")
|