0

I have written a python script that draws a GUI containing Graphs plotting various metrics such as CPU temperature, Ram usage etc. for my Rasberry pi v4 . The source code of the script is outlined below: `

#!/usr/bin/env python3

#Import required modules
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time
from gpiozero import CPUTemperature
import os
import psutil
import sys
    
#Create application GUI
Main_Window=tk.Tk()
Main_Window.title("Monitors v1.0")
window_width = 800
window_height = 400
Main_Window.resizable(False,False)
Main_Window.geometry(f'{window_width}x{window_height}')

#Set up the layout of the graphs .There will be 4 graphs plotting:
#CPU Temperature in Celsius,RAM,CPU and Disk usage usage in % 
fig,(axs)=plt.subplots(2,2,constrained_layout=True,figsize=(8, 4))
canvas=FigureCanvasTkAgg(fig, Main_Window)
canvas.get_tk_widget().place(x=0,y=0)
fig.suptitle('Performance Metrics')
axs[0,0].set_ylabel('CPU Temperature(C)')
axs[0,0].set_xlabel('Time(Sec)')
axs[0,1].set_ylabel('RAM Usage(%)')
axs[0,1].set_xlabel('Time(Sec)')
axs[1,0].set_ylabel('CPU Usage(%)')
axs[1,0].set_xlabel('Time(%)')
axs[1,1].set_ylabel('Disk Usage(%)')
axs[1,1].set_xlabel('Time(%)')

#Initialize data buffers and plots
temp=[]
RAM=[]
CPU=[]
Disk=[]
axs[0,0].plot(temp)
axs[0,1].plot(RAM)
axs[1,0].plot(CPU)
axs[1,1].plot(Disk)

#Run the script for 50 samples
for _ in range(50):
    #Sampling the metrics.
    temp.append(CPUTemperature().temperature)
    RAM.append(psutil.virtual_memory()[2])
    load1, load5, load15 = psutil.getloadavg()
    cpu_usage = (load15/os.cpu_count()) * 100
    CPU.append(cpu_usage)
    Disk.append(psutil.disk_usage('/').percent)
    #Update the Plots every 200 msec
    time.sleep(0.2)
    canvas.draw()
    axs[0,0].clear()
    axs[0,1].clear()
    axs[1,0].clear()
    axs[1,1].clear()
    axs[0,0].set_ylabel('CPU Temperature(C)')
    axs[0,0].set_xlabel('Time(Sec)')
    axs[0,1].set_ylabel('RAM Usage(%)')
    axs[0,1].set_xlabel('Time(Sec)')
    axs[1,0].set_ylabel('CPU Usage(%)')
    axs[1,0].set_xlabel('Time(%)')
    axs[1,1].set_ylabel('Disk Usage(%)')
    axs[1,1].set_xlabel('Time(sec)')
    axs[0,0].plot(temp)
    axs[0,1].plot(RAM)
    axs[1,0].plot(CPU)
    axs[1,1].plot(Disk)

Main_Window.mainloop()
sys.exit()

I have also created a bash command so that I can run the script with a simple command via the terminal. The script runs as supposed to be plotting 50 measurements of the metrics described before in real time but there are two issues I am facing. First, despite clicking the close button(the classic X button on the top right corner) the window wont close until the 50 measurements are done. Then if I close the window after the measurements are done, then the terminal looks busy as if the script is still running. Any ideas why these things happen?

I tried adding a sys.exit() command after mainloop() to make sure the script exits after the main window is closed but it did not help.

  • 1
    Try adding `plt.close()` after `mainloop()` – codester_09 Oct 30 '22 at 13:24
  • 1
    Thank you codester by adding the plt.close command just before the mainloop() solved my second problem. Now after the 50 measurements are carried out the terminal is released after closing the window. – CrazyEce91 Oct 30 '22 at 15:09

1 Answers1

-1

When you enter your for-loop, your program runs all 50 iteration of that code block until it exits and runs code below. This is toxic for different reasons. Overall, your mainloop is interrupted, events such as mouse or key input won't be processed. This includes clicking X, the window simply does not proceed this message.

However, Tkinter provides tools for such operations. You could use after for it: Example:

def my_function():
    #useful instructions

for i in range(50):
    ms = 20*i
    root.after(ms, my_function)

After returns immediately and won't block your application, means still responsive to input. But since you still want to sleep in the block of code, you could use something like tksleep.

Thingamabobs
  • 7,274
  • 5
  • 21
  • 54
  • Thank you Thingamabobs for your input. The tksleep function indeed does let me close the window before the measurements are done but yields the following error: invalid command name "548104227776" while executing "548104227776" ("after" script) The main problem is that after this error the cosole remains busy. – CrazyEce91 Oct 30 '22 at 15:25
  • @CrazyEce91 Yes, there is a problem with tkinter in this case, thanks for making me aware of it. – Thingamabobs Oct 30 '22 at 15:54