터치 패널 반응성 개선과 커서 숨김
This commit is contained in:
1 parent
209b587eac
commit
ed76621201
1 file changed
+83
-37
+83
-37
@@ -4,8 +4,8 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import QPointF, Qt, QTimer
|
||||
from PyQt5.QtGui import QColor, QFont, QPainter, QPainterPath, QPen
|
||||
from PyQt5.QtCore import QObject, QPointF, Qt, QThread, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication,
|
||||
QFrame,
|
||||
@@ -51,15 +51,20 @@ class StatusDot(QWidget):
|
||||
self.pulse = 0
|
||||
self.setFixedSize(22, 22)
|
||||
|
||||
def set_state(self, active, warning=False):
|
||||
self.active = active
|
||||
def set_warning(self, warning=False):
|
||||
self.warning = warning
|
||||
self.pulse = 4 if active else 0
|
||||
self.update()
|
||||
|
||||
def start_pulse(self):
|
||||
self.active = True
|
||||
self.pulse = 4
|
||||
self.update()
|
||||
|
||||
def tick(self):
|
||||
if self.pulse > 0:
|
||||
self.pulse -= 1
|
||||
if self.pulse <= 0:
|
||||
self.active = False
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
@@ -79,8 +84,9 @@ class StatusDot(QWidget):
|
||||
|
||||
|
||||
class StatusTile(QFrame):
|
||||
def __init__(self, title):
|
||||
def __init__(self, title, pulse_delay_ms=0):
|
||||
super().__init__()
|
||||
self.pulse_delay_ms = pulse_delay_ms
|
||||
self.dot = StatusDot()
|
||||
self.title = QLabel(title)
|
||||
self.title.setObjectName("statusTitle")
|
||||
@@ -105,7 +111,9 @@ class StatusTile(QFrame):
|
||||
self.setObjectName("statusTile")
|
||||
|
||||
def update_state(self, active, detail, warning=False):
|
||||
self.dot.set_state(active, warning)
|
||||
self.dot.set_warning(warning)
|
||||
if active:
|
||||
QTimer.singleShot(self.pulse_delay_ms, self.dot.start_pulse)
|
||||
self.detail.setText(detail)
|
||||
|
||||
def tick(self):
|
||||
@@ -119,6 +127,22 @@ class FanButton(QPushButton):
|
||||
self.setMinimumHeight(44)
|
||||
|
||||
|
||||
class BridgeWorker(QObject):
|
||||
finished = pyqtSignal(str, object)
|
||||
failed = pyqtSignal(str, str)
|
||||
|
||||
def __init__(self, action, args):
|
||||
super().__init__()
|
||||
self.action = action
|
||||
self.args = args
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.finished.emit(self.action, run_bridge(*self.args))
|
||||
except Exception as exc:
|
||||
self.failed.emit(self.action, str(exc))
|
||||
|
||||
|
||||
class MetricCard(QFrame):
|
||||
def __init__(self, title):
|
||||
super().__init__()
|
||||
@@ -218,17 +242,20 @@ class TouchPanel(QMainWindow):
|
||||
self.metrics = {}
|
||||
self.charts = {}
|
||||
self.last_counters = {}
|
||||
self.bridge_busy = False
|
||||
self.worker_thread = None
|
||||
self.worker = None
|
||||
self.current_mode = "auto"
|
||||
self.current_pwm = 120
|
||||
self.in_flight = False
|
||||
|
||||
self.setWindowTitle("Control Touch Panel")
|
||||
self.setCursor(Qt.BlankCursor)
|
||||
self.setMinimumSize(800, 480)
|
||||
self._build_ui()
|
||||
|
||||
self.refresh_timer = QTimer(self)
|
||||
self.refresh_timer.timeout.connect(self.refresh)
|
||||
self.refresh_timer.start(1200)
|
||||
self.refresh_timer.start(1500)
|
||||
|
||||
self.pulse_timer = QTimer(self)
|
||||
self.pulse_timer.timeout.connect(self.tick_pulses)
|
||||
@@ -252,14 +279,14 @@ class TouchPanel(QMainWindow):
|
||||
title.setObjectName("sectionTitle")
|
||||
left_layout.addWidget(title)
|
||||
|
||||
for key, label in [
|
||||
for index, (key, label) in enumerate([
|
||||
("io", "IO"),
|
||||
("wan", "WAN"),
|
||||
("lan", "LAN"),
|
||||
("wifi24", "2.4G"),
|
||||
("wifi5", "5G"),
|
||||
]:
|
||||
row = StatusTile(label)
|
||||
]):
|
||||
row = StatusTile(label, index * 70)
|
||||
self.rows[key] = row
|
||||
left_layout.addWidget(row)
|
||||
left_layout.addStretch(1)
|
||||
@@ -274,8 +301,11 @@ class TouchPanel(QMainWindow):
|
||||
fan_title.setObjectName("sectionTitle")
|
||||
self.clock = QLabel("--:--:--")
|
||||
self.clock.setObjectName("clock")
|
||||
self.status_text = QLabel("")
|
||||
self.status_text.setObjectName("statusText")
|
||||
header.addWidget(fan_title)
|
||||
header.addStretch(1)
|
||||
header.addWidget(self.status_text)
|
||||
header.addWidget(self.clock)
|
||||
right_layout.addLayout(header)
|
||||
|
||||
@@ -331,10 +361,6 @@ class TouchPanel(QMainWindow):
|
||||
chart_grid.addWidget(chart, index // 2, index % 2)
|
||||
right_layout.addLayout(chart_grid, 1)
|
||||
|
||||
self.notice = QLabel("프로토타입")
|
||||
self.notice.setObjectName("notice")
|
||||
self.notice.setWordWrap(True)
|
||||
right_layout.addWidget(self.notice)
|
||||
right_layout.addStretch(1)
|
||||
|
||||
layout.addWidget(left)
|
||||
@@ -347,21 +373,12 @@ class TouchPanel(QMainWindow):
|
||||
|
||||
def slider_changed(self, value):
|
||||
self.current_pwm = value
|
||||
self.fan_pwm.setText(f"{value} PWM / {value / 255 * 100:.1f}%")
|
||||
if "pwm" in self.metrics:
|
||||
self.metrics["pwm"].set_value(f"{value} / {value / 255 * 100:.1f}%")
|
||||
|
||||
def set_fan(self, mode, pwm):
|
||||
if self.in_flight:
|
||||
return
|
||||
self.in_flight = True
|
||||
self.notice.setText("팬 변경 중")
|
||||
try:
|
||||
self.snapshot = run_bridge("fan", mode, pwm)
|
||||
self.notice.setText("팬 상태를 바꿨어")
|
||||
self.render()
|
||||
except Exception as exc:
|
||||
self.notice.setText(str(exc))
|
||||
finally:
|
||||
self.in_flight = False
|
||||
if self.start_bridge("fan", "fan", mode, pwm):
|
||||
self.status_text.setText("팬 변경 중")
|
||||
|
||||
def step_pwm(self, delta):
|
||||
value = max(0, min(255, self.slider.value() + delta))
|
||||
@@ -369,14 +386,42 @@ class TouchPanel(QMainWindow):
|
||||
self.set_fan("manual", value)
|
||||
|
||||
def refresh(self):
|
||||
if self.in_flight:
|
||||
return
|
||||
try:
|
||||
self.snapshot = run_bridge("status")
|
||||
self.start_bridge("status", "status")
|
||||
|
||||
def start_bridge(self, action, *args):
|
||||
if self.bridge_busy:
|
||||
return False
|
||||
self.bridge_busy = True
|
||||
self.worker_thread = QThread(self)
|
||||
self.worker = BridgeWorker(action, args)
|
||||
self.worker.moveToThread(self.worker_thread)
|
||||
self.worker_thread.started.connect(self.worker.run)
|
||||
self.worker.finished.connect(self.bridge_done)
|
||||
self.worker.failed.connect(self.bridge_failed)
|
||||
self.worker.finished.connect(self.worker_thread.quit)
|
||||
self.worker.failed.connect(self.worker_thread.quit)
|
||||
self.worker.finished.connect(self.worker.deleteLater)
|
||||
self.worker.failed.connect(self.worker.deleteLater)
|
||||
self.worker_thread.finished.connect(self.worker_thread.deleteLater)
|
||||
self.worker_thread.finished.connect(self.bridge_finished)
|
||||
self.worker_thread.start()
|
||||
return True
|
||||
|
||||
def bridge_done(self, action, data):
|
||||
self.snapshot = data
|
||||
self.status_text.setText("팬 변경 완료" if action == "fan" else "")
|
||||
self.render()
|
||||
except Exception as exc:
|
||||
self.notice.setText(str(exc))
|
||||
self.rows["io"].update_state(False, "control snapshot 실패", True)
|
||||
|
||||
def bridge_failed(self, action, message):
|
||||
del action
|
||||
self.status_text.setText("통신 오류")
|
||||
self.rows["io"].update_state(False, "snapshot 실패", True)
|
||||
print(message, file=sys.stderr)
|
||||
|
||||
def bridge_finished(self):
|
||||
self.bridge_busy = False
|
||||
self.worker_thread = None
|
||||
self.worker = None
|
||||
|
||||
def render(self):
|
||||
data = self.snapshot
|
||||
@@ -500,6 +545,7 @@ class TouchPanel(QMainWindow):
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
app.setOverrideCursor(Qt.BlankCursor)
|
||||
app.setStyleSheet(
|
||||
"""
|
||||
QWidget { background: #0b1220; color: #e5edf7; font-family: "Noto Sans CJK KR", "Noto Sans CJK KR Regular", "Noto Sans KR", sans-serif; }
|
||||
@@ -515,7 +561,7 @@ def main():
|
||||
#chartTitle { color: #dce8f8; font-size: 14px; font-weight: 700; background: transparent; }
|
||||
#chartCaption { color: #93a4bd; font-size: 11px; background: transparent; }
|
||||
#clock { font-size: 18px; color: #8fb3ff; font-weight: 700; }
|
||||
#notice { padding: 10px; background: #17233a; border: 1px solid #2b3c59; border-radius: 12px; color: #a8b8d0; }
|
||||
#statusText { color: #93a4bd; font-size: 13px; background: transparent; }
|
||||
QPushButton { min-height: 42px; border: 1px solid #33445f; border-radius: 14px; background: #1a2942; color: #dce8f8; font-size: 18px; font-weight: 700; }
|
||||
QPushButton:checked { background: #16a34a; color: white; border-color: #22c55e; }
|
||||
QPushButton#stepButton { min-width: 56px; font-size: 28px; }
|
||||
|
||||
Reference in new issue
Block a user