#!/usr/bin/env python3 """ Floating Window UI for GPU Monitor PyQt6-based always-on-top floating window """ import sys from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QHBoxLayout, QPushButton from PyQt6.QtCore import Qt, QPoint, QTimer from PyQt6.QtGui import QMouseEvent class FloatingWindow(QWidget): def __init__(self): super().__init__() self.initUI() self.oldPos = self.pos() def initUI(self): # Set window flags for floating behavior self.setWindowFlags( Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint | Qt.WindowType.Tool ) # Set window properties self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.setFixedSize(220, 200) # Create layout self.layout = QVBoxLayout() self.layout.setContentsMargins(10, 10, 10, 10) self.layout.setSpacing(2) # Set stylesheet self.setStyleSheet(""" QWidget { background-color: rgba(0, 0, 0, 180); border-radius: 10px; border: 1px solid #3498db; } QLabel { color: #ecf0f1; font-family: 'Segoe UI', Arial; font-size: 12px; font-weight: bold; border: none; } #title { color: #3498db; font-size: 14px; margin-bottom: 5px; } """) # Create title bar self.title_layout = QHBoxLayout() self.title_layout.setContentsMargins(0, 0, 0, 5) self.lbl_title = QLabel("AMD GPU MONITOR") self.lbl_title.setObjectName("title") self.btn_close = QPushButton("×") self.btn_close.setFixedSize(20, 20) self.btn_close.setStyleSheet(""" QPushButton { background-color: transparent; color: #e74c3c; font-size: 18px; font-weight: bold; border: none; margin: 0; padding: 0; } QPushButton:hover { color: #ff0000; } """) self.btn_close.clicked.connect(self.close) self.title_layout.addWidget(self.lbl_title) self.title_layout.addStretch() self.title_layout.addWidget(self.btn_close) # Create data labels self.lbl_load = QLabel("GPU Load: --%") self.lbl_clocks = QLabel("Clocks: -- / -- MHz") self.lbl_vram = QLabel("VRAM: -- / -- MB") self.lbl_temp = QLabel("Temp: --°C") self.lbl_power = QLabel("Power: --W") self.lbl_fan = QLabel("Fan: -- RPM (--%)") # Add widgets to layout self.layout.addLayout(self.title_layout) self.layout.addWidget(self.lbl_load) self.layout.addWidget(self.lbl_clocks) self.layout.addWidget(self.lbl_vram) self.layout.addWidget(self.lbl_temp) self.layout.addWidget(self.lbl_power) self.layout.addWidget(self.lbl_fan) self.setLayout(self.layout) def update_data(self, data): """Update the UI with new GPU data""" # Update GPU load if data['gpu_usage'] is not None: self.lbl_load.setText(f"GPU Load: {data['gpu_usage']:.1f}%") else: self.lbl_load.setText("GPU Load: --%") # Update clocks core_clk = data['core_clock'] if data['core_clock'] is not None else "--" mem_clk = data['mem_clock'] if data['mem_clock'] is not None else "--" self.lbl_clocks.setText(f"Clocks: {core_clk} / {mem_clk} MHz") # Update VRAM if data['vram_used'] is not None and data['vram_total'] is not None: self.lbl_vram.setText(f"VRAM: {data['vram_used']} / {data['vram_total']} MB") else: self.lbl_vram.setText("VRAM: -- / -- MB") # Update temperature if data['temperature'] is not None: self.lbl_temp.setText(f"Temp: {data['temperature']:.1f}°C") else: self.lbl_temp.setText("Temp: --°C") # Update power if data['power_draw'] is not None: self.lbl_power.setText(f"Power: {data['power_draw']:.1f}W") else: self.lbl_power.setText("Power: --W") # Update fan rpm = data['fan_rpm'] if data['fan_rpm'] is not None else "--" pwm = data['fan_pwm'] if data['fan_pwm'] is not None else "--" self.lbl_fan.setText(f"Fan: {rpm} RPM ({pwm}%)") def mousePressEvent(self, event: QMouseEvent): """Handle mouse press for window dragging""" if event.button() == Qt.MouseButton.LeftButton: self.oldPos = event.globalPosition().toPoint() def mouseMoveEvent(self, event: QMouseEvent): """Handle mouse move for window dragging""" if event.buttons() & Qt.MouseButton.LeftButton: delta = event.globalPosition().toPoint() - self.oldPos self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPos = event.globalPosition().toPoint() if __name__ == "__main__": app = QApplication(sys.argv) window = FloatingWindow() window.show() sys.exit(app.exec())