# mainwindow.py import sys import logging from PySide6.QtWidgets import (QApplication, QMainWindow, QTreeWidgetItem, QMessageBox, QHeaderView, QMenu, QInputDialog) from PySide6.QtCore import Qt, QPoint from ui_form import Ui_MainWindow from system_info import SystemInfoManager from logger_config import setup_logging, logger from disk_operations import DiskOperations class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) setup_logging(self.ui.logOutputTextEdit) logger.info("应用程序启动。") self.system_manager = SystemInfoManager() self.disk_ops = DiskOperations() if hasattr(self.ui, 'refreshButton'): self.ui.refreshButton.clicked.connect(self.refresh_all_info) else: logger.warning("Warning: refreshButton not found in UI. Please add it in form.ui and regenerate ui_form.py.") self.ui.treeWidget_block_devices.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeWidget_block_devices.customContextMenuRequested.connect(self.show_block_device_context_menu) self.refresh_all_info() logger.info("所有设备信息已初始化加载。") def refresh_all_info(self): """ 刷新所有设备信息:块设备、RAID和LVM。 """ logger.info("开始刷新所有设备信息...") self.refresh_block_devices_info() self.refresh_raid_info() self.refresh_lvm_info() logger.info("所有设备信息刷新完成。") def refresh_block_devices_info(self): """ 刷新块设备信息并显示在 QTreeWidget 中。 """ self.ui.treeWidget_block_devices.clear() columns = [ ("设备名", 'name'), ("类型", 'type'), ("大小", 'size'), ("挂载点", 'mountpoint'), ("文件系统", 'fstype'), ("只读", 'ro'), ("UUID", 'uuid'), ("PARTUUID", 'partuuid'), ("厂商", 'vendor'), ("型号", 'model'), ("序列号", 'serial'), ("主次号", 'maj:min'), ("父设备名", 'pkname'), ] headers = [col[0] for col in columns] self.field_keys = [col[1] for col in columns] self.ui.treeWidget_block_devices.setColumnCount(len(headers)) self.ui.treeWidget_block_devices.setHeaderLabels(headers) for i in range(len(headers)): self.ui.treeWidget_block_devices.header().setSectionResizeMode(i, QHeaderView.ResizeToContents) try: devices = self.system_manager.get_block_devices() for dev in devices: self._add_device_to_tree(self.ui.treeWidget_block_devices, dev) for i in range(len(headers)): self.ui.treeWidget_block_devices.resizeColumnToContents(i) logger.info("块设备信息刷新成功。") except Exception as e: QMessageBox.critical(self, "错误", f"刷新块设备信息失败: {e}") logger.error(f"刷新块设备信息失败: {e}") def _add_device_to_tree(self, parent_item, dev_data): """ 辅助函数,将单个设备及其子设备添加到 QTreeWidget。 parent_item 可以是 QTreeWidget 本身,也可以是另一个 QTreeWidgetItem。 """ item = QTreeWidgetItem(parent_item) for i, key in enumerate(self.field_keys): value = dev_data.get(key) if key == 'ro': item.setText(i, "是" if value else "否") elif value is None: item.setText(i, "") else: item.setText(i, str(value)) item.setData(0, Qt.UserRole, dev_data) if 'children' in dev_data: for child in dev_data['children']: self._add_device_to_tree(item, child) item.setExpanded(True) def refresh_raid_info(self): """ 刷新RAID阵列信息并显示在 QTreeWidget 中。 """ self.ui.treeWidget_raid.clear() # 定义RAID显示列头 raid_headers = [ "阵列设备", "级别", "状态", "大小", "活动设备", "失败设备", "备用设备", "总设备数", "UUID", "名称", "Chunk Size" ] self.ui.treeWidget_raid.setColumnCount(len(raid_headers)) self.ui.treeWidget_raid.setHeaderLabels(raid_headers) for i in range(len(raid_headers)): self.ui.treeWidget_raid.header().setSectionResizeMode(i, QHeaderView.ResizeToContents) try: raid_arrays = self.system_manager.get_mdadm_arrays() if not raid_arrays: item = QTreeWidgetItem(self.ui.treeWidget_raid) item.setText(0, "未找到RAID阵列。") logger.info("未找到RAID阵列。") return for array in raid_arrays: array_item = QTreeWidgetItem(self.ui.treeWidget_raid) array_item.setText(0, array.get('device', 'N/A')) array_item.setText(1, array.get('level', 'N/A')) array_item.setText(2, array.get('state', 'N/A')) array_item.setText(3, array.get('array_size', 'N/A')) array_item.setText(4, array.get('active_devices', 'N/A')) array_item.setText(5, array.get('failed_devices', 'N/A')) array_item.setText(6, array.get('spare_devices', 'N/A')) array_item.setText(7, array.get('total_devices', 'N/A')) array_item.setText(8, array.get('uuid', 'N/A')) array_item.setText(9, array.get('name', 'N/A')) array_item.setText(10, array.get('chunk_size', 'N/A')) array_item.setExpanded(True) # 添加成员设备作为子节点 for member in array.get('member_devices', []): member_item = QTreeWidgetItem(array_item) member_item.setText(0, f" {member.get('device_path', 'N/A')}") # 缩进显示 member_item.setText(1, f"成员") member_item.setText(2, member.get('state', 'N/A')) # 其他列留空或填充N/A,因为是成员设备的特有信息 member_item.setText(3, f"RaidDevice: {member.get('raid_device', 'N/A')}") for i in range(len(raid_headers)): self.ui.treeWidget_raid.resizeColumnToContents(i) logger.info("RAID阵列信息刷新成功。") except Exception as e: QMessageBox.critical(self, "错误", f"刷新RAID阵列信息失败: {e}") logger.error(f"刷新RAID阵列信息失败: {e}") def refresh_lvm_info(self): """ 刷新LVM信息(PVs, VGs, LVs)并显示在 QTreeWidget 中。 """ self.ui.treeWidget_lvm.clear() # 定义LVM显示列头 lvm_headers = ["名称", "大小", "属性", "UUID", "关联", "空闲/已用", "路径"] self.ui.treeWidget_lvm.setColumnCount(len(lvm_headers)) self.ui.treeWidget_lvm.setHeaderLabels(lvm_headers) for i in range(len(lvm_headers)): self.ui.treeWidget_lvm.header().setSectionResizeMode(i, QHeaderView.ResizeToContents) try: lvm_data = self.system_manager.get_lvm_info() if not lvm_data.get('pvs') and not lvm_data.get('vgs') and not lvm_data.get('lvs'): item = QTreeWidgetItem(self.ui.treeWidget_lvm) item.setText(0, "未找到LVM信息。") logger.info("未找到LVM信息。") return # 物理卷 (PVs) pv_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) pv_root_item.setText(0, "物理卷 (PVs)") pv_root_item.setExpanded(True) if lvm_data.get('pvs'): for pv in lvm_data['pvs']: pv_item = QTreeWidgetItem(pv_root_item) pv_item.setText(0, pv.get('pv_name', 'N/A')) pv_item.setText(1, pv.get('pv_size', 'N/A')) pv_item.setText(2, pv.get('pv_attr', 'N/A')) pv_item.setText(3, pv.get('pv_uuid', 'N/A')) pv_item.setText(4, f"VG: {pv.get('vg_name', 'N/A')}") pv_item.setText(5, f"空闲: {pv.get('pv_free', 'N/A')}") pv_item.setText(6, pv.get('pv_fmt', 'N/A')) # PV格式 else: item = QTreeWidgetItem(pv_root_item) item.setText(0, "未找到物理卷。") # 卷组 (VGs) vg_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) vg_root_item.setText(0, "卷组 (VGs)") vg_root_item.setExpanded(True) if lvm_data.get('vgs'): for vg in lvm_data['vgs']: vg_item = QTreeWidgetItem(vg_root_item) vg_item.setText(0, vg.get('vg_name', 'N/A')) vg_item.setText(1, vg.get('vg_size', 'N/A')) vg_item.setText(2, vg.get('vg_attr', 'N/A')) vg_item.setText(3, vg.get('vg_uuid', 'N/A')) vg_item.setText(4, f"PVs: {vg.get('pv_count', 'N/A')}, LVs: {vg.get('lv_count', 'N/A')}") vg_item.setText(5, f"空闲: {vg.get('vg_free', 'N/A')}, 已分配: {vg.get('vg_alloc_percent', 'N/A')}%") vg_item.setText(6, vg.get('vg_fmt', 'N/A')) # VG格式 else: item = QTreeWidgetItem(vg_root_item) item.setText(0, "未找到卷组。") # 逻辑卷 (LVs) lv_root_item = QTreeWidgetItem(self.ui.treeWidget_lvm) lv_root_item.setText(0, "逻辑卷 (LVs)") lv_root_item.setExpanded(True) if lvm_data.get('lvs'): for lv in lvm_data['lvs']: lv_item = QTreeWidgetItem(lv_root_item) lv_item.setText(0, lv.get('lv_name', 'N/A')) lv_item.setText(1, lv.get('lv_size', 'N/A')) lv_item.setText(2, lv.get('lv_attr', 'N/A')) lv_item.setText(3, lv.get('lv_uuid', 'N/A')) lv_item.setText(4, f"VG: {lv.get('vg_name', 'N/A')}, Origin: {lv.get('origin', 'N/A')}") lv_item.setText(5, f"快照: {lv.get('snap_percent', 'N/A')}%") lv_item.setText(6, lv.get('lv_path', 'N/A')) else: item = QTreeWidgetItem(lv_root_item) item.setText(0, "未找到逻辑卷。") for i in range(len(lvm_headers)): self.ui.treeWidget_lvm.resizeColumnToContents(i) logger.info("LVM信息刷新成功。") except Exception as e: QMessageBox.critical(self, "错误", f"刷新LVM信息失败: {e}") logger.error(f"刷新LVM信息失败: {e}") def show_block_device_context_menu(self, pos: QPoint): """ 显示块设备列表的右键上下文菜单。 """ item = self.ui.treeWidget_block_devices.itemAt(pos) if item: dev_data = item.data(0, Qt.UserRole) if not dev_data: logger.warning(f"无法获取设备 {item.text(0)} 的详细数据。") return device_name = dev_data.get('name') device_type = dev_data.get('type') mount_point = dev_data.get('mountpoint') menu = QMenu(self) if device_type in ['part', 'disk']: if not mount_point or mount_point == '' or mount_point == 'N/A': mount_action = menu.addAction(f"挂载 {device_name}...") mount_action.triggered.connect(lambda: self._handle_mount(device_name)) elif mount_point != '[SWAP]': unmount_action = menu.addAction(f"卸载 {device_name}") unmount_action.triggered.connect(lambda: self._handle_unmount(device_name)) if menu.actions(): menu.addSeparator() if device_type == 'part': delete_action = menu.addAction(f"删除分区 {device_name}") delete_action.triggered.connect(lambda: self._handle_delete_partition(device_name)) format_action = menu.addAction(f"格式化分区 {device_name}...") format_action.triggered.connect(lambda: self._handle_format_partition(device_name)) if menu.actions(): menu.exec(self.ui.treeWidget_block_devices.mapToGlobal(pos)) else: logger.info(f"设备 {device_name} 没有可用的操作。") else: logger.info("右键点击了空白区域。") def _handle_mount(self, device_name): """处理挂载操作,并刷新UI。""" if self.disk_ops.mount_partition(device_name): self.refresh_all_info() def _handle_unmount(self, device_name): """处理卸载操作,并刷新UI。""" if self.disk_ops.unmount_partition(device_name): self.refresh_all_info() def _handle_delete_partition(self, device_name): """处理删除分区操作,并刷新UI。""" if self.disk_ops.delete_partition(device_name): self.refresh_all_info() def _handle_format_partition(self, device_name): """处理格式化分区操作,并刷新UI。""" if self.disk_ops.format_partition(device_name): self.refresh_all_info() if __name__ == "__main__": app = QApplication(sys.argv) widget = MainWindow() widget.show() sys.exit(app.exec())