0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python tkinter:CPU使用率グラフを作る(1)

Last updated at Posted at 2025-01-02

(2025.01.04) コード修正

はじめに

tkinter を使って CPU 使用率グラフを作ってみた、
Mac 標準装備のアクティビティモニタでもいいのだが、欲しい情報を1つの画面に表示したくて今回のものを作った次第である。
こういうものでモニタリングしていると、かなりハードな計算でも4コア程度しか使われておらず、8コア全て使い切るには並列処理などを意識して使っていく必要があることがよくわかる。
環境は以下の通り。

MacBook Pro 14"
Chip Apple M1 Pro (8-core CPU, 14-core GPU)
Memory 16GB
Storage 512GB SSD
macOS Sequoia 15.2
Python 3.13.1
Tcl/Tk 9.0.1

作例

まずは作図事例を示そう。
1枚目の写真はアイドリング時のもの。2枚目の写真は、Python で並列処理をかけて全 CPU を動かした場合のもの。
私が使っている M1 MacBook Pro は 8 コア CPU なので、CPU 稼動率の履歴を示すグラフは8段としている。グラフの上2段が高効率コア、下6段が高性能コア。並びは Mac 標準のアクティビティモニタの表示(CPU history)から類推。これでいいと思う。

IMG_2279.jpeg

IMG_2281.jpeg

概略説明

tkinter の画面全体を2つの部分(Frame)に分けている。
上段 Frame には8個の Canvas を配置、下段 Frame には平均 CPU 稼動率とメモリ使用率を表示するための4個の Label を配置している。

グラフの中には、各 CPU 毎に、コアの番号・最新の CPU 稼動率の数値・CPU 稼動率の履歴グラフを表示している。履歴の横軸は60秒であり1秒ステップで稼動率の値を取得・更新している。

グラフの表示方法については、ネットで検索すると matplotlib で描画しこれを Canvas に貼り付けるものが多くみられるが、ここでは複雑な情報を表示するものでもないので、Canvas に直接描画している。
また、折れ線表示だと視覚的に認識しづらいので、グラフ更新ステップである1秒相当の幅の線を create_line で棒グラフ状に描画している。

グラフの時間ステップでの更新は、x軸として固定の numpy 配列 xx を準備(0から60までの61個の要素)、y軸として CPU 稼動率格納用に61要素の numpy 配列を準備、y軸の数値を1秒毎にずらしながら 0.5*(xx[i]+xx[i+1]) 及び 0.5*(yy[i]+yy[i+1]) の位置に棒線を描画していく。この際、線には line、稼動率数値には sval というタグをつけ、グラフ更新直前に delete('line'), delete('sval') している。

コード

import tkinter as tk
from tkinter import ttk
import numpy as np
import psutil


def update():
    global root,cv,xx,yy,mlb,ww,hh,xmin,xmax,ymin,ymax,cpu,fontn
    kxi,kxf,kyi,kyf=0,ww,0,hh
    lw=int((kxf-kxi)/(xmax-xmin))+1 # line width
    r=psutil.cpu_percent(interval=1, percpu=True)
    work=yy
    yy=np.roll(work,-1,axis=1)
    yy[:,-1]=np.array(r)
    for k in range(0,len(r)):
        cv[k].delete('line')
        cv[k].delete('sval')
        for i in range(0,len(xx)-1):
            xx2=0.5*(xx[i]+xx[i+1])
            yy2=0.5*(yy[k,i]+yy[k,i+1])
            xx1,yy1=xx2,ymin
            x1=kxi+(xx1-xmin)*(kxf-kxi)/(xmax-xmin)
            y1=kyf-(yy1-ymin)*(kyf-kyi)/(ymax-ymin)
            x2=kxi+(xx2-xmin)*(kxf-kxi)/(xmax-xmin)
            y2=kyf-(yy2-ymin)*(kyf-kyi)/(ymax-ymin)
            cv[k].create_line(x1,y1,x2,y2,width=lw,fill='#999999',tag='line')
        s='{0:3s} {1:6.1f}'.format(cpu[k],r[k])
        cv[k].create_text(5,5,text=s,anchor='nw',font=(fontn),fill='#ffff00',tag='sval')
    mlb[1]['text']='{0:6.1f}%'.format(sum(r)/len(r))    
    mlb[3]['text']='{0:6.1f}%'.format(psutil.virtual_memory().percent)
    root.after(1000, update)


def main():
    global root,cv,xx,yy,mlb,ww,hh,xmin,xmax,ymin,ymax,cpu,fontn

   # basin parameter
    nc=psutil.cpu_count() # number pf cores
    cpu=[]
    for i in range(0,nc):
        s='P'  # performance core
        if 0<=i<=1: s='E' # efficiency core
        cpu.append('{0}-{1}'.format(i+1,s))
    ww,hh=200,50 # size of canvas
    tspan=60 # time span of fig
    tstr='CPU (ts={0:.0f}s)'.format(tspan)
    # initialization of plot data
    xmin,xmax,dx=0,tspan,1 # time range 
    ymin,ymax=0,100 # CPU percentage
    xx=np.linspace(xmin,xmax,int((xmax-xmin)/dx)+1)
    yy=np.zeros((nc,len(xx)),dtype=np.float64)

    # GUI
    root =tk.Tk()
    root.resizable(False,False)
    root.title(tstr)
    style=ttk.Style()
    style.theme_use('alt')
    fontn='Calibri 12 bold'
    fontb='Calibri 14 bold'
    style.configure('.',font=(fontn))
    style.configure('.',background='#393939',foreground='#ffffff')

    # arrangement of Canvas
    frame0=ttk.Frame(root)
    cv=np.empty(nc,dtype=object)
    for k in range(0,len(cv)):
        cv[k]=tk.Canvas(root,width=ww,height=hh,bg='#0f0f0f')
        cv[k].pack()
    frame0.pack()

    # arrangement of Lavel
    frame1=ttk.Frame(root)
    nn=4
    mlb=np.empty(nn,dtype=object)
    txt=['CPU','','MEM','']
    for k in range(0,len(mlb)):
        mlb[k]=ttk.Label(frame1,text=txt[k],borderwidth=1,relief=tk.SOLID,anchor=tk.CENTER,width=7,font=(fontb))
        i,j=k//2,k%2
        mlb[k].grid(row=i,column=j)
    frame1.pack()

    update()

    root.mainloop()


if __name__ == '__main__': main()

以 上

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?