#!/usr/bin/env python3 # Lancement: python cpu_monitor_gui.py import tkinter as tk from tkinter import ttk from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk from matplotlib.figure import Figure from collections import deque import time import psutil MAX_POINTS = 60 UPDATE_INTERVAL = 1000 # ms class Tracker: def __init__(self): self.cpu_data = deque(maxlen=MAX_POINTS) self.ram_data = deque(maxlen=MAX_POINTS) self.disk_data = deque(maxlen=MAX_POINTS) self.disk_read_data = deque(maxlen=MAX_POINTS) self.disk_write_data = deque(maxlen=MAX_POINTS) self.net_sent_data = deque(maxlen=MAX_POINTS) self.net_recv_data = deque(maxlen=MAX_POINTS) self._last_cpu = None self._last_net_sent = None self._last_net_recv = None self._last_disk_read = None self._last_disk_write = None now = time.time() self.time_data = deque(maxlen=MAX_POINTS) self.time_data.append(now) def _net_rate(self, curr, last): delta = curr - last rate = max(delta / 2.0, 0) return max(int(rate), 0) def update(self): cpu = psutil.cpu_percent(interval=None) ram = psutil.virtual_memory().percent disk = psutil.disk_usage('/').percent self._last_cpu = cpu t = time.time() self.time_data.append(t) self.cpu_data.append(cpu) self.ram_data.append(ram) self.disk_data.append(disk) d_io = psutil.disk_io_counters() if d_io is not None: d_read = d_io.read_bytes d_write = d_io.write_bytes if self._last_disk_read is not None: self.disk_read_data.append(max(int((d_read - self._last_disk_read) / 2.0), 0)) self.disk_write_data.append(max(int((d_write - self._last_disk_write) / 2.0), 0)) self._last_disk_read = d_read self._last_disk_write = d_write net = psutil.net_io_counters() sent = net.bytes_sent recv = net.bytes_recv if self._last_net_sent is not None: send_rate = self._net_rate(sent, self._last_net_sent) recv_rate = self._net_rate(recv, self._last_net_recv) self.net_sent_data.append(send_rate) self.net_recv_data.append(recv_rate) self._last_net_sent = sent self._last_net_recv = recv def get_latest(self): if self._last_cpu is None: self.update() return self._last_cpu, self.ram_data[-1] def get_disk(self): if not self.disk_data: return 0 return self.disk_data[-1] def get_disk_rates(self): if not self.disk_read_data or not self.disk_write_data: return 0, 0 return self.disk_read_data[-1], self.disk_write_data[-1] def get_net_rates(self): if not self.net_sent_data or not self.net_recv_data: return 0, 0 return self.net_sent_data[-1], self.net_recv_data[-1] class App: def __init__(self, root): self.root = root self.root.title("CPU / RAM Monitor") self.tracker = Tracker() # --- Header card --- card_bg = "#23272f" card_fg = "#ffffff" accent = "#4fc3f7" header_frame = ttk.Frame(root) header_frame.pack(fill=tk.X, padx=10, pady=(10, 0)) self.cpu_label = ttk.Label( header_frame, text=f"CPU: --%", font=("Segoe UI", 20, "bold"), foreground=accent, ) self.cpu_label.pack(side=tk.LEFT, padx=20) self.ram_label = ttk.Label( header_frame, text=f"RAM: --%", font=("Segoe UI", 20, "bold"), foreground="#81c784", ) self.ram_label.pack(side=tk.LEFT, padx=20) self.net_label = ttk.Label( header_frame, text=f"Net: ↓ --", font=("Segoe UI", 20, "bold"), foreground="#ffb74d", ) self.net_label.pack(side=tk.LEFT, padx=20) self.disk_label = ttk.Label( header_frame, text=f"Disk: --%", font=("Segoe UI", 20, "bold"), foreground="#ba68c8", ) self.disk_label.pack(side=tk.LEFT, padx=20) # --- Plot --- fig = Figure(figsize=(14, 3), facecolor=card_bg) ax_cpu = fig.add_subplot(1, 4, 1, facecolor=card_bg) ax_ram = fig.add_subplot(1, 4, 2, facecolor=card_bg) ax_disk = fig.add_subplot(1, 4, 3, facecolor=card_bg) ax_net = fig.add_subplot(1, 4, 4, facecolor=card_bg) ax_cpu.set_title("CPU %", color=card_fg, fontsize=11) ax_ram.set_title("RAM %", color=card_fg, fontsize=11) ax_disk.set_title("Disk %", color=card_fg, fontsize=11) ax_net.set_title("Réseau (B/s)", color=card_fg, fontsize=11) ax_cpu.set_ylim(0, 100) ax_ram.set_ylim(0, 100) ax_disk.set_ylim(0, 100) ax_net.set_ylim(0, 1) ax_cpu.set_ylabel("%") ax_ram.set_ylabel("%") ax_disk.set_ylabel("%") ax_net.set_ylabel("B/s") ax_cpu.tick_params(colors="#aaa") ax_ram.tick_params(colors="#aaa") ax_net.tick_params(colors="#aaa") for label in ax_cpu.get_yticklabels(): label.set_color("#aaa") for label in ax_ram.get_yticklabels(): label.set_color("#aaa") for label in ax_net.get_yticklabels(): label.set_color("#aaa") for line in ax_cpu.spines.values(): line.set_color("#3a3f4a") for line in ax_ram.spines.values(): line.set_color("#3a3f4a") for line in ax_net.spines.values(): line.set_color("#3a3f4a") self.cpu_line, = ax_cpu.plot( [], [], color="#ef5350", linewidth=1.5, marker="", markerfacecolor="#ef5350" ) self.ram_line, = ax_ram.plot( [], [], color="#66bb6a", linewidth=1.5, marker="", markerfacecolor="#66bb6a" ) self.disk_line, = ax_disk.plot( [], [], color="#ba68c8", linewidth=1.5, marker="", markerfacecolor="#ba68c8" ) self.net_sent_line, = ax_net.plot( [], [], color="#ffb74d", linewidth=1.5, marker="", markerfacecolor="#ffb74d" ) self.net_recv_line, = ax_net.plot( [], [], color="#90caf9", linewidth=1.5, marker="", markerfacecolor="#90caf9" ) ax_cpu.axhline(y=80, color="#ef5350", linestyle="--", alpha=0.4) ax_ram.axhline(y=80, color="#66bb6a", linestyle="--", alpha=0.4) ax_disk.axhline(y=80, color="#ba68c8", linestyle="--", alpha=0.4) ax_cpu.text(0.02, 0.02, "80%", fontsize=8, color="#ef5350", alpha=0.6, transform=ax_cpu.transAxes) self.fig = fig self.ax_cpu = ax_cpu self.ax_ram = ax_ram self.ax_disk = ax_disk self.ax_net = ax_net canvas = FigureCanvasTkAgg(fig, master=root) canvas.draw() canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=10, pady=10) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() # --- Footer status --- status_frame = ttk.Frame(root) status_frame.pack(fill=tk.X, padx=10, pady=(0, 10)) self.status_label = ttk.Label( status_frame, text="Monitoring...", font=("Segoe UI", 9), foreground="#888", ) self.status_label.pack(side=tk.LEFT) stop_btn = ttk.Button( status_frame, text="Stop", command=self._stop, width=10, ) stop_btn.pack(side=tk.RIGHT, padx=(10, 0)) self._running = True self._refresh() def _refresh(self): if not self._running: return self.tracker.update() cpu, ram = self.tracker.get_latest() disk = self.tracker.get_disk() send_rate, recv_rate = self.tracker.get_net_rates() self.cpu_label.config(text=f"CPU: {cpu:5.1f}%") self.ram_label.config(text=f"RAM: {ram:5.1f}%") self.disk_label.config(text=f"Disk: {disk:5.1f}%") mb_sent = send_rate / (1024 * 1024) mb_recv = recv_rate / (1024 * 1024) self.net_label.config(text=f"Net: ↑{mb_sent:.2f} ↓{mb_recv:.2f} MB/s") cpu_arr = list(self.tracker.cpu_data) ram_arr = list(self.tracker.ram_data) disk_arr = list(self.tracker.disk_data) sent_arr = list(self.tracker.net_sent_data) recv_arr = list(self.tracker.net_recv_data) self.cpu_line.set_data(range(len(cpu_arr)), cpu_arr) self.ram_line.set_data(range(len(ram_arr)), ram_arr) self.disk_line.set_data(range(len(disk_arr)), disk_arr) self.net_sent_line.set_data(range(len(sent_arr)), sent_arr) self.net_recv_line.set_data(range(len(recv_arr)), recv_arr) self.ax_cpu.set_xlim(0, MAX_POINTS) self.ax_ram.set_xlim(0, MAX_POINTS) self.ax_disk.set_xlim(0, MAX_POINTS) self.ax_net.set_xlim(0, MAX_POINTS) max_net = max(max(sent_arr) if sent_arr else 0, max(recv_arr) if recv_arr else 0) if max_net > 0: self.ax_net.set_ylim(0, max_net * 1.2) self.fig.canvas.draw_idle() self.root.after(UPDATE_INTERVAL, self._refresh) def _stop(self): self._running = False self.status_label.config(text="Stopped.") def main(): root = tk.Tk() app = App(root) root.mainloop() if __name__ == "__main__": main()