from PySide6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QLineEdit, QFrame, QMessageBox, QStatusBar, QDialog) from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QFont, QIcon, QAction from vm_manager import VMManager from config_dialog import ConfigDialog from styles import DARK_STYLE, CARD_STYLE class StatCard(QFrame): def __init__(self, title: str, parent=None): super().__init__(parent) self.setObjectName("stat_card") self.setStyleSheet(CARD_STYLE) self.setMinimumSize(150, 100) layout = QVBoxLayout(self) layout.setSpacing(5) layout.setContentsMargins(15, 15, 15, 15) self.value_label = QLabel("0") self.value_label.setObjectName("stat_value") self.value_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.title_label = QLabel(title) self.title_label.setObjectName("stat_title") self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.value_label) layout.addWidget(self.title_label) def set_value(self, value: str): self.value_label.setText(str(value)) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.vm_manager = VMManager() self.vms = [] self.setup_ui() def setup_ui(self): self.setWindowTitle("企业级虚拟化管理平台") self.setMinimumSize(1200, 800) self.setStyleSheet(DARK_STYLE) central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setSpacing(0) main_layout.setContentsMargins(20, 20, 20, 20) title_layout = QHBoxLayout() title_label = QLabel("企业级虚拟化管理平台") title_label.setObjectName("title_label") title_layout.addWidget(title_label) title_layout.addStretch() config_btn = QPushButton("配置") config_btn.setObjectName("config_button") config_btn.clicked.connect(self.open_config) title_layout.addWidget(config_btn) main_layout.addLayout(title_layout) toolbar_layout = QHBoxLayout() toolbar_layout.addStretch() self.connect_btn = QPushButton("连接") self.connect_btn.setObjectName("connect_button") self.connect_btn.setFixedSize(120, 45) self.connect_btn.clicked.connect(self.connect_and_load) toolbar_layout.addWidget(self.connect_btn) self.refresh_btn = QPushButton("刷新") self.refresh_btn.setFixedSize(120, 45) self.refresh_btn.clicked.connect(self.refresh_vms) toolbar_layout.addWidget(self.refresh_btn) main_layout.addLayout(toolbar_layout) stats_layout = QHBoxLayout() stats_layout.setSpacing(15) self.total_card = StatCard("虚拟机总数") stats_layout.addWidget(self.total_card) self.running_card = StatCard("运行中") stats_layout.addWidget(self.running_card) self.stopped_card = StatCard("已停止") stats_layout.addWidget(self.stopped_card) self.paused_card = StatCard("已暂停") stats_layout.addWidget(self.paused_card) main_layout.addLayout(stats_layout) list_section_layout = QVBoxLayout() list_section_layout.setSpacing(10) section_label = QLabel("虚拟机列表") section_label.setObjectName("section_label") list_section_layout.addWidget(section_label) search_layout = QHBoxLayout() self.search_edit = QLineEdit() self.search_edit.setPlaceholderText("搜索虚拟机...") self.search_edit.textChanged.connect(self.filter_vms) search_layout.addWidget(self.search_edit) list_section_layout.addLayout(search_layout) main_layout.addLayout(list_section_layout) self.table = QTableWidget() self.table.setColumnCount(8) self.table.verticalHeader().setDefaultSectionSize(100) self.table.setHorizontalHeaderLabels(["VMID", "名称", "状态", "CPU", "内存", "磁盘", "运行时间", "操作"]) self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed) self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Fixed) self.table.horizontalHeader().setSectionResizeMode(7, QHeaderView.ResizeMode.Interactive) self.table.setColumnWidth(0, 60) self.table.setColumnWidth(2, 80) self.table.setColumnWidth(7, 480) self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows) self.table.setAlternatingRowColors(True) main_layout.addWidget(self.table) self.setStatusBar(QStatusBar()) self.statusBar().setSizeGripEnabled(False) def open_config(self): dialog = ConfigDialog(self) if dialog.exec() == QDialog.Accepted: config = self.vm_manager.get_config() self.statusBar().showMessage("配置已更新") def connect_and_load(self): config = self.vm_manager.get_config() if not config.get('host') or not config.get('user') or not config.get('password'): QMessageBox.warning(self, "警告", "请先配置连接信息!") self.open_config() return self.connect_btn.setEnabled(False) self.statusBar().showMessage("正在连接...") if self.vm_manager.connect( config['host'], config['user'], config['password'], config.get('node', 'pve'), config.get('verify_ssl', False) ): self.load_vms() self.statusBar().showMessage("连接成功") else: QMessageBox.critical(self, "错误", "连接失败,请检查配置!") self.statusBar().showMessage("连接失败") self.connect_btn.setEnabled(True) def load_vms(self): self.vms = self.vm_manager.get_vms() stats = self.vm_manager.get_vm_stats(self.vms) self.total_card.set_value(str(stats['total'])) self.running_card.set_value(str(stats['running'])) self.stopped_card.set_value(str(stats['stopped'])) self.paused_card.set_value(str(stats['paused'])) self.update_table(self.vms) self.statusBar().showMessage(f"已加载 {len(self.vms)} 个虚拟机") def refresh_vms(self): if self.vm_manager.is_connected(): self.load_vms() self.statusBar().showMessage("已刷新") else: QMessageBox.warning(self, "警告", "请先连接服务器!") def filter_vms(self, text: str): if not text: self.update_table(self.vms) else: filtered = [vm for vm in self.vms if text.lower() in str(vm.get('vmid', '')) or text.lower() in vm.get('name', '').lower()] self.update_table(filtered) def update_table(self, vms: list): self.table.setRowCount(0) for vm in vms: row = self.table.rowCount() self.table.insertRow(row) info = self.vm_manager.get_vm_info(vm) status = info['status'] self.table.setItem(row, 0, QTableWidgetItem(str(info['vmid']))) self.table.setItem(row, 1, QTableWidgetItem(info['name'])) status_item = QTableWidgetItem(info['status_text']) status_item.setForeground(Qt.GlobalColor.green if status == 'running' else Qt.GlobalColor.red if status == 'stopped' else Qt.GlobalColor.yellow) self.table.setItem(row, 2, status_item) self.table.setItem(row, 3, QTableWidgetItem(info['cpu_percent'])) self.table.setItem(row, 4, QTableWidgetItem(info['memory'])) self.table.setItem(row, 5, QTableWidgetItem(info['disk'])) self.table.setItem(row, 6, QTableWidgetItem(info['uptime'])) action_widget = QWidget() action_layout = QHBoxLayout(action_widget) action_layout.setSpacing(5) action_layout.setContentsMargins(5, 2, 5, 2) node = self.vm_manager.get_config().get('node', 'pve') vmid = info['vmid'] start_btn = self.create_action_button("启动", "action_start", vmid, node, 'start', status == 'running') stop_btn = self.create_action_button("关机", "action_stop", vmid, node, 'shutdown', status != 'running') restart_btn = self.create_action_button("重启", "action_restart", vmid, node, 'reboot', status != 'running') force_stop_btn = self.create_action_button("强制停止", "action_stop", vmid, node, 'stop', status != 'running') suspend_btn = self.create_action_button("挂起", "action_button", vmid, node, 'suspend', status != 'running') action_layout.addWidget(start_btn) action_layout.addWidget(stop_btn) action_layout.addWidget(restart_btn) action_layout.addWidget(force_stop_btn) action_layout.addWidget(suspend_btn) self.table.setCellWidget(row, 7, action_widget) def create_action_button(self, text: str, obj_name: str, vmid: int, node: str, operation: str, disabled: bool): btn = QPushButton(text) btn.setObjectName(obj_name) btn.setEnabled(not disabled) btn.clicked.connect(lambda: self.execute_vm_operation(vmid, node, operation)) return btn def execute_vm_operation(self, vmid: int, node: str, operation: str): operation_names = { 'start': '启动', 'shutdown': '关机', 'reboot': '重启', 'stop': '强制停止', 'suspend': '挂起' } if self.vm_manager._vm_operation(vmid, node, operation): QMessageBox.information(self, "成功", f"虚拟机 {vmid} {operation_names[operation]}命令已发送!") self.load_vms() else: QMessageBox.critical(self, "错误", f"虚拟机 {vmid} {operation_names[operation]}失败!")