KNEO-Academy/src/views/utilities_screen.py
HuangMason320 c8be1db25e Fix: Add timeout mechanisms to prevent blocking on device scan and camera open
- device_service: Add timeout mechanism for device scanning
- video_thread: Add timeout mechanism for camera opening
- device_list: Fix height and hide scrollbars to prevent scroll issues
- mainWindows: Adjust UI startup order, delay device refresh and camera start
- utilities_screen: Temporarily disable auto refresh to prevent blocking
- .gitignore: Add new ignore entries

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 02:37:46 +08:00

1037 lines
40 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.

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
from ..config import FW_DIR
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"""
try:
# 檢查是否有選擇設備
selected_rows = self.device_table.selectionModel().selectedRows()
if not selected_rows:
QMessageBox.warning(self, "警告", "請選擇要更新韌體的設備")
return
# 獲取選擇的設備資訊
row_index = selected_rows[0].row()
device_model = self.device_table.item(row_index, 0).text() # 設備型號
port_id = self.device_table.item(row_index, 1).text() # Port ID
# 顯示進度條
self.show_progress(f"正在更新 {device_model} 的韌體...", 0)
# 連接設備
device_group = kp.core.connect_devices(usb_port_ids=[int(port_id)])
# 構建韌體檔案路徑
scpu_fw_path = os.path.join(FW_DIR, device_model, "fw_scpu.bin")
ncpu_fw_path = os.path.join(FW_DIR, device_model, "fw_ncpu.bin")
# 檢查韌體檔案是否存在
if not os.path.exists(scpu_fw_path) or not os.path.exists(ncpu_fw_path):
self.hide_progress()
QMessageBox.critical(self, "錯誤", f"找不到 {device_model} 的韌體檔案")
return
# 更新進度
self.update_progress(30)
# 載入韌體
kp.core.load_firmware_from_file(
device_group=device_group,
scpu_fw_path=scpu_fw_path,
ncpu_fw_path=ncpu_fw_path
)
# 更新進度
self.update_progress(100)
# 顯示成功訊息
QMessageBox.information(self, "成功", f"{device_model} 的韌體已成功更新")
except Exception as e:
self.hide_progress()
QMessageBox.critical(self, "錯誤", f"更新韌體時發生錯誤: {str(e)}")
finally:
self.hide_progress()
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"