From 7c8f76106ce72d54c1fc4c748212b89381d9b20c Mon Sep 17 00:00:00 2001 From: tmzf5455 Date: Sat, 9 May 2026 20:54:12 +0200 Subject: [PATCH] creation du package --- cpu-monitor_1.0_all.deb | Bin 0 -> 3430 bytes cpu_monitor.py | 23 +- cpu_monitor_gui.desktop | 9 + cpu_monitor_gui.py | 131 +++++++- cpu_monitor_gui.sh | 4 + cpu_monitor_native.py | 252 ++++++++++++++++ .../dh_installchangelogs.dch.trimmed | 5 + .../cpu-monitor/installed-by-dh_installdocs | 0 debian/changelog | 5 + debian/control | 13 + debian/cpu-monitor.substvars | 2 + debian/cpu-monitor/DEBIAN/control | 10 + debian/cpu-monitor/DEBIAN/md5sums | 3 + debian/cpu-monitor/usr/bin/cpu-monitor | 283 ++++++++++++++++++ .../share/applications/cpu-monitor.desktop | 9 + .../usr/share/doc/cpu-monitor/changelog.gz | Bin 0 -> 124 bytes debian/debhelper-build-stamp | 1 + debian/files | 2 + debian/rules | 8 + debian/source/format | 1 + 20 files changed, 750 insertions(+), 11 deletions(-) create mode 100644 cpu-monitor_1.0_all.deb create mode 100644 cpu_monitor_gui.desktop mode change 100644 => 100755 cpu_monitor_gui.py create mode 100755 cpu_monitor_gui.sh create mode 100644 cpu_monitor_native.py create mode 100644 debian/.debhelper/generated/cpu-monitor/dh_installchangelogs.dch.trimmed create mode 100644 debian/.debhelper/generated/cpu-monitor/installed-by-dh_installdocs create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/cpu-monitor.substvars create mode 100644 debian/cpu-monitor/DEBIAN/control create mode 100644 debian/cpu-monitor/DEBIAN/md5sums create mode 100755 debian/cpu-monitor/usr/bin/cpu-monitor create mode 100644 debian/cpu-monitor/usr/share/applications/cpu-monitor.desktop create mode 100644 debian/cpu-monitor/usr/share/doc/cpu-monitor/changelog.gz create mode 100644 debian/debhelper-build-stamp create mode 100644 debian/files create mode 100755 debian/rules create mode 100644 debian/source/format diff --git a/cpu-monitor_1.0_all.deb b/cpu-monitor_1.0_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..89307d717b3219c20e0f9e5a010c26c88b492f8f GIT binary patch literal 3430 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 literal 0 HcmV?d00001 diff --git a/cpu_monitor.py b/cpu_monitor.py index 5b5180f..78290ef 100644 --- a/cpu_monitor.py +++ b/cpu_monitor.py @@ -13,14 +13,33 @@ def get_cpu_ram() -> tuple[float, float]: return cpu, ram +def get_disk_usage() -> float: + return psutil.disk_usage('/').percent + + +def get_net_io() -> tuple[int, int]: + counters = psutil.net_io_counters() + return counters.bytes_sent, counters.bytes_recv + + +def get_disk_io() -> tuple[int, int]: + counters = psutil.disk_io_counters() + if counters is None: + return 0, 0 + return counters.read_bytes, counters.write_bytes + + def format_line() -> str: now = datetime.now().strftime("%H:%M:%S") cpu, ram = get_cpu_ram() - return f"{now} | CPU: {cpu:5.1f}% | RAM: {ram:5.1f}%" + disk = get_disk_usage() + d_read, d_write = get_disk_io() + sent, recv = get_net_io() + return f"{now} | CPU: {cpu:5.1f}% | RAM: {ram:5.1f}% | Disk: {disk:5.1f}% | ↑{d_read:,} ↓{d_write:,} B | Net: ↑{sent:,} ↓{recv:,} B" def main() -> None: - print("Monitoring CPU/RAM. Ctrl+C pour arrêter.") + print("Monitoring CPU/RAM/Disk. Ctrl+C pour arrêter.") try: while True: print(format_line()) diff --git a/cpu_monitor_gui.desktop b/cpu_monitor_gui.desktop new file mode 100644 index 0000000..edec41a --- /dev/null +++ b/cpu_monitor_gui.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Exec=/usr/bin/cpu-monitor +Hidden=false +NoDisplay=false +X-GNOME-Autostart-enabled=true +Name=CPU Monitor GUI +Comment=CPU/RAM/Disk/Network monitor +Icon=utilities-system-monitor diff --git a/cpu_monitor_gui.py b/cpu_monitor_gui.py old mode 100644 new mode 100755 index 3b74824..3953d86 --- a/cpu_monitor_gui.py +++ b/cpu_monitor_gui.py @@ -14,37 +14,89 @@ MAX_POINTS = 60 UPDATE_INTERVAL = 1000 # ms -class CPUTracker: +class Tracker: def __init__(self): self.cpu_data = deque(maxlen=MAX_POINTS) self.ram_data = deque(maxlen=MAX_POINTS) - self.time_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.time_data.append(t) + 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 = CPUTracker() + self.tracker = Tracker() # --- Header card --- card_bg = "#23272f" @@ -70,25 +122,56 @@ class App: ) 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=(8, 3), facecolor=card_bg) - ax_cpu = fig.add_subplot(1, 2, 1, facecolor=card_bg) - ax_ram = fig.add_subplot(1, 2, 2, facecolor=card_bg) + 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" @@ -96,14 +179,26 @@ class App: 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() @@ -140,17 +235,35 @@ class App: 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") - ax = self.ax_cpu 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) - ax.set_xlim(0, MAX_POINTS) + 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) diff --git a/cpu_monitor_gui.sh b/cpu_monitor_gui.sh new file mode 100755 index 0000000..4a57bb9 --- /dev/null +++ b/cpu_monitor_gui.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export DISPLAY=:1 +export GDK_BACKEND=wayland,x11 +/home/tmzf5455/projects/continue-lab/.venv/bin/python /home/tmzf5455/projects/continue-lab/cpu_monitor_gui.py diff --git a/cpu_monitor_native.py b/cpu_monitor_native.py new file mode 100644 index 0000000..744ccad --- /dev/null +++ b/cpu_monitor_native.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +# Lancement: python cpu_monitor_native.py +# Intégrable dans GNOME via /etc/xdg/autostart/resource-monitor.desktop + +from collections import deque +import time + +from gi.repository import Gtk, GLib +import psutil +from matplotlib.figure import Figure +from matplotlib.backends.backend_gtk4agg import FigureCanvasGTK4Agg as FigureCanvas + +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 + self.time_data = deque(maxlen=MAX_POINTS) + self.time_data.append(time.time()) + + def _rate(self, curr, last): + delta = curr - last + return max(int(delta / 2.0), 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(self._rate(d_read, self._last_disk_read)) + self.disk_write_data.append(self._rate(d_write, self._last_disk_write)) + 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: + self.net_sent_data.append(self._rate(sent, self._last_net_sent)) + self.net_recv_data.append(self._rate(recv, self._last_net_recv)) + 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_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 MonitorWindow(Gtk.ApplicationWindow): + def __init__(self, app): + super().__init__(application=app, title="Resource Monitor") + self.set_default_size(1100, 360) + self.set_icon_name("org.gnome.SystemMonitor") + + self.tracker = Tracker() + self._running = True + + # Dark palette + self.DARK_BG = "#1e1e2e" + self.DARK_SURFACE = "#313244" + self.DARK_BORDER = "#45475a" + self.TEXT = "#cdd6f4" + self.TEXT_DIM = "#a6adc8" + self.CPU_COLOR = "#f38ba8" + self.RAM_COLOR = "#a6e3a1" + self.DISK_COLOR = "#cba6f7" + self.NET_UP_COLOR = "#fab387" + self.NET_DOWN_COLOR = "#89b4fa" + self.ALPHA_40 = 0.4 + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + self.add(box) + + # --- Top bar with values --- + header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + header.set_css_classes(["sourcing-frame"]) + header.get_style_context().add_class("card") + header.add_to_css_styleclass("monitor-header") + + self.cpu_label = Gtk.Label() + self.cpu_label.set_css_classes(["stat-label"]) + header.append(self.cpu_label) + + self.ram_label = Gtk.Label() + self.ram_label.set_css_classes(["stat-label"]) + header.append(self.ram_label) + + self.disk_label = Gtk.Label() + self.disk_label.set_css_classes(["stat-label"]) + header.append(self.disk_label) + + self.net_label = Gtk.Label() + self.net_label.set_css_classes(["stat-label"]) + header.append(self.net_label) + + box.append(header) + + # --- Plots --- + fig = Figure(figsize=(13, 3.2), facecolor=self.DARK_BG) + fig.set_tight_layout({"pad": 8}) + + axes = [] + titles = ["CPU %", "RAM %", "Disk %", "Réseau (B/s)"] + for i, title in enumerate(titles): + ax = fig.add_subplot(1, 4, i + 1) + ax.set_facecolor(self.DARK_BG) + ax.set_title(title, color=self.TEXT, fontsize=11, fontweight="medium") + ax.set_ylim(0, 100) + ax.set_ylabel("%" if i < 3 else "B/s", color=self.TEXT_DIM, fontsize=9) + ax.tick_params(colors=self.TEXT_DIM) + ax.spines["bottom"].set_color(self.DARK_BORDER) + ax.spines["top"].set_color(self.DARK_BORDER) + ax.spines["left"].set_color(self.DARK_BORDER) + ax.spines["right"].set_color(self.DARK_BORDER) + axes.append(ax) + + self.cpu_line, = axes[0].plot([], [], color=self.CPU_COLOR, linewidth=1.5) + self.ram_line, = axes[1].plot([], [], color=self.RAM_COLOR, linewidth=1.5) + self.disk_line, = axes[2].plot([], [], color=self.DISK_COLOR, linewidth=1.5) + self.net_up_line, = axes[3].plot([], [], color=self.NET_UP_COLOR, linewidth=1.5, label="↑") + self.net_down_line, = axes[3].plot([], [], color=self.NET_DOWN_COLOR, linewidth=1.5, label="↓") + + for ax in axes[:3]: + ax.axhline(y=80, color=ax.get_lines()[0].get_color(), linestyle="--", alpha=self.ALPHA_40) + + canvas = FigureCanvas(fig) + canvas.set_css_classes(["plot-canvas"]) + box.append(canvas) + + # --- Footer --- + footer = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0) + footer.set_valign(Gtk.Align.END) + footer.get_style_context().add_class("linked") + + self.status_label = Gtk.Label(label="Monitoring...") + self.status_label.set_css_classes(["dim-label"]) + footer.append(self.status_label) + + self.stop_btn = Gtk.Button(label="Arrêter") + self.stop_btn.set_css_classes(["destructive-action"]) + self.stop_btn.connect("clicked", lambda *_: self._stop()) + footer.append(self.stop_btn) + + box.append(footer) + + self.fig = fig + self.axes = axes + self.canvas = canvas + + GLib.timeout_add(UPDATE_INTERVAL, self._refresh) + + def _update_plot(self): + 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_up_line.set_data(range(len(sent_arr)), sent_arr) + self.net_down_line.set_data(range(len(recv_arr)), recv_arr) + + for i, ax in enumerate(self.axes[:3]): + ax.set_xlim(0, MAX_POINTS) + + max_net = max(max(sent_arr) if sent_arr else 0, max(recv_arr) if recv_arr else 0) + self.axes[3].set_xlim(0, MAX_POINTS) + if max_net > 0: + self.axes[3].set_ylim(0, max_net * 1.2) + + self.fig.canvas.draw() + + def _refresh(self): + if not self._running: + return GLib.SOURCE_REMOVE + + 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.set_label(f"CPU: {cpu:5.1f}%") + self.ram_label.set_label(f"RAM: {ram:5.1f}%") + self.disk_label.set_label(f"Disk: {disk:5.1f}%") + + mb_up = send_rate / (1024 * 1024) + mb_dn = recv_rate / (1024 * 1024) + self.net_label.set_label(f"Net: ↑{mb_up:.2f} ↓{mb_dn:.2f} MB/s") + + self._update_plot() + return GLib.SOURCE_CONTINUE + + def _stop(self, *_): + self._running = False + self.status_label.set_label("Arrêté.") + self.stop_btn.set_sensitive(False) + + +class MonitorApplication(Gtk.Application): + def __init__(self): + super().__init__(application_id="com.local.resource-monitor") + + def do_activate(self): + win = MonitorWindow(self) + win.present() + + +def main(): + app = MonitorApplication() + app.run() + + +if __name__ == "__main__": + main() diff --git a/debian/.debhelper/generated/cpu-monitor/dh_installchangelogs.dch.trimmed b/debian/.debhelper/generated/cpu-monitor/dh_installchangelogs.dch.trimmed new file mode 100644 index 0000000..241e004 --- /dev/null +++ b/debian/.debhelper/generated/cpu-monitor/dh_installchangelogs.dch.trimmed @@ -0,0 +1,5 @@ +cpu-monitor (1.0) stable; urgency=low + + * Initial release + + -- tmzf5455 Sat, 09 May 2026 10:00:00 +0000 diff --git a/debian/.debhelper/generated/cpu-monitor/installed-by-dh_installdocs b/debian/.debhelper/generated/cpu-monitor/installed-by-dh_installdocs new file mode 100644 index 0000000..e69de29 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..241e004 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +cpu-monitor (1.0) stable; urgency=low + + * Initial release + + -- tmzf5455 Sat, 09 May 2026 10:00:00 +0000 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..256ee2c --- /dev/null +++ b/debian/control @@ -0,0 +1,13 @@ +Source: cpu-monitor +Section: python +Priority: optional +Maintainer: tmzf5455 +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.1.4 + +Package: cpu-monitor +Maintainer: tmzf5455 +Architecture: all +Depends: python3, python3-psutil, python3-matplotlib, python3-tk, x11-utils, ${misc:Depends} +Description: CPU Monitor GUI + A Python GUI application for monitoring CPU usage in real-time. diff --git a/debian/cpu-monitor.substvars b/debian/cpu-monitor.substvars new file mode 100644 index 0000000..978fc8b --- /dev/null +++ b/debian/cpu-monitor.substvars @@ -0,0 +1,2 @@ +misc:Depends= +misc:Pre-Depends= diff --git a/debian/cpu-monitor/DEBIAN/control b/debian/cpu-monitor/DEBIAN/control new file mode 100644 index 0000000..76edf1f --- /dev/null +++ b/debian/cpu-monitor/DEBIAN/control @@ -0,0 +1,10 @@ +Package: cpu-monitor +Version: 1.0 +Architecture: all +Maintainer: tmzf5455 +Installed-Size: 20 +Depends: python3, python3-psutil, python3-matplotlib, python3-tk, x11-utils +Section: python +Priority: optional +Description: CPU Monitor GUI + A Python GUI application for monitoring CPU usage in real-time. diff --git a/debian/cpu-monitor/DEBIAN/md5sums b/debian/cpu-monitor/DEBIAN/md5sums new file mode 100644 index 0000000..a4791b9 --- /dev/null +++ b/debian/cpu-monitor/DEBIAN/md5sums @@ -0,0 +1,3 @@ +3bb2a78f10db731e82226d58d83c7d31 usr/bin/cpu-monitor +b40607068b416aad76a36519e068e13a usr/share/applications/cpu-monitor.desktop +417d73b9352097d7166b96829dc5a863 usr/share/doc/cpu-monitor/changelog.gz diff --git a/debian/cpu-monitor/usr/bin/cpu-monitor b/debian/cpu-monitor/usr/bin/cpu-monitor new file mode 100755 index 0000000..3953d86 --- /dev/null +++ b/debian/cpu-monitor/usr/bin/cpu-monitor @@ -0,0 +1,283 @@ +#!/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() diff --git a/debian/cpu-monitor/usr/share/applications/cpu-monitor.desktop b/debian/cpu-monitor/usr/share/applications/cpu-monitor.desktop new file mode 100644 index 0000000..edec41a --- /dev/null +++ b/debian/cpu-monitor/usr/share/applications/cpu-monitor.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Exec=/usr/bin/cpu-monitor +Hidden=false +NoDisplay=false +X-GNOME-Autostart-enabled=true +Name=CPU Monitor GUI +Comment=CPU/RAM/Disk/Network monitor +Icon=utilities-system-monitor diff --git a/debian/cpu-monitor/usr/share/doc/cpu-monitor/changelog.gz b/debian/cpu-monitor/usr/share/doc/cpu-monitor/changelog.gz new file mode 100644 index 0000000000000000000000000000000000000000..da8d4414f0e3d3b888ccee6253f9c289ad6a895f GIT binary patch literal 124 zcmV-?0E7P@iwFP!0000214}L_)y>V%%Ph$+QqVBeGtg8hE=f$vNwroeElN+#ORlud z$uH;PQc%!R@C2&LOw3UzO3g`4EKUUq>gp<#