diff --git a/mucapy/main.py b/mucapy/main.py index eafe5e3..80a5961 100644 --- a/mucapy/main.py +++ b/mucapy/main.py @@ -16,6 +16,7 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout from PyQt5.QtCore import Qt, QTimer, QDir, QSize, QSettings, QDateTime, QRect, QThread, pyqtSignal, QMutex, QObject from PyQt5.QtGui import (QImage, QPixmap, QIcon, QColor, QKeySequence, QPainter, QPen, QBrush) +import platform import time import requests import subprocess @@ -759,53 +760,97 @@ class CollapsibleDock(QDockWidget): self.toggle_button.setIcon(QIcon.fromTheme("arrow-left")) self.collapsed = False class AboutWindow(QDialog): - def __init__(self, parent=None): # Add parent parameter with default None - super().__init__(parent) # Pass parent to QDialog + def __init__(self, parent=None): + super().__init__(parent) self.setWindowTitle("About Multi-Camera YOLO Detection") self.setWindowIcon(QIcon.fromTheme("help-about")) - self.resize(400, 300) - - # Make it modal and stay on top + self.resize(450, 420) + self.setWindowModality(Qt.ApplicationModal) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) - + layout = QVBoxLayout() - layout.setAlignment(Qt.AlignCenter) + layout.setAlignment(Qt.AlignTop) layout.setSpacing(20) - - # Application icon/logo (placeholder) + + # App icon icon_label = QLabel() icon_label.setPixmap(QIcon.fromTheme("camera-web").pixmap(64, 64)) icon_label.setAlignment(Qt.AlignCenter) layout.addWidget(icon_label) - - # Application title + + # Title title_label = QLabel("MuCaPy - 1") title_label.setStyleSheet("font-size: 18px; font-weight: bold;") title_label.setAlignment(Qt.AlignCenter) layout.addWidget(title_label) - - # Version information + + # Version label version_label = QLabel("Version 1.0") version_label.setAlignment(Qt.AlignCenter) layout.addWidget(version_label) - - # Description - desc_label = QLabel( - "MuCaPy\n" - "Multiple Camera Python\n" - "Using CV2" - ) - desc_label.setAlignment(Qt.AlignCenter) - desc_label.setWordWrap(True) - layout.addWidget(desc_label) - # Close Button + # Get system info + info = self.get_system_info() + self.important_keys = ["Python", "OpenCV", "Memory", "CUDA"] + self.full_labels = {} + + # === System Info Group === + self.sysinfo_box = QGroupBox() + sysinfo_main_layout = QVBoxLayout() + sysinfo_main_layout.setContentsMargins(8, 8, 8, 8) + + # Header layout: title + triangle button + header_layout = QHBoxLayout() + header_label = QLabel("System Information") + header_label.setStyleSheet("font-weight: bold;") + header_layout.addWidget(header_label) + + header_layout.addStretch() + + self.toggle_btn = QToolButton() + self.toggle_btn.setText("▶") + self.toggle_btn.setCheckable(True) + self.toggle_btn.setChecked(False) + self.toggle_btn.setStyleSheet(""" + QToolButton { + border: none; + background: transparent; + font-size: 14px; + color: #DDD; + } + """) + self.toggle_btn.toggled.connect(self.toggle_expand) + header_layout.addWidget(self.toggle_btn) + + sysinfo_main_layout.addLayout(header_layout) + + # Details layout + self.sysinfo_layout = QVBoxLayout() + self.sysinfo_layout.setSpacing(5) + + for key, value in info.items(): + if key == "MemoryGB": + continue + + label = QLabel(f"{key}: {value}") + self.style_label(label, key, value) + self.sysinfo_layout.addWidget(label) + self.full_labels[key] = label + + if key not in self.important_keys: + label.setVisible(False) + + sysinfo_main_layout.addLayout(self.sysinfo_layout) + self.sysinfo_box.setLayout(sysinfo_main_layout) + layout.addWidget(self.sysinfo_box) + + # Close button close_btn = QPushButton("Close") close_btn.clicked.connect(self.accept) close_btn.setFixedWidth(100) layout.addWidget(close_btn, alignment=Qt.AlignCenter) - + self.setStyleSheet(""" QDialog { background-color: #2D2D2D; @@ -816,9 +861,9 @@ class AboutWindow(QDialog): } QGroupBox { border: 1px solid #555; - border-radius: 4px; + border-radius: 6px; margin-top: 10px; - padding-top: 15px; + padding: 4px; background-color: #252525; } QGroupBox::title { @@ -839,8 +884,90 @@ class AboutWindow(QDialog): background-color: #4A4A4A; } """) - + self.setLayout(layout) + + def toggle_expand(self, checked): + for key, label in self.full_labels.items(): + if key not in self.important_keys: + label.setVisible(checked) + self.toggle_btn.setText("▼" if checked else "▶") + + def style_label(self, label, key, value): + if key == "Python": + label.setStyleSheet("color: #7FDBFF;") + elif key == "OpenCV": + label.setStyleSheet("color: #FF851B;") + elif key == "CUDA": + label.setStyleSheet("color: green;" if value == "Yes" else "color: red;") + elif key == "NumPy": + label.setStyleSheet("color: #B10DC9;") + elif key == "Requests": + label.setStyleSheet("color: #0074D9;") + elif key == "Memory": + try: + ram = int(value.split()[0]) + if ram < 8: + label.setStyleSheet("color: red;") + elif ram < 16: + label.setStyleSheet("color: yellow;") + elif ram < 32: + label.setStyleSheet("color: lightgreen;") + else: + label.setStyleSheet("color: #90EE90;") + except: + label.setStyleSheet("color: gray;") + elif key == "CPU Usage": + try: + usage = float(value.strip('%')) + if usage > 80: + label.setStyleSheet("color: red;") + elif usage > 50: + label.setStyleSheet("color: yellow;") + else: + label.setStyleSheet("color: lightgreen;") + except: + label.setStyleSheet("color: gray;") + elif key in ("CPU Cores", "Logical CPUs"): + label.setStyleSheet("color: lightgreen;") + elif key in ("CPU", "Architecture", "OS"): + label.setStyleSheet("color: lightgray;") + else: + label.setStyleSheet("color: #DDD;") + + def get_system_info(self): + import platform + + info = {} + info['Python'] = sys.version.split()[0] + info['OS'] = f"{platform.system()} {platform.release()}" + info['Architecture'] = platform.machine() + info['OpenCV'] = cv2.__version__ + info['CUDA'] = "Yes" if cv2.cuda.getCudaEnabledDeviceCount() > 0 else "No" + info['NumPy'] = np.__version__ + info['Requests'] = requests.__version__ + + mem = psutil.virtual_memory() + info['MemoryGB'] = mem.total // (1024**3) + info['Memory'] = f"{info['MemoryGB']} GB RAM" + + info['CPU Cores'] = psutil.cpu_count(logical=False) + info['Logical CPUs'] = psutil.cpu_count(logical=True) + info['CPU Usage'] = f"{psutil.cpu_percent()}%" + + try: + if sys.platform == "win32": + info['CPU'] = platform.processor() + elif sys.platform == "linux": + info['CPU'] = subprocess.check_output("lscpu", shell=True).decode().split("\n")[0] + elif sys.platform == "darwin": + info['CPU'] = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"]).decode().strip() + except Exception: + info['CPU'] = "Unknown" + + return info + + class NetworkCameraDialog(QDialog): def __init__(self, parent=None): super().__init__(parent)