【问题标题】:How do I create a live graph with cpu data in Tkinter?如何在 Tkinter 中使用 cpu 数据创建实时图表?
【发布时间】:2020-07-29 11:20:13
【问题描述】:

我正在尝试将 psutil.cpu_percent() 绘制为 Tkinter 中的实时图表,但我无法让它工作。 我的主要问题是让 cpu_percent 有一个“历史”,而不仅仅是每秒绘制一个点。

另外,我不希望使用 cpu_percent() 结果创建一个不断扩展的数据框,因为如果不断运行我的脚本,这可能是一个问题。 - 所以我正在寻找可能创建某种正在运行的数据帧循环来清除最旧的条目,或者类似的东西。

我对哪种解决方案最好并不挑剔,只要 tkinter 窗口以一定的固定间隔显示带有 cpu 信息的实时运行图。

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import Toplevel
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showinfo, showwarning, askquestion
from tkinter import OptionMenu
from tkinter import StringVar

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib import style
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from psutil import cpu_percent

from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
import sklearn.cluster as cluster
import scipy.spatial.distance as sdist
from sklearn.ensemble import IsolationForest

import pandas as pd
import numpy as np
import seaborn as sn
from datetime import datetime

from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler

RANDOM_STATE = 42 #used to help randomly select the data points
low_memory=False
LARGE_FONT= ("Verdana", 12)
style.use("ggplot")

f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
        
LARGE_FONT= ("Verdana", 12)
style.use("ggplot")

f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)


def animate(i):

    cpu_measure = cpu_percent()             
    
    dateTimeObj = datetime.now()
    cpu_time = dateTimeObj.strftime("%H:%M:%S")

    a.clear()

    a.plot_date(cpu_time, cpu_measure, label='CPU Usage')

    a.legend(bbox_to_anchor=(0, 1.02, 1, .102), loc=3,
             ncol=2, borderaxespad=0)

    title = "Graph"
    a.set_title(title)


class Analyticsapp(tk.Tk):

    def __init__(self, *args, **kwargs):
        
        tk.Tk.__init__(self, *args, **kwargs)
        
        #tk.Tk.iconbitmap(self, default="iconimage_kmeans.ico") #Icon for program
        tk.Tk.wm_title(self, "Advanched analytics")
        
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        
        self.frames = {} 
        
        for F in (StartPage, GraphPage):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()
        
class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text=
                         "Advanched analytics", font=LARGE_FONT)
        label.pack(pady=10, padx=10)
        
        button3 = ttk.Button(self, text="Live Plot", 
                            command=lambda: controller.show_frame(GraphPage))
        button3.pack(fill='x')


class GraphPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Example of Live Plotting", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        canvas = FigureCanvasTkAgg(f, self)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2Tk(canvas, self)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        
        button1 = ttk.Button(self, text="Back",
                           command=lambda: controller.show_frame(StartPage))
        button1.pack()

        
app = Analyticsapp()
app.geometry('500x400')
ani = animation.FuncAnimation(f, animate, interval=1000)
app.mainloop()

【问题讨论】:

  • 能否将导入语句添加到您的代码中?
  • 现在添加了完整的可用代码。感谢您的建议。不知道为什么我没有从一开始就这样做。

标签: python function class matplotlib tkinter


【解决方案1】:

要创建实时图表,您可以为 x 和 y 数据创建列表:x_datay_data。每一秒,您都会在列表末尾添加一个新点并删除第一个点,这样您就始终拥有相同的间隔:

x_data.append(new_x)
y_data.append(new_y)
x_data = x_data[1:]
Y_data = y_data[1:]

通过这种方法,使用 matplotlib 的 line 对象的 set_xdata()set_ydata() 更新图形非常简单。因此,如果您使用

f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
plot = a.plot(x_data, y_data)[0]  # get the line object

您可以使用更新它

plot.set_xdata(x_data)
plot.set_ydata(y_data)
canvas.draw_idle()  # update display

这是一个完整的例子:

import tkinter as tk
from psutil import cpu_percent
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from datetime import datetime, timedelta
import matplotlib.dates as mdates


class GraphPage(tk.Frame):

    def __init__(self, parent, nb_points):  
        # nb_points: number of points for the graph
        tk.Frame.__init__(self, parent)
        # matplotlib figure
        self.figure = Figure(figsize=(5, 5), dpi=100)
        self.ax = self.figure.add_subplot(111)
        # format the x-axis to show the time
        myFmt = mdates.DateFormatter("%H:%M:%S")
        self.ax.xaxis.set_major_formatter(myFmt)

        # initial x and y data
        dateTimeObj = datetime.now() + timedelta(seconds=-nb_points)
        self.x_data = [dateTimeObj + timedelta(seconds=i) for i in range(nb_points)]
        self.y_data = [0 for i in range(nb_points)]
        # create the plot
        self.plot = self.ax.plot(self.x_data, self.y_data, label='CPU')[0]
        self.ax.set_ylim(0, 100)
        self.ax.set_xlim(self.x_data[0], self.x_data[-1])

        label = tk.Label(self, text="Example of Live Plotting")
        label.pack(pady=10, padx=10)
        self.canvas = FigureCanvasTkAgg(self.figure, self)
        self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

    def animate(self):
        # append new data point to the x and y data
        self.x_data.append(datetime.now())
        self.y_data.append(cpu_percent())
        # remove oldest data point
        self.x_data = self.x_data[1:]
        self.y_data = self.y_data[1:]
        #  update plot data
        self.plot.set_xdata(self.x_data)
        self.plot.set_ydata(self.y_data)
        self.ax.set_xlim(self.x_data[0], self.x_data[-1])
        self.canvas.draw_idle()  # redraw plot
        self.after(1000, self.animate)  # repeat after 1s


root = tk.Tk()
graph = GraphPage(root, nb_points=1000)
graph.pack(fill='both', expand=True)
root.geometry('500x400')
graph.animate()  # launch the animation
root.mainloop()

在上面的例子中,我决定 matplotlib 图形和轴是GraphPage 类的属性,animate 是一个类方法。另外,由于我对tkintermatplotlib.animate 更熟悉,所以我使用.after(<delay>, <function>) 方法来安排图表的刷新。

编辑:要将其合并到代码的主要结构中,您需要将 controller 参数添加到 GraphPage init 方法,创建“返回”按钮并启动动画创建页面。这是代码(我删除了此示例不需要的导入):

import tkinter as tk
from tkinter import ttk

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib import style
import matplotlib.dates as mdates
from psutil import cpu_percent
from datetime import datetime, timedelta

RANDOM_STATE = 42 #used to help randomly select the data points
low_memory = False
LARGE_FONT = ("Verdana", 12)
style.use("ggplot")

class Analyticsapp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        #tk.Tk.iconbitmap(self, default="iconimage_kmeans.ico") #Icon for program
        tk.Tk.wm_title(self, "Advanched analytics")

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (StartPage, GraphPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Advanched analytics", font=LARGE_FONT)
        label.pack(pady=10, padx=10)

        button3 = ttk.Button(self, text="Live Plot",
                             command=lambda: controller.show_frame(GraphPage))
        button3.pack(fill='x')

class GraphPage(tk.Frame):

    def __init__(self, parent, controller, nb_points=360):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Example of Live Plotting", font=LARGE_FONT)
        label.pack(pady=10, padx=10, side='top')

        # matplotlib figure
        self.figure = Figure(figsize=(5, 5), dpi=100)
        self.ax = self.figure.add_subplot(111)
        # format the x-axis to show the time
        myFmt = mdates.DateFormatter("%H:%M:%S")
        self.ax.xaxis.set_major_formatter(myFmt)
        # initial x and y data
        dateTimeObj = datetime.now() + timedelta(seconds=-nb_points)
        self.x_data = [dateTimeObj + timedelta(seconds=i) for i in range(nb_points)]
        self.y_data = [0 for i in range(nb_points)]
        # create the plot
        self.plot = self.ax.plot(self.x_data, self.y_data, label='CPU')[0]
        self.ax.set_ylim(0, 100)
        self.ax.set_xlim(self.x_data[0], self.x_data[-1])

        self.canvas = FigureCanvasTkAgg(self.figure, self)

        toolbar = NavigationToolbar2Tk(self.canvas, self)
        toolbar.update()

        button1 = ttk.Button(self, text="Back",
                             command=lambda: controller.show_frame(StartPage))
        button1.pack(side='bottom')
        self.canvas.get_tk_widget().pack(side='top', fill=tk.BOTH, expand=True)
        self.animate()

    def animate(self):
        # append new data point to the x and y data
        self.x_data.append(datetime.now())
        self.y_data.append(cpu_percent())
        # remove oldest data point
        self.x_data = self.x_data[1:]
        self.y_data = self.y_data[1:]
        #  update plot data
        self.plot.set_xdata(self.x_data)
        self.plot.set_ydata(self.y_data)
        self.ax.set_xlim(self.x_data[0], self.x_data[-1])
        self.canvas.draw_idle()  # redraw plot
        self.after(1000, self.animate)  # repeat after 1s

app = Analyticsapp()
app.geometry('500x400')
app.mainloop()

【讨论】:

  • 我在尝试您的示例时得到了这个。但我会尝试使用您的示例使其适合我现有的脚本。非常感谢您的贡献! ``` >TypeError Traceback (最近一次调用最后一次) > in > 49 > 50 root = tk.Tk() >---> 51 graph = GraphPage(root, maxpoints =1000) > 52 graph.pack(fill='both', expand=True) > 53 root.geometry('500x400') > >TypeError: __init__() got an unexpected keyword argument 'maxpoints' ` ```跨度>
  • @Snoozium 感谢您的反馈,我在发布之前重命名了一个变量并忘记了一些事件。代码现在应该可以工作了。
  • 我在使用 tkinter 方面有点菜鸟。但我似乎无法让您的脚本在我的总体类分析应用程序中工作,该应用程序具有 GraphPage 所在的“子类”一。我可以单独运行它,但是当与我的脚本结合时,我失败了。
  • @Snoozium 我已经编辑了我的答案以将图表合并到您的页面结构中。如果您自己无法弄清楚,那么您可能应该花一些时间更仔细地查看这个结构以了解页面是如何连接的。
  • 你对我了解更多有关结构如何连接的信息是完全正确的。非常感谢您的帮助 - 您的脚本非常完美!
猜你喜欢
  • 1970-01-01
  • 2015-12-26
  • 2016-08-04
  • 2015-10-22
  • 2014-05-14
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
相关资源
最近更新 更多