Téléverser les fichiers vers "/"
This commit is contained in:
BIN
7cooler-ultimate_1.0_amd64.deb
Normal file
BIN
7cooler-ultimate_1.0_amd64.deb
Normal file
Binary file not shown.
422
7cooler_ultimate.py
Normal file
422
7cooler_ultimate.py
Normal file
@@ -0,0 +1,422 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys, time, subprocess
|
||||
import psutil
|
||||
from collections import deque
|
||||
from PyQt6.QtCore import Qt, QTimer, QThread, pyqtSignal, QPointF, QRectF, QPropertyAnimation, QEasingCurve, pyqtProperty
|
||||
from PyQt6.QtGui import QFont, QPainter, QColor, QPen, QBrush, QPainterPath, QAction, QIcon
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QProgressBar, QSystemTrayIcon, QMenu, QGridLayout,
|
||||
QStackedWidget, QPushButton, QFrame, QScrollArea, QSlider)
|
||||
|
||||
# ==========================================
|
||||
# THREAD DE DONNÉES (CPU, RAM, GPU, RESEAU)
|
||||
# ==========================================
|
||||
class UltimateMonitorThread(QThread):
|
||||
stats_updated = pyqtSignal(dict)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.interval = 1000
|
||||
self.net_io = psutil.net_io_counters()
|
||||
self.last_time = time.time()
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
current_time = time.time()
|
||||
dt = current_time - self.last_time
|
||||
new_net_io = psutil.net_io_counters()
|
||||
|
||||
stats = {
|
||||
'cpu_temp': self.get_cpu_temp(),
|
||||
'cpu_percent': psutil.cpu_percent(interval=None),
|
||||
'cores': self.get_cores_temp(),
|
||||
'ram': psutil.virtual_memory().percent,
|
||||
'swap': psutil.swap_memory().percent,
|
||||
'disk': psutil.disk_usage('/').percent,
|
||||
'net_dl': (new_net_io.bytes_recv - self.net_io.bytes_recv) / dt / 1024 / 1024, # MB/s
|
||||
'net_ul': (new_net_io.bytes_sent - self.net_io.bytes_sent) / dt / 1024 / 1024, # MB/s
|
||||
'gpu': self.get_gpu_info()
|
||||
}
|
||||
|
||||
self.net_io = new_net_io
|
||||
self.last_time = current_time
|
||||
self.stats_updated.emit(stats)
|
||||
self.msleep(self.interval)
|
||||
|
||||
def get_cpu_temp(self):
|
||||
temps = psutil.sensors_temperatures()
|
||||
for name in ['coretemp', 'acpitz', 'cpu_thermal', 'k10temp']:
|
||||
if name in temps: return int(temps[name][0].current)
|
||||
return 40
|
||||
|
||||
def get_cores_temp(self):
|
||||
cores = []
|
||||
temps = psutil.sensors_temperatures()
|
||||
if 'coretemp' in temps:
|
||||
cores = [(e.label, int(e.current)) for e in temps['coretemp'] if 'Core' in e.label]
|
||||
return cores if cores else [(f"Core {i}", 40) for i in range(psutil.cpu_count(logical=False) or 4)]
|
||||
|
||||
def get_gpu_info(self):
|
||||
# Tente de récupérer les infos NVIDIA en priorité
|
||||
try:
|
||||
res = subprocess.check_output(['nvidia-smi', '--query-gpu=temperature.gpu,utilization.gpu', '--format=csv,noheader,nounits'], encoding='utf-8')
|
||||
temp, util = map(int, res.strip().split(', '))
|
||||
return {"name": "NVIDIA GPU", "temp": temp, "util": util}
|
||||
except:
|
||||
# Fallback sur les capteurs standards (AMD/Intel)
|
||||
temps = psutil.sensors_temperatures()
|
||||
if 'amdgpu' in temps:
|
||||
return {"name": "AMD GPU", "temp": int(temps['amdgpu'][0].current), "util": 0}
|
||||
return None
|
||||
|
||||
# ==========================================
|
||||
# WIDGETS ANIMÉS ZORIN OS PRO
|
||||
# ==========================================
|
||||
class SmoothProgressBar(QProgressBar):
|
||||
def __init__(self, color="#1E88E5"):
|
||||
super().__init__()
|
||||
self.setFixedHeight(6)
|
||||
self.setTextVisible(False)
|
||||
self._color = color
|
||||
self._value = 0
|
||||
self.anim = QPropertyAnimation(self, b"animated_value")
|
||||
self.anim.setEasingCurve(QEasingCurve.Type.OutCubic)
|
||||
self.anim.setDuration(400)
|
||||
self.update_style()
|
||||
|
||||
def update_style(self):
|
||||
self.setStyleSheet(f"""
|
||||
QProgressBar {{ background-color: #222222; border-radius: 3px; }}
|
||||
QProgressBar::chunk {{ background-color: {self._color}; border-radius: 3px; }}
|
||||
""")
|
||||
|
||||
def set_color(self, color):
|
||||
self._color = color
|
||||
self.update_style()
|
||||
|
||||
@pyqtProperty(int)
|
||||
def animated_value(self): return self._value
|
||||
|
||||
@animated_value.setter
|
||||
def animated_value(self, val):
|
||||
self._value = val
|
||||
super().setValue(val)
|
||||
|
||||
def set_smooth_value(self, val):
|
||||
self.anim.setStartValue(self._value)
|
||||
self.anim.setEndValue(val)
|
||||
self.anim.start()
|
||||
|
||||
class ModernGauge(QWidget):
|
||||
def __init__(self, title, color="#1E88E5"):
|
||||
super().__init__()
|
||||
self.setFixedSize(180, 180)
|
||||
self.value = 0
|
||||
self.title = title
|
||||
self.color = QColor(color)
|
||||
|
||||
def set_value(self, val):
|
||||
self.value = val
|
||||
self.update()
|
||||
|
||||
def set_color(self, hex_color):
|
||||
self.color = QColor(hex_color)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
rect = QRectF(20, 20, 140, 140)
|
||||
|
||||
painter.setPen(QPen(QColor("#2A2A2A"), 12, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))
|
||||
painter.drawArc(rect, 135 * 16, -270 * 16)
|
||||
|
||||
span = int(-270 * (self.value / 100.0) * 16)
|
||||
painter.setPen(QPen(self.color, 12, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap))
|
||||
painter.drawArc(rect, 135 * 16, span)
|
||||
|
||||
painter.setPen(QColor("#FFFFFF"))
|
||||
painter.setFont(QFont("Inter", 32, QFont.Weight.Thin))
|
||||
painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, f"{self.value}°")
|
||||
|
||||
painter.setFont(QFont("Inter", 9, QFont.Weight.Bold))
|
||||
painter.setPen(QColor("#888888"))
|
||||
painter.drawText(QRectF(20, 140, 140, 30), Qt.AlignmentFlag.AlignCenter, self.title)
|
||||
|
||||
# ==========================================
|
||||
# INTERFACE PRINCIPALE
|
||||
# ==========================================
|
||||
class UltimateApp(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.accent_color = "#1E88E5" # Bleu Zorin par défaut
|
||||
self.init_ui()
|
||||
self.init_tray()
|
||||
|
||||
self.thread = UltimateMonitorThread()
|
||||
self.thread.stats_updated.connect(self.update_data)
|
||||
self.thread.start()
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle("7Cooler Ultimate")
|
||||
self.resize(850, 550)
|
||||
self.setStyleSheet("QMainWindow { background-color: #0E0E0E; color: #FFFFFF; }")
|
||||
|
||||
main_widget = QWidget()
|
||||
self.setCentralWidget(main_widget)
|
||||
main_layout = QHBoxLayout(main_widget)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
# --- MENU LATÉRAL ---
|
||||
sidebar = QFrame()
|
||||
sidebar.setFixedWidth(200)
|
||||
sidebar.setStyleSheet("background-color: #151515; border-right: 1px solid #222;")
|
||||
side_layout = QVBoxLayout(sidebar)
|
||||
side_layout.setContentsMargins(10, 30, 10, 30)
|
||||
|
||||
brand = QLabel("7COOLER")
|
||||
brand.setFont(QFont("Inter", 16, QFont.Weight.Black))
|
||||
brand.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
brand.setStyleSheet("letter-spacing: 2px; margin-bottom: 30px;")
|
||||
side_layout.addWidget(brand)
|
||||
|
||||
self.btn_dash = self.create_nav_btn("📊 Tableau de bord", 0)
|
||||
self.btn_hard = self.create_nav_btn("🌡️ Matériel & Capteurs", 1)
|
||||
self.btn_set = self.create_nav_btn("⚙️ Paramètres", 2)
|
||||
|
||||
side_layout.addWidget(self.btn_dash)
|
||||
side_layout.addWidget(self.btn_hard)
|
||||
side_layout.addWidget(self.btn_set)
|
||||
side_layout.addStretch()
|
||||
|
||||
# --- CONTENU CENTRAL (PAGES) ---
|
||||
self.stack = QStackedWidget()
|
||||
self.stack.setStyleSheet("background-color: #0E0E0E;")
|
||||
|
||||
self.page_dash = self.build_dashboard()
|
||||
self.page_hard = self.build_hardware()
|
||||
self.page_set = self.build_settings()
|
||||
|
||||
self.stack.addWidget(self.page_dash)
|
||||
self.stack.addWidget(self.page_hard)
|
||||
self.stack.addWidget(self.page_set)
|
||||
|
||||
main_layout.addWidget(sidebar)
|
||||
main_layout.addWidget(self.stack)
|
||||
|
||||
self.switch_page(0)
|
||||
|
||||
def create_nav_btn(self, text, index):
|
||||
btn = QPushButton(text)
|
||||
btn.setFont(QFont("Inter", 10, QFont.Weight.Bold))
|
||||
btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
btn.setStyleSheet("""
|
||||
QPushButton { text-align: left; padding: 12px; border-radius: 8px; background: transparent; color: #888; border: none; }
|
||||
QPushButton:hover { background: #222; color: #FFF; }
|
||||
""")
|
||||
btn.clicked.connect(lambda: self.switch_page(index))
|
||||
return btn
|
||||
|
||||
def switch_page(self, index):
|
||||
self.stack.setCurrentIndex(index)
|
||||
for i, btn in enumerate([self.btn_dash, self.btn_hard, self.btn_set]):
|
||||
if i == index:
|
||||
btn.setStyleSheet(f"text-align: left; padding: 12px; border-radius: 8px; background: {self.accent_color}; color: #FFF; font-weight: bold; border: none;")
|
||||
else:
|
||||
btn.setStyleSheet("text-align: left; padding: 12px; border-radius: 8px; background: transparent; color: #888; border: none;")
|
||||
|
||||
# --- PAGE 1: DASHBOARD ---
|
||||
def build_dashboard(self):
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
layout.setContentsMargins(40, 40, 40, 40)
|
||||
|
||||
title = QLabel("Vue d'ensemble")
|
||||
title.setFont(QFont("Inter", 22, QFont.Weight.Bold))
|
||||
layout.addWidget(title)
|
||||
|
||||
# Jauges Hautes
|
||||
gauge_layout = QHBoxLayout()
|
||||
self.cpu_gauge = ModernGauge("CPU TEMP")
|
||||
self.gpu_gauge = ModernGauge("GPU TEMP")
|
||||
gauge_layout.addWidget(self.cpu_gauge)
|
||||
gauge_layout.addWidget(self.gpu_gauge)
|
||||
gauge_layout.addStretch()
|
||||
layout.addLayout(gauge_layout)
|
||||
|
||||
layout.addSpacing(30)
|
||||
|
||||
# Barres d'utilisation
|
||||
self.bars = {}
|
||||
for name, label in [('cpu', 'Charge CPU'), ('ram', 'Mémoire Vive'), ('swap', 'Fichier d\'échange'), ('disk', 'Stockage Principal')]:
|
||||
lbl = QLabel(f"{label}: 0%")
|
||||
lbl.setFont(QFont("Inter", 10))
|
||||
bar = SmoothProgressBar()
|
||||
self.bars[name] = {'lbl': lbl, 'bar': bar}
|
||||
layout.addWidget(lbl)
|
||||
layout.addWidget(bar)
|
||||
layout.addSpacing(10)
|
||||
|
||||
layout.addStretch()
|
||||
return page
|
||||
|
||||
# --- PAGE 2: HARDWARE ---
|
||||
def build_hardware(self):
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
layout.setContentsMargins(40, 40, 40, 40)
|
||||
|
||||
title = QLabel("Capteurs Détaillés")
|
||||
title.setFont(QFont("Inter", 22, QFont.Weight.Bold))
|
||||
layout.addWidget(title)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setStyleSheet("QScrollArea { border: none; background: transparent; }")
|
||||
|
||||
content = QWidget()
|
||||
self.grid = QGridLayout(content)
|
||||
scroll.setWidget(content)
|
||||
layout.addWidget(scroll)
|
||||
|
||||
# Réseau UI
|
||||
self.net_lbl = QLabel("Réseau: DL 0 MB/s | UL 0 MB/s")
|
||||
self.net_lbl.setFont(QFont("Inter", 12, QFont.Weight.Bold))
|
||||
self.net_lbl.setStyleSheet("color: #888;")
|
||||
layout.addWidget(self.net_lbl)
|
||||
|
||||
return page
|
||||
|
||||
# --- PAGE 3: SETTINGS ---
|
||||
def build_settings(self):
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
layout.setContentsMargins(40, 40, 40, 40)
|
||||
|
||||
title = QLabel("Paramètres de l'Application")
|
||||
title.setFont(QFont("Inter", 22, QFont.Weight.Bold))
|
||||
layout.addWidget(title)
|
||||
|
||||
# Thèmes
|
||||
theme_lbl = QLabel("Couleur d'accentuation")
|
||||
theme_lbl.setFont(QFont("Inter", 12, QFont.Weight.Bold))
|
||||
layout.addWidget(theme_lbl)
|
||||
|
||||
colors_layout = QHBoxLayout()
|
||||
for c in ["#1E88E5", "#E53935", "#43A047", "#8E24AA", "#FDD835", "#FFFFFF"]:
|
||||
btn = QPushButton()
|
||||
btn.setFixedSize(40, 40)
|
||||
btn.setStyleSheet(f"background-color: {c}; border-radius: 20px; border: 2px solid #333;")
|
||||
btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
btn.clicked.connect(lambda checked, col=c: self.change_accent(col))
|
||||
colors_layout.addWidget(btn)
|
||||
colors_layout.addStretch()
|
||||
layout.addLayout(colors_layout)
|
||||
|
||||
layout.addSpacing(30)
|
||||
|
||||
# Intervalle
|
||||
speed_lbl = QLabel("Vitesse de rafraîchissement (ms)")
|
||||
speed_lbl.setFont(QFont("Inter", 12, QFont.Weight.Bold))
|
||||
layout.addWidget(speed_lbl)
|
||||
|
||||
self.slider = QSlider(Qt.Orientation.Horizontal)
|
||||
self.slider.setRange(500, 3000)
|
||||
self.slider.setValue(1000)
|
||||
self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
||||
self.slider.setTickInterval(500)
|
||||
self.slider.valueChanged.connect(self.change_speed)
|
||||
layout.addWidget(self.slider)
|
||||
|
||||
layout.addStretch()
|
||||
return page
|
||||
|
||||
def change_accent(self, color):
|
||||
self.accent_color = color
|
||||
self.cpu_gauge.set_color(color)
|
||||
self.gpu_gauge.set_color(color)
|
||||
for b in self.bars.values(): b['bar'].set_color(color)
|
||||
self.switch_page(self.stack.currentIndex()) # Refresh menu color
|
||||
|
||||
def change_speed(self, val):
|
||||
self.thread.interval = val
|
||||
|
||||
def update_data(self, stats):
|
||||
# Update Dashboard
|
||||
self.cpu_gauge.set_value(stats['cpu_temp'])
|
||||
|
||||
if stats['gpu']:
|
||||
self.gpu_gauge.set_value(stats['gpu']['temp'])
|
||||
self.gpu_gauge.title = stats['gpu']['name'].upper()
|
||||
else:
|
||||
self.gpu_gauge.set_value(0)
|
||||
self.gpu_gauge.title = "NO GPU"
|
||||
|
||||
self.bars['cpu']['lbl'].setText(f"Charge CPU: {stats['cpu_percent']}%")
|
||||
self.bars['cpu']['bar'].set_smooth_value(int(stats['cpu_percent']))
|
||||
|
||||
self.bars['ram']['lbl'].setText(f"Mémoire Vive: {stats['ram']}%")
|
||||
self.bars['ram']['bar'].set_smooth_value(int(stats['ram']))
|
||||
|
||||
self.bars['swap']['lbl'].setText(f"Fichier d'échange: {stats['swap']}%")
|
||||
self.bars['swap']['bar'].set_smooth_value(int(stats['swap']))
|
||||
|
||||
self.bars['disk']['lbl'].setText(f"Stockage Principal: {stats['disk']}%")
|
||||
self.bars['disk']['bar'].set_smooth_value(int(stats['disk']))
|
||||
|
||||
# Update Network
|
||||
self.net_lbl.setText(f"Réseau: ↓ {stats['net_dl']:.2f} MB/s | ↑ {stats['net_ul']:.2f} MB/s")
|
||||
|
||||
# Update Hardware Grid
|
||||
for i in reversed(range(self.grid.count())):
|
||||
w = self.grid.itemAt(i).widget()
|
||||
if w: w.setParent(None)
|
||||
|
||||
row, col = 0, 0
|
||||
for name, temp in stats['cores']:
|
||||
card = QFrame()
|
||||
card.setStyleSheet("background-color: #1A1A1A; border-radius: 10px; padding: 10px;")
|
||||
cl = QVBoxLayout(card)
|
||||
|
||||
n = QLabel(name)
|
||||
n.setStyleSheet("color: #888; font-size: 11px;")
|
||||
t = QLabel(f"{temp}°C")
|
||||
t.setFont(QFont("Inter", 16, QFont.Weight.Bold))
|
||||
if temp > 85: t.setStyleSheet("color: #E53935;")
|
||||
|
||||
cl.addWidget(n)
|
||||
cl.addWidget(t)
|
||||
self.grid.addWidget(card, row, col)
|
||||
|
||||
col += 1
|
||||
if col > 3:
|
||||
col, row = 0, row + 1
|
||||
|
||||
self.tray_icon.setToolTip(f"CPU: {stats['cpu_temp']}°C | RAM: {stats['ram']}%")
|
||||
|
||||
def init_tray(self):
|
||||
self.tray_icon = QSystemTrayIcon(self)
|
||||
self.tray_icon.setIcon(QIcon.fromTheme("utilities-system-monitor"))
|
||||
menu = QMenu()
|
||||
show_action = QAction("Ouvrir 7Cooler Ultimate", self)
|
||||
show_action.triggered.connect(self.show)
|
||||
quit_action = QAction("Quitter", self)
|
||||
quit_action.triggered.connect(QApplication.instance().quit)
|
||||
menu.addAction(show_action)
|
||||
menu.addAction(quit_action)
|
||||
self.tray_icon.setContextMenu(menu)
|
||||
self.tray_icon.show()
|
||||
|
||||
def closeEvent(self, event):
|
||||
event.ignore()
|
||||
self.hide()
|
||||
self.tray_icon.showMessage("7Cooler Ultimate", "Fonctionne en arrière-plan", QSystemTrayIcon.MessageIcon.Information, 2000)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
QApplication.setQuitOnLastWindowClosed(False)
|
||||
app.setFont(QFont("Inter", 10))
|
||||
window = UltimateApp()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user