from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFrame, QMessageBox, QScrollArea, QTableWidget, QTableWidgetItem, QHeaderView, QProgressBar, QLineEdit, QAbstractItemView) from PyQt5.QtCore import Qt, pyqtSignal, QTimer from PyQt5.QtGui import QPixmap, QFont, QIcon, QColor import os import kp from src.config import UXUI_ASSETS, WINDOW_SIZE, BACKGROUND_COLOR, DongleModelMap from src.controllers.device_controller import DeviceController from src.services.device_service import check_available_device class UtilitiesScreen(QWidget): # Signals for navigation back_to_selection = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.device_controller = DeviceController(self) self.current_page = "utilities" # 追蹤當前頁面: "utilities" 或 "purchased_items" self.init_ui() def init_ui(self): # Basic window setup self.setGeometry(100, 100, *WINDOW_SIZE) self.setStyleSheet(f"background-color: #F5F7FA;") # Light gray background # Main layout self.main_layout = QVBoxLayout(self) self.main_layout.setContentsMargins(20, 20, 20, 20) self.main_layout.setSpacing(20) # Header with back button and logo header_frame = self.create_header() self.main_layout.addWidget(header_frame) # 創建主要內容容器 self.content_container = QFrame(self) self.content_container.setStyleSheet(""" QFrame { background-color: white; border-radius: 10px; border: 1px solid #E0E0E0; } """) content_layout = QVBoxLayout(self.content_container) content_layout.setContentsMargins(20, 20, 20, 20) content_layout.setSpacing(20) # 創建兩個頁面的容器 self.utilities_page = QWidget() self.purchased_items_page = QWidget() # 設置 utilities 頁面 self.setup_utilities_page() # 設置 purchased items 頁面 self.setup_purchased_items_page() # 添加頁面到內容容器 content_layout.addWidget(self.utilities_page) content_layout.addWidget(self.purchased_items_page) # 初始顯示 utilities 頁面 self.utilities_page.show() self.purchased_items_page.hide() # 添加內容容器到主佈局 self.main_layout.addWidget(self.content_container, 1) # Initialize with device refresh QTimer.singleShot(500, self.refresh_devices) def create_header(self): """Create the header with back button and logo""" header_frame = QFrame(self) header_frame.setStyleSheet("background-color: #2C3E50; border-radius: 0px;") header_frame.setFixedHeight(60) header_layout = QHBoxLayout(header_frame) header_layout.setContentsMargins(20, 0, 20, 0) # Back button back_button = QPushButton("", self) back_button.setIcon(QIcon(os.path.join(UXUI_ASSETS, "Assets_png/back_arrow.png"))) back_button.setIconSize(QPixmap(os.path.join(UXUI_ASSETS, "Assets_png/back_arrow.png")).size()) back_button.setFixedSize(40, 40) back_button.setStyleSheet(""" QPushButton { background-color: transparent; border: none; } QPushButton:hover { background-color: rgba(255, 255, 255, 0.1); border-radius: 20px; } """) back_button.clicked.connect(self.back_to_selection.emit) header_layout.addWidget(back_button, alignment=Qt.AlignLeft) # Title self.title_label = QLabel("Utilities", self) self.title_label.setStyleSheet("color: white; font-size: 24px; font-weight: bold;") header_layout.addWidget(self.title_label, alignment=Qt.AlignCenter) # Navigation buttons nav_container = QFrame() nav_layout = QHBoxLayout(nav_container) nav_layout.setContentsMargins(0, 0, 0, 0) nav_layout.setSpacing(10) # Utilities button self.utilities_button = QPushButton("Utilities", self) self.utilities_button.setStyleSheet(""" QPushButton { background-color: #3498DB; color: white; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { background-color: #2980B9; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.utilities_button.clicked.connect(self.show_utilities_page) nav_layout.addWidget(self.utilities_button) # Purchased Items button self.purchased_items_button = QPushButton("Purchased Items", self) self.purchased_items_button.setStyleSheet(""" QPushButton { background-color: transparent; color: #BDC3C7; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { color: white; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.purchased_items_button.clicked.connect(self.show_purchased_items_page) nav_layout.addWidget(self.purchased_items_button) header_layout.addWidget(nav_container) # Logo logo_label = QLabel(self) logo_path = os.path.join(UXUI_ASSETS, "Assets_png/kneron_logo.png") if os.path.exists(logo_path): logo_pixmap = QPixmap(logo_path) scaled_logo = logo_pixmap.scaled(104, 40, Qt.KeepAspectRatio, Qt.SmoothTransformation) logo_label.setPixmap(scaled_logo) header_layout.addWidget(logo_label, alignment=Qt.AlignRight) return header_frame def setup_utilities_page(self): """Create the utilities page""" utilities_layout = QVBoxLayout(self.utilities_page) utilities_layout.setContentsMargins(0, 0, 0, 0) utilities_layout.setSpacing(20) # Device connection section device_section = self.create_device_section() utilities_layout.addWidget(device_section) # Status section status_section = self.create_status_section() utilities_layout.addWidget(status_section) def setup_purchased_items_page(self): """Create the purchased items page""" purchased_items_layout = QVBoxLayout(self.purchased_items_page) purchased_items_layout.setContentsMargins(0, 0, 0, 0) purchased_items_layout.setSpacing(20) # 已購買項目區域 purchased_items_section = self.create_purchased_items_section() purchased_items_layout.addWidget(purchased_items_section) def create_purchased_items_section(self): """創建已購買項目區域""" purchased_section = QFrame() purchased_section.setStyleSheet(""" QFrame { background-color: white; border-radius: 8px; border: 1px solid #E0E0E0; } """) purchased_layout = QVBoxLayout(purchased_section) purchased_layout.setContentsMargins(15, 15, 15, 15) purchased_layout.setSpacing(15) # 標題 title_label = QLabel("Your Purchased Items", purchased_section) title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #2C3E50;") purchased_layout.addWidget(title_label) # 描述 desc_label = QLabel("Select items to download to your device", purchased_section) desc_label.setStyleSheet("font-size: 14px; color: #7F8C8D;") purchased_layout.addWidget(desc_label) # 項目表格 self.purchased_table = QTableWidget() # 修改為只有5列,移除 "Action" 列 self.purchased_table.setColumnCount(5) self.purchased_table.setHorizontalHeaderLabels([ "Select", "Product", "Model", "Current Version", "Compatible Dongles" ]) # 設置行寬 header = self.purchased_table.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 勾選框列 header.setSectionResizeMode(1, QHeaderView.Stretch) header.setSectionResizeMode(2, QHeaderView.Stretch) header.setSectionResizeMode(3, QHeaderView.ResizeToContents) header.setSectionResizeMode(4, QHeaderView.Stretch) # 設置表格高度 self.purchased_table.setMinimumHeight(300) # 啟用整行選擇 self.purchased_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.purchased_table.setStyleSheet(""" QTableWidget { background-color: white; color: #2C3E50; border: 1px solid #E0E0E0; border-radius: 8px; gridline-color: #E0E0E0; } QTableWidget::item { padding: 8px; border-bottom: 1px solid #E0E0E0; } QTableWidget::item:selected { background-color: #3498DB; color: white; } QHeaderView::section { background-color: #3498DB; color: white; padding: 8px; border: none; font-weight: bold; } """) purchased_layout.addWidget(self.purchased_table) # 添加一些模擬數據 self.populate_mock_purchased_items() # 下載按鈕 button_layout = QHBoxLayout() button_layout.setSpacing(10) refresh_button = QPushButton("Refresh Items", purchased_section) refresh_button.setMinimumHeight(40) refresh_button.setStyleSheet(""" QPushButton { background-color: #3498DB; color: white; border: none; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #2980B9; } QPushButton:pressed { background-color: #1F618D; } """) refresh_button.clicked.connect(self.populate_mock_purchased_items) button_layout.addWidget(refresh_button) download_button = QPushButton("Download Selected Items", purchased_section) download_button.setMinimumHeight(40) download_button.setStyleSheet(""" QPushButton { background-color: #2ECC71; color: white; border: none; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #27AE60; } QPushButton:pressed { background-color: #1E8449; } """) download_button.clicked.connect(self.download_selected_items) button_layout.addWidget(download_button) purchased_layout.addLayout(button_layout) return purchased_section def populate_mock_purchased_items(self): """填充模擬的已購買項目數據""" # 清空表格 self.purchased_table.setRowCount(0) # 模擬數據 mock_items = [ { "product": "KL720 AI Package", "model": "Face Detection", "version": "1.2.3", "dongles": "KL720, KL730" }, { "product": "KL520 AI Package", "model": "Object Detection", "version": "2.0.1", "dongles": "KL520" }, { "product": "KL720 AI Package", "model": "Pose Estimation", "version": "1.5.0", "dongles": "KL720, KL730, KL830" }, { "product": "KL630 AI Package", "model": "Image Classification", "version": "3.1.2", "dongles": "KL630, KL720" }, { "product": "KL830 AI Package", "model": "Semantic Segmentation", "version": "1.0.0", "dongles": "KL830" } ] # 添加數據到表格 for i, item in enumerate(mock_items): self.purchased_table.insertRow(i) # 創建勾選框 checkbox_item = QTableWidgetItem() checkbox_item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable) checkbox_item.setCheckState(Qt.Unchecked) self.purchased_table.setItem(i, 0, checkbox_item) self.purchased_table.setItem(i, 1, QTableWidgetItem(item["product"])) self.purchased_table.setItem(i, 2, QTableWidgetItem(item["model"])) self.purchased_table.setItem(i, 3, QTableWidgetItem(item["version"])) self.purchased_table.setItem(i, 4, QTableWidgetItem(item["dongles"])) def download_item(self, row): """下載特定項目""" product = self.purchased_table.item(row, 1).text() model = self.purchased_table.item(row, 2).text() # 顯示進度條 self.show_progress(f"Downloading {product} - {model}...", 0) # 模擬下載過程 for i in range(1, 11): progress = i * 10 QTimer.singleShot(i * 300, lambda p=progress: self.update_progress(p)) # 完成下載 QTimer.singleShot(3000, lambda: self.handle_download_complete(product, model)) def handle_download_complete(self, product, model): """處理下載完成""" self.hide_progress() QMessageBox.information(self, "Download Complete", f"{product} - {model} has been downloaded successfully!") def download_selected_items(self): """下載所有勾選的項目""" selected_rows = set() # 檢查勾選的項目 for row in range(self.purchased_table.rowCount()): if self.purchased_table.item(row, 0).checkState() == Qt.Checked: selected_rows.add(row) if not selected_rows: QMessageBox.warning(self, "No Selection", "Please select at least one item to download") return # 顯示進度條 self.show_progress(f"Downloading {len(selected_rows)} items...", 0) # 模擬下載過程 total_items = len(selected_rows) for i, row in enumerate(selected_rows): product = self.purchased_table.item(row, 1).text() model = self.purchased_table.item(row, 2).text() progress = int((i / total_items) * 100) # 更新進度條 self.update_progress(progress) self.progress_title.setText(f"Downloading {product} - {model}... ({i+1}/{total_items})") # 模擬下載延遲 QTimer.singleShot((i+1) * 1000, lambda p=product, m=model: self.status_label.setText(f"Downloaded {p} - {m}")) # 完成所有下載 QTimer.singleShot((total_items+1) * 1000, self.handle_all_downloads_complete) def update_download_progress(self, progress, message): """更新下載進度""" self.update_progress(progress) self.progress_title.setText(message) def handle_all_downloads_complete(self): """處理所有下載完成""" self.hide_progress() QMessageBox.information(self, "Downloads Complete", "All selected items have been downloaded successfully!") def create_device_section(self): """Create the device connection section""" device_section = QFrame(self) device_section.setStyleSheet(""" QFrame { background-color: white; border-radius: 8px; border: 1px solid #E0E0E0; } """) device_layout = QVBoxLayout(device_section) device_layout.setContentsMargins(15, 15, 15, 15) device_layout.setSpacing(15) # Title title_label = QLabel("Device Connection", self) title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #2C3E50;") device_layout.addWidget(title_label) # Description desc_label = QLabel("Connect and manage your Kneron devices", self) desc_label.setStyleSheet("font-size: 14px; color: #7F8C8D;") device_layout.addWidget(desc_label) # Create device table self.device_table = QTableWidget() self.device_table.setColumnCount(6) self.device_table.setHorizontalHeaderLabels([ "Device Type", "Port ID", "Firmware Version", "KN Number", "Link Speed", "Status" ]) # Enable row selection mode self.device_table.setSelectionBehavior(QTableWidget.SelectRows) self.device_table.setSelectionMode(QTableWidget.SingleSelection) self.device_table.setStyleSheet(""" QTableWidget { background-color: white; color: #2C3E50; border: 1px solid #E0E0E0; border-radius: 8px; gridline-color: #E0E0E0; } QTableWidget::item { padding: 8px; border-bottom: 1px solid #E0E0E0; } QTableWidget::item:selected { background-color: #3498DB; color: white; } QHeaderView::section { background-color: #3498DB; color: white; padding: 8px; border: none; font-weight: bold; } """) # Set header properties header = self.device_table.horizontalHeader() for i in range(6): header.setSectionResizeMode(i, QHeaderView.Stretch) # Add the table to a scroll area table_scroll = QScrollArea() table_scroll.setWidgetResizable(True) table_scroll.setStyleSheet(""" QScrollArea { border: none; background-color: transparent; } """) table_scroll.setWidget(self.device_table) device_layout.addWidget(table_scroll) # Connect selection changed signal self.device_table.itemSelectionChanged.connect(self.on_device_selection_changed) # Button layout button_layout = QHBoxLayout() button_layout.setSpacing(10) # Refresh button refresh_button = QPushButton("Refresh Devices", self) refresh_button.setMinimumHeight(40) refresh_button.setStyleSheet(""" QPushButton { background-color: #3498DB; color: white; border: 2px solid #2980B9; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #2980B9; } QPushButton:pressed { background-color: #1F618D; } """) refresh_button.clicked.connect(self.refresh_devices) button_layout.addWidget(refresh_button) # Register button register_button = QPushButton("Register Device", self) register_button.setMinimumHeight(40) register_button.setStyleSheet(""" QPushButton { background-color: #2ECC71; color: white; border: 2px solid #27AE60; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #27AE60; } QPushButton:pressed { background-color: #1E8449; } """) register_button.clicked.connect(self.register_device) button_layout.addWidget(register_button) # Update firmware button update_button = QPushButton("Update Firmware", self) update_button.setMinimumHeight(40) update_button.setStyleSheet(""" QPushButton { background-color: #F39C12; color: white; border: 2px solid #D35400; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #D35400; } QPushButton:pressed { background-color: #A04000; } """) update_button.clicked.connect(self.update_firmware) button_layout.addWidget(update_button) # Install Driver button install_driver_button = QPushButton("Install Driver", self) install_driver_button.setMinimumHeight(40) install_driver_button.setStyleSheet(""" QPushButton { background-color: #9B59B6; color: white; border: 2px solid #8E44AD; border-radius: 5px; padding: 10px 15px; font-weight: bold; font-size: 14px; } QPushButton:hover { background-color: #8E44AD; } QPushButton:pressed { background-color: #7D3C98; } """) install_driver_button.clicked.connect(self.install_drivers) button_layout.addWidget(install_driver_button) device_layout.addLayout(button_layout) return device_section def create_status_section(self): """Create the status section""" status_section = QFrame(self) status_section.setStyleSheet(""" QFrame { background-color: white; border-radius: 8px; border: 1px solid #E0E0E0; } """) status_layout = QVBoxLayout(status_section) status_layout.setContentsMargins(15, 15, 15, 15) status_layout.setSpacing(15) # Title title_label = QLabel("Device Status", self) title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #2C3E50;") status_layout.addWidget(title_label) # Status message self.status_label = QLabel("No devices found", self) self.status_label.setStyleSheet("font-size: 14px; color: #7F8C8D;") status_layout.addWidget(self.status_label) # Progress section self.progress_section = QFrame(self) self.progress_section.setVisible(False) self.progress_section.setStyleSheet(""" QFrame { background-color: #F8F9FA; border-radius: 5px; border: 1px solid #E0E0E0; padding: 10px; } """) progress_layout = QVBoxLayout(self.progress_section) progress_layout.setContentsMargins(10, 10, 10, 10) progress_layout.setSpacing(10) self.progress_title = QLabel("Operation in progress...", self) self.progress_title.setStyleSheet("font-size: 14px; font-weight: bold; color: #2C3E50;") progress_layout.addWidget(self.progress_title) self.progress_bar = QProgressBar(self) self.progress_bar.setRange(0, 100) self.progress_bar.setValue(0) self.progress_bar.setTextVisible(True) self.progress_bar.setStyleSheet(""" QProgressBar { border: 1px solid #E0E0E0; border-radius: 5px; background-color: white; text-align: center; height: 20px; } QProgressBar::chunk { background-color: #3498DB; border-radius: 5px; } """) progress_layout.addWidget(self.progress_bar) status_layout.addWidget(self.progress_section) return status_section def refresh_devices(self): """Refresh the list of devices""" try: # Clear the table self.device_table.setRowCount(0) # Show progress self.show_progress("Scanning for devices...", 0) # Get the devices device_descriptors = check_available_device() # Update progress self.update_progress(50) # Display the devices in the table if device_descriptors.device_descriptor_number > 0: devices = device_descriptors.device_descriptor_list for i, device in enumerate(devices): self.device_table.insertRow(i) # Product ID to Device Model mapping product_id_hex = hex(device.product_id) # Map the product_id to device model name using DongleModelMap device_model = DongleModelMap.get(product_id_hex, product_id_hex) model_item = QTableWidgetItem(device_model) self.device_table.setItem(i, 0, model_item) # Device ID (Port ID) port_id = str(device.usb_port_id) usb_id = QTableWidgetItem(port_id) self.device_table.setItem(i, 1, usb_id) # 嘗試獲取 system_info 中的 firmware_version firmware_version = "-" try: if device.is_connectable: # 連接設備並獲取系統信息 device_group = kp.core.connect_devices(usb_port_ids=[port_id]) system_info = kp.core.get_system_info( device_group=device_group, usb_port_id=device.usb_port_id ) # 從 system_info 對象獲取固件版本 if system_info and hasattr(system_info, 'firmware_version'): # firmware_version 是一個對象,獲取其字符串表示 fw_version = system_info.firmware_version if hasattr(fw_version, 'firmware_version'): # 提取版本號,移除字典格式 version_str = fw_version.firmware_version # 如果版本是字典格式,提取其中的值 if isinstance(version_str, dict) and 'firmware_version' in version_str: firmware_version = version_str['firmware_version'] else: firmware_version = version_str else: # 將對象轉換為字符串並清理格式 version_str = str(fw_version) # 嘗試從字符串中提取版本號 import re match = re.search(r'"firmware_version":\s*"([^"]+)"', version_str) if match: firmware_version = match.group(1) else: firmware_version = version_str except Exception as e: print(f"Error getting firmware version: {e}") # Firmware firmware = QTableWidgetItem(firmware_version) self.device_table.setItem(i, 2, firmware) # KN Number kn_number = QTableWidgetItem(str(device.kn_number)) self.device_table.setItem(i, 3, kn_number) # Link Speed link_speed_str = "Unknown" if hasattr(device, 'link_speed'): # 從完整的 link_speed 字符串中提取 SPEED_XXX 部分 full_speed = str(device.link_speed) if "SUPER" in full_speed: link_speed_str = "SUPER" elif "HIGH" in full_speed: link_speed_str = "HIGH" elif "FULL" in full_speed: link_speed_str = "FULL" else: # 嘗試提取 KP_USB_SPEED_XXX 部分 import re match = re.search(r'KP_USB_SPEED_(\w+)', full_speed) if match: link_speed_str = match.group(1) link_speed = QTableWidgetItem(link_speed_str) self.device_table.setItem(i, 4, link_speed) # Status status = QTableWidgetItem("Connected" if device.is_connectable else "Not Available") status.setForeground(Qt.green if device.is_connectable else Qt.red) self.device_table.setItem(i, 5, status) # Hide progress self.hide_progress() # Update status device_count = self.device_table.rowCount() if device_count > 0: self.status_label.setText(f"Found {device_count} device(s)") self.status_label.setStyleSheet("font-size: 14px; color: #27AE60; font-weight: bold;") else: self.status_label.setText("No devices found") self.status_label.setStyleSheet("font-size: 14px; color: #E74C3C;") return device_count > 0 except Exception as e: print(f"Error refreshing devices: {e}") self.hide_progress() self.status_label.setText(f"Error: {str(e)}") self.status_label.setStyleSheet("font-size: 14px; color: #E74C3C;") return False def register_device(self): """Register the selected device""" selected_rows = self.device_table.selectedItems() if not selected_rows: QMessageBox.warning(self, "Warning", "Please select a device to register") return # In a real application, you would implement the device registration logic here QMessageBox.information(self, "Info", "Device registration functionality will be implemented in a future update") def update_firmware(self): """Update firmware for the selected device""" selected_rows = self.device_table.selectedItems() if not selected_rows: QMessageBox.warning(self, "Warning", "Please select a device to update firmware") return # In a real application, you would implement the firmware update logic here QMessageBox.information(self, "Info", "Firmware update functionality will be implemented in a future update") def install_drivers(self): """Install drivers for all supported Kneron devices""" try: # 顯示進度條 self.show_progress("Installing Kneron Device Drivers...", 0) # 列出所有產品 ID product_ids = [ kp.ProductId.KP_DEVICE_KL520, kp.ProductId.KP_DEVICE_KL720_LEGACY, kp.ProductId.KP_DEVICE_KL720, kp.ProductId.KP_DEVICE_KL630, kp.ProductId.KP_DEVICE_KL730, kp.ProductId.KP_DEVICE_KL830 ] success_count = 0 total_count = len(product_ids) # 安裝每個驅動程式 for i, product_id in enumerate(product_ids): try: # 更新進度條 progress = int((i / total_count) * 100) self.update_progress(progress) self.progress_title.setText(f"Installing [{product_id.name}] driver...") # 安裝驅動程式 kp.core.install_driver_for_windows(product_id=product_id) success_count += 1 # 更新狀態訊息 self.status_label.setText(f"Successfully installed {product_id.name} driver") except kp.ApiKPException as exception: error_msg = f"Error: install {product_id.name} driver failed, error msg: [{str(exception)}]" self.status_label.setText(error_msg) QMessageBox.warning(self, "Driver Installation Error", error_msg) # 完成安裝 self.update_progress(100) self.hide_progress() # 顯示結果訊息 if success_count == total_count: QMessageBox.information(self, "Success", "All Kneron device drivers installed successfully!") else: QMessageBox.information(self, "Partial Success", f"Installed {success_count} out of {total_count} drivers. Check the status for details.") except Exception as e: self.hide_progress() error_msg = f"Error during driver installation: {str(e)}" self.status_label.setText(error_msg) QMessageBox.critical(self, "Error", error_msg) def show_progress(self, title, value): """Show the progress bar with the given title and value""" self.progress_title.setText(title) self.progress_bar.setValue(value) self.progress_section.setVisible(True) def update_progress(self, value): """Update the progress bar value""" self.progress_bar.setValue(value) def hide_progress(self): """Hide the progress section""" self.progress_section.setVisible(False) def on_device_selection_changed(self): """Handle device selection changes""" selected_rows = self.device_table.selectionModel().selectedRows() if selected_rows: # Get the selected row index row_index = selected_rows[0].row() # Update the status label to show which device is selected device_model = self.device_table.item(row_index, 0).text() device_id = self.device_table.item(row_index, 1).text() self.status_label.setText(f"Selected device: {device_model} (ID: {device_id})") # Ensure the entire row is highlighted self.device_table.selectRow(row_index) def show_utilities_page(self): self.utilities_button.setStyleSheet(""" QPushButton { background-color: #3498DB; color: white; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { background-color: #2980B9; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.purchased_items_button.setStyleSheet(""" QPushButton { background-color: transparent; color: #BDC3C7; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { color: white; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.utilities_page.show() self.purchased_items_page.hide() self.title_label.setText("Utilities") self.current_page = "utilities" def show_purchased_items_page(self): self.purchased_items_button.setStyleSheet(""" QPushButton { background-color: #3498DB; color: white; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { background-color: #2980B9; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.utilities_button.setStyleSheet(""" QPushButton { background-color: transparent; color: #BDC3C7; border: none; border-radius: 5px; padding: 5px 10px; font-weight: bold; } QPushButton:hover { color: white; } QPushButton:disabled { background-color: #3498DB; color: white; } """) self.utilities_page.hide() self.purchased_items_page.show() self.title_label.setText("Purchased Items") self.current_page = "purchased_items"