v1.2: Remove AppIndicator, simplify

This commit is contained in:
2026-05-10 00:43:28 +02:00
parent b8abe83c98
commit c5ef37f44b
11 changed files with 36 additions and 342 deletions

View File

@@ -3,26 +3,10 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
import subprocess
import tempfile
import os
import time import time
import psutil import psutil
import gi
try:
gi.require_version('AyatanaAppIndicator3', '0.1')
from gi.repository import AyatanaAppIndicator3, GLib, Gtk
HAVE_INDICATOR = True
except (ImportError, ValueError):
HAVE_INDICATOR = False
MAX_POINTS = 60
UPDATE_INTERVAL = 1000 # ms
try: try:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure from matplotlib.figure import Figure
@@ -31,147 +15,28 @@ except ImportError:
HAVE_MPL = False HAVE_MPL = False
MAX_POINTS = 60
UPDATE_INTERVAL = 1000 # ms
class Tracker: class Tracker:
def __init__(self): def __init__(self):
self.cpu_data = [] self.cpu_data = []
self.ram_data = [] self.ram_data = []
self.disk_data = []
self._last_cpu = None self._last_cpu = None
now = time.time()
self.time_data = [now]
def _net_rate(self, curr, last, interval):
delta = curr - last
rate = max(delta / interval, 0)
return max(int(rate), 0)
def update(self, dt):
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)
net = psutil.net_io_counters()
sent = net.bytes_sent
recv = net.bytes_recv
self._last_net_sent = sent
self._last_net_recv = recv
def get_latest(self):
if self._last_cpu is None:
self.update(1)
return self._last_cpu, self.ram_data[-1] if self.ram_data else 0
def get_disk(self):
return self.disk_data[-1] if self.disk_data else 0
def get_net_rates(self):
return 0, 0
def cpu_percent(self): def cpu_percent(self):
return psutil.cpu_percent(interval=None) return psutil.cpu_percent(interval=None)
class SysTrayIndicator:
def __init__(self, monitor):
if not HAVE_INDICATOR:
return
self.indicator = AyatanaAppIndicator3.Indicator.new(
"cpu-monitor",
"cpu-monitor",
AyatanaAppIndicator3.IndicatorCategory.SYSTEM_SERVICES
)
self.indicator.set_menu(self._create_menu())
self.monitor = monitor
self._update_icon()
try:
import cairo
except ImportError:
return
def _create_menu(self):
menu = Gtk.Menu()
item = Gtk.MenuItem(label=f"CPU: {psutil.cpu_percent(interval=None)}%")
item.show()
menu.append(item)
item2 = Gtk.MenuItem(label=f"RAM: {psutil.virtual_memory().percent}%")
item2.show()
menu.append(item2)
sep = Gtk.SeparatorMenuItem()
sep.show()
menu.append(sep)
open_item = Gtk.MenuItem(label="Open Monitor")
open_item.connect("activate", lambda w: subprocess.Popen(["cpu-monitor"]))
open_item.show()
menu.append(open_item)
close_item = Gtk.MenuItem(label="Quit")
close_item.connect("activate", lambda w: Gtk.main_quit())
close_item.show()
menu.append(close_item)
return menu
def _update_icon(self):
if not HAVE_INDICATOR:
return
try:
import cairo
except ImportError:
return
cpu = psutil.cpu_percent()
if cpu > 80:
color = (0.93, 0.33, 0.31)
elif cpu > 50:
color = (1.0, 0.55, 0.0)
else:
color = (0.40, 0.76, 0.97)
size = 24
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size)
cr = cairo.Context(surface)
cr.set_source_rgba(*color, 1.0)
cr.paint()
tmp = tempfile.NamedTemporaryFile(suffix='.png', delete=False, dir='/tmp')
tmp.close()
surface.write_to_png(tmp.name)
try:
self.indicator.set_icon_full(tmp.name, "CPU Monitor")
except Exception:
pass
GLib.timeout_add_seconds(2, self._update_icon)
def create_tray_icon(monitor):
if HAVE_INDICATOR:
return SysTrayIndicator(monitor)
return None
class App: class App:
cpu_data = [] cpu_data = []
ram_data = [] ram_data = []
_last_cpu = None
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
self.root.title("CPU / RAM Monitor") self.root.title("CPU / RAM Monitor")
self.root.geometry("1000x200")
self.tracker = Tracker() self.tracker = Tracker()
@@ -199,14 +64,6 @@ class App:
) )
self.ram_label.pack(side=tk.LEFT, padx=20) 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( self.disk_label = ttk.Label(
header_frame, header_frame,
text=f"Disk: --%", text=f"Disk: --%",
@@ -299,10 +156,10 @@ class App:
self.cpu_data.append(cpu) self.cpu_data.append(cpu)
self.ram_data.append(ram) self.ram_data.append(ram)
if len(self.cpu_data) > MAX_POINTS: if len(App.cpu_data) > MAX_POINTS:
self.cpu_data = self.cpu_data[-MAX_POINTS:] App.cpu_data = App.cpu_data[-MAX_POINTS:]
if len(self.ram_data) > MAX_POINTS: if len(App.ram_data) > MAX_POINTS:
self.ram_data = self.ram_data[-MAX_POINTS:] App.ram_data = App.ram_data[-MAX_POINTS:]
self.cpu_label.config(text=f"CPU: {cpu:5.1f}%") self.cpu_label.config(text=f"CPU: {cpu:5.1f}%")
self.ram_label.config(text=f"RAM: {ram:5.1f}%") self.ram_label.config(text=f"RAM: {ram:5.1f}%")
@@ -313,8 +170,8 @@ class App:
ax_ram = self.fig.axes[1] ax_ram = self.fig.axes[1]
ax_disk = self.fig.axes[2] ax_disk = self.fig.axes[2]
self.cpu_line.set_data(range(len(self.cpu_data)), self.cpu_data) self.cpu_line.set_data(range(len(App.cpu_data)), App.cpu_data)
self.ram_line.set_data(range(len(self.ram_data)), self.ram_data) self.ram_line.set_data(range(len(App.ram_data)), App.ram_data)
ax_cpu.set_xlim(0, MAX_POINTS) ax_cpu.set_xlim(0, MAX_POINTS)
ax_ram.set_xlim(0, MAX_POINTS) ax_ram.set_xlim(0, MAX_POINTS)
@@ -328,19 +185,11 @@ class App:
self._running = False self._running = False
self.status_label.config(text="Stopped.") self.status_label.config(text="Stopped.")
self.root.destroy() self.root.destroy()
if HAVE_INDICATOR:
GLib.idle_add(Gtk.main_quit)
def main(): def main():
root = tk.Tk() root = tk.Tk()
app = App(root) app = App(root)
_tray = None
if HAVE_INDICATOR:
_tray = SysTrayIndicator(app)
root.bind('<Destroy>', lambda e: GLib.idle_add(Gtk.main_quit))
root.mainloop() root.mainloop()

View File

@@ -1,7 +1,6 @@
cpu-monitor (1.1) noble; urgency=low cpu-monitor (1.2) noble; urgency=low
* v2: Add GNOME AppIndicator systray icon in system tray bar * Removed AppIndicator (Wayland incompatible)
* v2: Autostart .desktop in /etc/xdg/autostart * Simpler, more reliable autostart
* v2: System tray icon with CPU/RAM status menu
-- tmzf5455 <tmzf5455> Sun, 10 May 2026 00:00:00 +0000 -- tmzf5455 <tmzf5455> Sun, 10 May 2026 00:00:00 +0000

7
debian/changelog vendored
View File

@@ -1,7 +1,6 @@
cpu-monitor (1.1) noble; urgency=low cpu-monitor (1.2) noble; urgency=low
* v2: Add GNOME AppIndicator systray icon in system tray bar * Removed AppIndicator (Wayland incompatible)
* v2: Autostart .desktop in /etc/xdg/autostart * Simpler, more reliable autostart
* v2: System tray icon with CPU/RAM status menu
-- tmzf5455 <tmzf5455> Sun, 10 May 2026 00:00:00 +0000 -- tmzf5455 <tmzf5455> Sun, 10 May 2026 00:00:00 +0000

2
debian/control vendored
View File

@@ -8,7 +8,7 @@ Rules-Requires-Root: no
Package: cpu-monitor Package: cpu-monitor
Architecture: all Architecture: all
Depends: python3, python3-psutil, python3-matplotlib, python3-tk, gir1.2-ayatanaappindicator3-0.1, ${misc:Depends} Depends: python3, python3-psutil, python3-matplotlib, python3-tk, ${misc:Depends}
Description: CPU/RAM/Disk/Network monitor GUI Description: CPU/RAM/Disk/Network monitor GUI
A system monitor tool with real-time matplotlib charts A system monitor tool with real-time matplotlib charts
and a tkinter GUI, showing CPU, RAM, disk and network usage. and a tkinter GUI, showing CPU, RAM, disk and network usage.

View File

@@ -1,9 +1,9 @@
Package: cpu-monitor Package: cpu-monitor
Version: 1.1 Version: 1.2
Architecture: all Architecture: all
Maintainer: tmzf5455 Maintainer: tmzf5455
Installed-Size: 32 Installed-Size: 19
Depends: python3, python3-psutil, python3-matplotlib, python3-tk, gir1.2-ayatanaappindicator3-0.1 Depends: python3, python3-psutil, python3-matplotlib, python3-tk
Section: utils Section: utils
Priority: optional Priority: optional
Description: CPU/RAM/Disk/Network monitor GUI Description: CPU/RAM/Disk/Network monitor GUI

View File

@@ -1,3 +1,2 @@
f0e9f6392ef8c8b1447d39da0622a0d7 usr/bin/cpu-monitor f2f2755049314fcc90af2529be7731e4 usr/bin/cpu-monitor
c8cc9827601e0cc0b156979c4caa4980 usr/share/doc/cpu-monitor/changelog.gz 34f5fe29e2343c3c795082bbcb93bd0c usr/share/doc/cpu-monitor/changelog.gz
fd11337fda885b79dc49d83a15abcffd usr/share/icons/hicolor/48x48/apps/cpu-monitor.png

View File

@@ -3,26 +3,10 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
import subprocess
import tempfile
import os
import time import time
import psutil import psutil
import gi
try:
gi.require_version('AyatanaAppIndicator3', '0.1')
from gi.repository import AyatanaAppIndicator3, GLib, Gtk
HAVE_INDICATOR = True
except (ImportError, ValueError):
HAVE_INDICATOR = False
MAX_POINTS = 60
UPDATE_INTERVAL = 1000 # ms
try: try:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure from matplotlib.figure import Figure
@@ -31,147 +15,28 @@ except ImportError:
HAVE_MPL = False HAVE_MPL = False
MAX_POINTS = 60
UPDATE_INTERVAL = 1000 # ms
class Tracker: class Tracker:
def __init__(self): def __init__(self):
self.cpu_data = [] self.cpu_data = []
self.ram_data = [] self.ram_data = []
self.disk_data = []
self._last_cpu = None self._last_cpu = None
now = time.time()
self.time_data = [now]
def _net_rate(self, curr, last, interval):
delta = curr - last
rate = max(delta / interval, 0)
return max(int(rate), 0)
def update(self, dt):
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)
net = psutil.net_io_counters()
sent = net.bytes_sent
recv = net.bytes_recv
self._last_net_sent = sent
self._last_net_recv = recv
def get_latest(self):
if self._last_cpu is None:
self.update(1)
return self._last_cpu, self.ram_data[-1] if self.ram_data else 0
def get_disk(self):
return self.disk_data[-1] if self.disk_data else 0
def get_net_rates(self):
return 0, 0
def cpu_percent(self): def cpu_percent(self):
return psutil.cpu_percent(interval=None) return psutil.cpu_percent(interval=None)
class SysTrayIndicator:
def __init__(self, monitor):
if not HAVE_INDICATOR:
return
self.indicator = AyatanaAppIndicator3.Indicator.new(
"cpu-monitor",
"cpu-monitor",
AyatanaAppIndicator3.IndicatorCategory.SYSTEM_SERVICES
)
self.indicator.set_menu(self._create_menu())
self.monitor = monitor
self._update_icon()
try:
import cairo
except ImportError:
return
def _create_menu(self):
menu = Gtk.Menu()
item = Gtk.MenuItem(label=f"CPU: {psutil.cpu_percent(interval=None)}%")
item.show()
menu.append(item)
item2 = Gtk.MenuItem(label=f"RAM: {psutil.virtual_memory().percent}%")
item2.show()
menu.append(item2)
sep = Gtk.SeparatorMenuItem()
sep.show()
menu.append(sep)
open_item = Gtk.MenuItem(label="Open Monitor")
open_item.connect("activate", lambda w: subprocess.Popen(["cpu-monitor"]))
open_item.show()
menu.append(open_item)
close_item = Gtk.MenuItem(label="Quit")
close_item.connect("activate", lambda w: Gtk.main_quit())
close_item.show()
menu.append(close_item)
return menu
def _update_icon(self):
if not HAVE_INDICATOR:
return
try:
import cairo
except ImportError:
return
cpu = psutil.cpu_percent()
if cpu > 80:
color = (0.93, 0.33, 0.31)
elif cpu > 50:
color = (1.0, 0.55, 0.0)
else:
color = (0.40, 0.76, 0.97)
size = 24
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, size, size)
cr = cairo.Context(surface)
cr.set_source_rgba(*color, 1.0)
cr.paint()
tmp = tempfile.NamedTemporaryFile(suffix='.png', delete=False, dir='/tmp')
tmp.close()
surface.write_to_png(tmp.name)
try:
self.indicator.set_icon_full(tmp.name, "CPU Monitor")
except Exception:
pass
GLib.timeout_add_seconds(2, self._update_icon)
def create_tray_icon(monitor):
if HAVE_INDICATOR:
return SysTrayIndicator(monitor)
return None
class App: class App:
cpu_data = [] cpu_data = []
ram_data = [] ram_data = []
_last_cpu = None
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
self.root.title("CPU / RAM Monitor") self.root.title("CPU / RAM Monitor")
self.root.geometry("1000x200")
self.tracker = Tracker() self.tracker = Tracker()
@@ -199,14 +64,6 @@ class App:
) )
self.ram_label.pack(side=tk.LEFT, padx=20) 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( self.disk_label = ttk.Label(
header_frame, header_frame,
text=f"Disk: --%", text=f"Disk: --%",
@@ -299,10 +156,10 @@ class App:
self.cpu_data.append(cpu) self.cpu_data.append(cpu)
self.ram_data.append(ram) self.ram_data.append(ram)
if len(self.cpu_data) > MAX_POINTS: if len(App.cpu_data) > MAX_POINTS:
self.cpu_data = self.cpu_data[-MAX_POINTS:] App.cpu_data = App.cpu_data[-MAX_POINTS:]
if len(self.ram_data) > MAX_POINTS: if len(App.ram_data) > MAX_POINTS:
self.ram_data = self.ram_data[-MAX_POINTS:] App.ram_data = App.ram_data[-MAX_POINTS:]
self.cpu_label.config(text=f"CPU: {cpu:5.1f}%") self.cpu_label.config(text=f"CPU: {cpu:5.1f}%")
self.ram_label.config(text=f"RAM: {ram:5.1f}%") self.ram_label.config(text=f"RAM: {ram:5.1f}%")
@@ -313,8 +170,8 @@ class App:
ax_ram = self.fig.axes[1] ax_ram = self.fig.axes[1]
ax_disk = self.fig.axes[2] ax_disk = self.fig.axes[2]
self.cpu_line.set_data(range(len(self.cpu_data)), self.cpu_data) self.cpu_line.set_data(range(len(App.cpu_data)), App.cpu_data)
self.ram_line.set_data(range(len(self.ram_data)), self.ram_data) self.ram_line.set_data(range(len(App.ram_data)), App.ram_data)
ax_cpu.set_xlim(0, MAX_POINTS) ax_cpu.set_xlim(0, MAX_POINTS)
ax_ram.set_xlim(0, MAX_POINTS) ax_ram.set_xlim(0, MAX_POINTS)
@@ -328,19 +185,11 @@ class App:
self._running = False self._running = False
self.status_label.config(text="Stopped.") self.status_label.config(text="Stopped.")
self.root.destroy() self.root.destroy()
if HAVE_INDICATOR:
GLib.idle_add(Gtk.main_quit)
def main(): def main():
root = tk.Tk() root = tk.Tk()
app = App(root) app = App(root)
_tray = None
if HAVE_INDICATOR:
_tray = SysTrayIndicator(app)
root.bind('<Destroy>', lambda e: GLib.idle_add(Gtk.main_quit))
root.mainloop() root.mainloop()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

4
debian/files vendored
View File

@@ -1,2 +1,2 @@
cpu-monitor_1.1_all.deb utils optional cpu-monitor_1.2_all.deb utils optional
cpu-monitor_1.1_arm64.buildinfo utils optional cpu-monitor_1.2_arm64.buildinfo utils optional

1
debian/rules vendored
View File

@@ -4,4 +4,3 @@
override_dh_auto_install: override_dh_auto_install:
install -D -m 0755 cpu_monitor_gui.py $(CURDIR)/debian/cpu-monitor/usr/bin/cpu-monitor install -D -m 0755 cpu_monitor_gui.py $(CURDIR)/debian/cpu-monitor/usr/bin/cpu-monitor
install -D -m 0644 cpu_monitor_gui.desktop $(CURDIR)/debian/cpu-monitor/etc/xdg/autostart/cpu-monitor.desktop install -D -m 0644 cpu_monitor_gui.desktop $(CURDIR)/debian/cpu-monitor/etc/xdg/autostart/cpu-monitor.desktop
install -D -m 0644 cpu-monitor.png $(CURDIR)/debian/cpu-monitor/usr/share/icons/hicolor/48x48/apps/cpu-monitor.png