spinner 추가

This commit is contained in:
westnife3 2025-08-27 19:02:11 +09:00
parent f6af247743
commit 0a63ae00d7
5 changed files with 231 additions and 43 deletions

29
qtDash/loading_overlay.py Normal file
View File

@ -0,0 +1,29 @@
# loading_overlay.py
from PyQt6.QtWidgets import QWidget, QLabel
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QMovie
class LoadingOverlay(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setStyleSheet("background-color: rgba(0, 0, 0, 150);")
self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True)
self.spinner = QLabel(self)
self.spinner.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.movie = QMovie("spinner.gif")
self.spinner.setMovie(self.movie)
layout = self.spinner.geometry()
self.resize(parent.size())
self.spinner.resize(100, 100)
self.spinner.move((self.width() - 100) // 2, (self.height() - 100) // 2)
self.hide()
def start(self):
self.movie.start()
self.show()
def stop(self):
self.movie.stop()
self.hide()

View File

@ -1,60 +1,65 @@
from PyQt6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QLabel, QGridLayout, QProgressBar
)
from PyQt6.QtCore import Qt, QTimer
# main.py
from PyQt6.QtWidgets import QApplication, QWidget, QGridLayout, QVBoxLayout
from PyQt6.QtCore import QTimer
import sys
from loading_overlay import LoadingOverlay
from monitor_data import get_monitor_data
from monitor_card import MonitorCard # 카드 UI는 따로 분리해도 좋음
class LoadingScreen(QWidget):
class Dashboard(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Server Monitor Dashboard")
self.setFixedSize(960, 640)
self.setWindowTitle("서버 모니터링 - 로딩 중")
layout = QVBoxLayout()
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.label = QLabel("서버 상태를 불러오는 중...")
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.spinner = QProgressBar()
self.spinner.setRange(0, 0) # 무한 로딩 표시
self.cards = {}
self.titles = [
"CPU", "RAM", "Disk", "Uptime",
"CPU Temp", "GPU Temp", "GPU Usage", "Swap",
"Download", "Upload", "Alive", "Processes"
]
layout.addWidget(self.label)
layout.addWidget(self.spinner)
self.setLayout(layout)
# 메인 레이아웃
self.layout = QVBoxLayout(self)
self.setLayout(self.layout)
class MonitoringGrid(QWidget):
def __init__(self):
super().__init__()
self.setFixedSize(960, 640)
self.setWindowTitle("서버 모니터링")
# 카드 그리드
self.grid = QGridLayout()
self.grid.setSpacing(10)
self.layout.addLayout(self.grid)
grid = QGridLayout()
grid.setSpacing(10)
# 카드 생성
for i, title in enumerate(self.titles):
row = i // 4
col = i % 4
card = MonitorCard(title)
self.grid.addWidget(card, row, col)
self.cards[title] = card
for row in range(3):
for col in range(4):
label = QLabel(f"서버 {row * 3 + col + 1}")
label.setStyleSheet("background-color: #e0e0e0; padding: 20px; border: 1px solid #aaa;")
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
grid.addWidget(label, row, col)
# 오버레이 생성
self.overlay = LoadingOverlay(self)
self.overlay.start()
self.setLayout(grid)
# 데이터 수신 후
QTimer.singleShot(2000, self.finish_loading)
class MainApp:
def __init__(self):
self.app = QApplication(sys.argv)
self.loading = LoadingScreen()
self.grid = MonitoringGrid()
# 주기적 업데이트
self.timer = QTimer()
self.timer.timeout.connect(self.update_data)
self.timer.start(3000)
def run(self):
self.loading.show()
def finish_loading(self):
self.overlay.stop()
self.update_data()
# 3초 후 로딩 화면 종료하고 그리드 표시
QTimer.singleShot(3000, self.show_grid)
sys.exit(self.app.exec())
def update_data(self):
data = get_monitor_data()
for key in self.titles:
self.cards[key].update_value(data.get(key, "--"))
def show_grid(self):
self.loading.close()
self.grid.show()
if __name__ == "__main__":
MainApp().run()
app = QApplication(sys.argv)
dashboard = Dashboard()
dashboard.show()
sys.exit(app.exec())

48
qtDash/monitor_card.py Normal file
View File

@ -0,0 +1,48 @@
# monitor_card.py
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QMovie
class MonitorCard(QWidget):
def __init__(self, title, bg_color="#2e2e2e"):
super().__init__()
self.setStyleSheet(f"""
background-color: {bg_color};
border-radius: 10px;
border: 1px solid #444;
""")
layout = QVBoxLayout()
self.title = QLabel(title)
self.title.setStyleSheet("font-weight: bold; font-size: 16px; color: white;")
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.value = QLabel("--")
self.value.setStyleSheet("font-size: 24px; font-weight: bold; color: white;")
self.value.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.spinner = QLabel()
self.spinner.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.movie = QMovie("spinner.gif")
self.spinner.setMovie(self.movie)
self.spinner.hide()
layout.addWidget(self.title)
layout.addWidget(self.value)
layout.addWidget(self.spinner)
self.setLayout(layout)
def start_loading(self):
self.value.hide()
self.spinner.show()
self.movie.start()
def stop_loading(self):
self.movie.stop()
self.spinner.hide()
self.value.show()
def update_value(self, text):
self.stop_loading()
self.value.setText(text)

74
qtDash/monitor_data.py Normal file
View File

@ -0,0 +1,74 @@
# monitor_data.py
import psutil
import subprocess
import GPUtil
import time
def format_uptime():
uptime_seconds = time.time() - psutil.boot_time()
days = int(uptime_seconds // 86400)
hours = int((uptime_seconds % 86400) // 3600)
minutes = int((uptime_seconds % 3600) // 60)
return f"{days}d {hours:02d}h {minutes:02d}m" if days > 0 else f"{hours:02d}h {minutes:02d}m"
def get_cpu_temp():
try:
output = subprocess.check_output(['sensors'], universal_newlines=True)
for line in output.splitlines():
if "Package id 0:" in line or "Core 0:" in line:
temp_str = line.split(":")[1].strip().split("°")[0]
return f"{float(temp_str)}°C"
except Exception:
return "---"
def get_gpu_temp():
try:
gpus = GPUtil.getGPUs()
return f"{gpus[0].temperature}°C" if gpus else "---"
except Exception:
return "---"
def get_gpu_usage():
try:
gpus = GPUtil.getGPUs()
return f"{gpus[0].load * 100:.1f}%" if gpus else "---"
except Exception:
return "---"
def get_net_speed(interface='enp0s31f6', interval=1):
try:
start = psutil.net_io_counters(pernic=True)[interface]
time.sleep(interval)
end = psutil.net_io_counters(pernic=True)[interface]
download = (end.bytes_recv - start.bytes_recv) / 1024 / interval
upload = (end.bytes_sent - start.bytes_sent) / 1024 / interval
return f"{download:.2f} KB/s", f"{upload:.2f} KB/s"
except Exception:
return "---", "---"
def check_alive(ip="192.168.0.101"):
try:
result = subprocess.run(["ping", "-c", "1", "-W", "1", ip],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return "True" if result.returncode == 0 else "False"
except Exception:
return "---"
def get_monitor_data():
download, upload = get_net_speed()
return {
"CPU": f"{psutil.cpu_percent()}%",
"RAM": f"{psutil.virtual_memory().percent}%",
"Disk": f"{psutil.disk_usage('/').percent}%",
"Uptime": format_uptime(),
"CPU Temp": get_cpu_temp(),
"GPU Temp": get_gpu_temp(),
"GPU Usage": get_gpu_usage(),
"Swap": f"{psutil.swap_memory().percent}%",
"Download": download,
"Upload": upload,
"Alive": check_alive(),
"Processes": str(len(psutil.pids()))
}

32
qtDash/t.py Normal file
View File

@ -0,0 +1,32 @@
# main.py
# main.py
from PyQt6.QtWidgets import QApplication, QWidget, QGridLayout
from PyQt6.QtCore import QTimer
import sys
from monitor_data import get_monitor_data
from monitor_card import MonitorCard # 카드 UI는 따로 분리해도 좋음
from loading_overlay import LoadingOverlay
class Dashboard(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
# 카드들 배치
self.layout = QVBoxLayout(self)
self.setLayout(self.layout)
# 오버레이 생성
self.overlay = LoadingOverlay(self)
# 로딩 시작
self.overlay.start()
# 데이터 수신 후
QTimer.singleShot(2000, self.finish_loading)
def finish_loading(self):
self.overlay.stop()
# 카드들에 값 업데이트