터치 패널 상태등 UI와 자동 실행 추가
This commit is contained in:
1 parent
44ed804121
commit
5bfac4429a
3 files changed
+48
-28
No files matched your search
@@ -70,6 +70,7 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
|
||||
- `bin/wifi_observe.php`: 5G WiFi 연결 시간 보정을 위한 시스템단 관측 CLI
|
||||
- `touch_panel/panel.py`: 라즈베리파이 정품 터치 디스플레이용 Qt 관리자 패널 프로토타입
|
||||
- `touch_panel/control_touch_bridge.php`: Qt 패널이 기존 Control PHP 함수를 호출하는 로컬 JSON 브리지
|
||||
- `touch_panel/control-touch-panel.desktop`: GUI 로그인 후 Qt 패널을 띄우는 데스크톱 autostart 파일
|
||||
- `systemd/control-wifi-observe.*`: WiFi 관측 CLI를 주기적으로 실행하는 systemd unit 예시
|
||||
- `systemd/control-touch-panel.service`: GUI 세션에서 Qt 관리자 패널을 띄우는 systemd unit 예시
|
||||
|
||||
@@ -145,7 +146,16 @@ cd /var/www/control
|
||||
/usr/bin/python3 touch_panel/panel.py
|
||||
```
|
||||
|
||||
패널은 `touch_panel/control_touch_bridge.php`를 통해 기존 `collect_snapshot()`, `set_control_state()`를 재사용합니다. 왼쪽에는 `IO`, `WAN`, `LAN`, `2.4G`, `5G` 상태 원을 표시하고 active 상태면 초록 원이 갱신 때 짧게 깜빡입니다. 오른쪽에는 팬 RPM, 판단 온도, PWM, `auto`/`manual`/`off`, PWM 증감 버튼을 둡니다. 팬 버튼은 우선 Control DB의 팬 상태를 저장하고, 실제 적용은 기존 Control 팬 정책 루프가 이어받는 구조입니다.
|
||||
패널은 `touch_panel/control_touch_bridge.php`를 통해 기존 `collect_snapshot()`, `set_control_state()`를 재사용합니다. 왼쪽에는 `IO`, `WAN`, `LAN`, `2.4G`, `5G` 상태 타일을 두고, 원은 평소 회색으로 두었다가 해당 상태가 살아 있는 갱신 순간에만 초록으로 짧게 깜빡입니다. 오른쪽에는 팬 RPM, 판단 온도, PWM, `auto`/`manual`/`off`, PWM 증감 버튼을 둡니다. 팬 버튼은 우선 Control DB의 팬 상태를 저장하고, 실제 적용은 기존 Control 팬 정책 루프가 이어받는 구조입니다.
|
||||
|
||||
GUI 자동 실행은 데스크톱 autostart가 1차 기준입니다.
|
||||
|
||||
```bash
|
||||
mkdir -p /home/seo/.config/autostart
|
||||
cp /var/www/control/touch_panel/control-touch-panel.desktop /home/seo/.config/autostart/
|
||||
```
|
||||
|
||||
systemd 방식이 필요하면 `systemd/control-touch-panel.service`를 `/etc/systemd/system/`에 복사해 씁니다.
|
||||
|
||||
## 갱신 주기
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Control Touch Panel
|
||||
Comment=Control Qt touch panel
|
||||
Exec=/usr/bin/python3 /var/www/control/touch_panel/panel.py
|
||||
Path=/var/www/control
|
||||
Terminal=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
+29
-27
@@ -52,26 +52,24 @@ class StatusDot(QWidget):
|
||||
self.setFixedSize(34, 34)
|
||||
|
||||
def set_state(self, active, warning=False):
|
||||
changed = self.active != active or self.warning != warning
|
||||
self.active = active
|
||||
self.warning = warning
|
||||
if active and changed:
|
||||
self.pulse = 6
|
||||
self.pulse = 4 if active else 0
|
||||
self.update()
|
||||
|
||||
def tick(self):
|
||||
if self.active:
|
||||
self.pulse = 6 if self.pulse <= 0 else self.pulse - 1
|
||||
if self.pulse > 0:
|
||||
self.pulse -= 1
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
del event
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
base = QColor("#22c55e") if self.active else QColor("#64748b")
|
||||
base = QColor("#8fa0aa")
|
||||
if self.warning:
|
||||
base = QColor("#f59e0b")
|
||||
if self.pulse > 0 and self.active and not self.warning:
|
||||
if self.pulse > 0 and self.active:
|
||||
base = QColor("#86efac") if self.pulse % 2 == 0 else QColor("#16a34a")
|
||||
painter.setBrush(base)
|
||||
painter.setPen(Qt.NoPen)
|
||||
@@ -80,7 +78,7 @@ class StatusDot(QWidget):
|
||||
painter.drawEllipse(10, 8, 8, 8)
|
||||
|
||||
|
||||
class StatusRow(QFrame):
|
||||
class StatusTile(QFrame):
|
||||
def __init__(self, title):
|
||||
super().__init__()
|
||||
self.dot = StatusDot()
|
||||
@@ -90,18 +88,17 @@ class StatusRow(QFrame):
|
||||
self.detail.setObjectName("statusDetail")
|
||||
self.detail.setWordWrap(True)
|
||||
|
||||
texts = QVBoxLayout()
|
||||
texts.setContentsMargins(0, 0, 0, 0)
|
||||
texts.setSpacing(2)
|
||||
texts.addWidget(self.title)
|
||||
texts.addWidget(self.detail)
|
||||
self.detail.setAlignment(Qt.AlignCenter)
|
||||
|
||||
layout = QHBoxLayout(self)
|
||||
layout.setContentsMargins(12, 9, 12, 9)
|
||||
layout.setSpacing(10)
|
||||
layout.addWidget(self.dot)
|
||||
layout.addLayout(texts, 1)
|
||||
self.setObjectName("statusRow")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(8, 7, 8, 8)
|
||||
layout.setSpacing(4)
|
||||
layout.addWidget(self.title, 0, Qt.AlignLeft)
|
||||
layout.addStretch(1)
|
||||
layout.addWidget(self.dot, 0, Qt.AlignCenter)
|
||||
layout.addStretch(1)
|
||||
layout.addWidget(self.detail)
|
||||
self.setObjectName("statusTile")
|
||||
|
||||
def update_state(self, active, detail, warning=False):
|
||||
self.dot.set_state(active, warning)
|
||||
@@ -152,21 +149,26 @@ class TouchPanel(QMainWindow):
|
||||
left.setObjectName("panel")
|
||||
left_layout = QVBoxLayout(left)
|
||||
left_layout.setContentsMargins(14, 14, 14, 14)
|
||||
left_layout.setSpacing(10)
|
||||
left_layout.setSpacing(8)
|
||||
|
||||
title = QLabel("상태")
|
||||
title.setObjectName("sectionTitle")
|
||||
left_layout.addWidget(title)
|
||||
for key, label in [
|
||||
|
||||
status_grid = QGridLayout()
|
||||
status_grid.setHorizontalSpacing(8)
|
||||
status_grid.setVerticalSpacing(8)
|
||||
for index, (key, label) in enumerate([
|
||||
("io", "IO"),
|
||||
("wan", "WAN"),
|
||||
("lan", "LAN"),
|
||||
("wifi24", "2.4G"),
|
||||
("wifi5", "5G"),
|
||||
]:
|
||||
row = StatusRow(label)
|
||||
]):
|
||||
row = StatusTile(label)
|
||||
self.rows[key] = row
|
||||
left_layout.addWidget(row)
|
||||
status_grid.addWidget(row, index // 2, index % 2)
|
||||
left_layout.addLayout(status_grid, 1)
|
||||
left_layout.addStretch(1)
|
||||
|
||||
right = QFrame()
|
||||
@@ -336,9 +338,9 @@ def main():
|
||||
QWidget { background: #0b1220; color: #e5edf7; font-family: "Noto Sans CJK KR", "Noto Sans KR", sans-serif; }
|
||||
#panel { background: #111a2e; border: 1px solid #27364f; border-radius: 18px; }
|
||||
#sectionTitle { font-size: 24px; font-weight: 700; }
|
||||
#statusRow { background: #18243a; border: 1px solid #263650; border-radius: 16px; }
|
||||
#statusTitle { font-size: 18px; font-weight: 700; color: #f8fafc; }
|
||||
#statusDetail { font-size: 13px; color: #9fb0c8; }
|
||||
#statusTile { background: #d7d9dc; border: 2px groove #f4f4f4; border-radius: 2px; }
|
||||
#statusTitle { font-size: 15px; font-weight: 700; color: #111827; background: transparent; }
|
||||
#statusDetail { font-size: 11px; color: #475569; background: transparent; }
|
||||
#bigValue { font-size: 56px; font-weight: 800; color: #f8fafc; }
|
||||
#subValue { font-size: 22px; color: #b8c5d8; }
|
||||
#clock { font-size: 18px; color: #8fb3ff; font-weight: 700; }
|
||||
|
||||
Reference in new issue
Block a user