터치 패널 반응성 개선과 커서 숨김

This commit is contained in:
seo committed 2026-07-30 04:50:34 +09:00
1 parent 209b587eac
commit ed76621201
1 file changed
+83 -37
+83 -37
View File
@@ -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; }