2017-08-15 55 views
0

我设计一个Tkinter的GUI将显示对应于一个粒子加速器的模块的过程变量(温度,湿度,风扇速度等)正确显示在Tkinter的多个动画的图形错误。我在同一时间显示多个动画人物时遇到问题。当执行下面的代码,只有两个动画功能的正常调用一个(请注意,不会引发错误):Python3.6:在使用matplotlib

""" 
tkinter GUI for module of particle accelerator 

author: F. Curtis Mason 
email: [email protected] 

Feel free to use or modify this code, but please include the authorship. 
""" 

# tkinter and ttk are both imported. tkinter is the main GUI library that is to 
# be employed in this code. ttk is a module that resides within tkinter, and 
# provides many of the same widgets as tkinter, but with different styles. 
import tkinter as tk 
from tkinter import ttk 

# random is imported for use of randomizers in graphing dummy variables. time is 
# utilized in order to graph process variables against some measure of time. 
import random as rn 
import time as tm 

# matplotlib is the graphing library employed in this code. 
import matplotlib 
import matplotlib.pyplot as plt 
matplotlib.use("TkAgg") 

# These imports allow for the creation of animated (live) graphs via 
# matplotlib, and provide for the proper display of such graphs in tk GUIs. 
import matplotlib.animation as animation 
from matplotlib.figure import Figure 
from matplotlib import style 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, 
NavigationToolbar2TkAgg 
from matplotlib.backend_bases import key_press_handler 

# The Epics library is imported, for obvious reasons. 
# import epics as ep 

# A pre-packaged graphing style is used for matplotlib figures. 
style.use("seaborn-bright") 

# Multiple figures are created; to each, a subplot is assigned. 
fig1 = plt.figure() 
ax11 = fig1.add_subplot(1, 1, 1) 
fig2 = plt.figure() 
ax12 = fig2.add_subplot(1, 1, 1) 

# Names are assigned to the process variables that will be used. Variables 
are grouped according to their appearance 
# on particular tabs of the GUI. 
"""pv0_t = ep.PV("TEM:Pixel:Ch0:T") 
pv0_rh = ep.PV("TEM:Pixel:Ch0:RH") 
pv1_t = ep.PV("TEM:Pixel:Ch1:T") 
pv1_rh = ep.PV("TEM:Pixel:Ch1:RH") 
pv2_t = ep.PV("TEM:Pixel:Ch2:T") 
pv2_rh = ep.PV("TEM:Pixel:Ch2:RH") 
pv3_t = ep.PV("TEM:Pixel:Ch3:T") 
pv3_rh = ep.PV("TEM:Pixel:Ch3:RH") 
pv4_t = ep.PV("TEM:Pixel:Ch4:T") 
pv4_rh = ep.PV("TEM:Pixel:Ch4:RH") 
pv5_t = ep.PV("TEM:Pixel:Ch5:T") 
pv5_rh = ep.PV("TEM:Pixel:Ch5:RH")""" 

# These lists are those upon which process variable values will be appended. 
# If code calls for plotting empty data sets, an error is thrown. To avoid this 
# issue, each list is supplied an appropriate initial value. In case the y-var- 
# iable in question is yet to be configured, a default value of "0" is 
# supplied. 
x_temp_0 = [0] 
y_temp_0 = [0] # change to pv0_t.get() 
x_rh_0 = [0] 
y_rh_0 = [0] # change to pv0_rh.get() 

x_temp_1 = [0] 
y_temp_1 = [0] 
x_rh_1 = [0] 
y_rh_1 = [0] 

x_temp_2 = [0] 
y_temp_2 = [0] 
x_rh_2 = [0] 
y_rh_2 = [0] 

x_temp_3 = [0] 
y_temp_3 = [0] 
x_rh_3 = [0] 
y_rh_3 = [0] 

x_temp_4 = [0] 
y_temp_4 = [0] 
x_rh_4 = [0] 
y_rh_4 = [0] 

x_temp_5 = [0] 
y_temp_5 = [0] 
x_rh_5 = [0] 
y_rh_5 = [0] 

def animate_temp(t): 
    """ Callback function for plotting pixel temperatures. 

    Its single input (i) is the frame number. As of now, x-lists operate as the 
    relative timestamp since initial execution of the code. Each time 
    animate_temp() is called (frequency = 1 Hz), the last value of the x-list 
    in question is incremented by one and appended to that list. Additionally, 
    variables associated with pixels 1-5 will host dummy values until those 
    pixels are configured. New values are appended to appropriate lists, and old 
    values are deleted. It is crucial to clear the plot each time it is desired 
    to plot updated list versions; otherwise, old plots will remain on the figure. 
""" 
    x_temp_0.append(x_temp_0[-1] + 1) 
    y_temp_0.append(rn.randrange(1, 51, 1)) 
    x_temp_1.append(x_temp_1[-1] + 1) 
    y_temp_1.append(rn.randrange(1, 51, 1)) 
    x_temp_2.append(x_temp_2[-1] + 1) 
    y_temp_2.append(rn.randrange(1, 51, 1)) 
    x_temp_3.append(x_temp_3[-1] + 1) 
    y_temp_3.append(rn.randrange(1, 51, 1)) 
    x_temp_4.append(x_temp_4[-1] + 1) 
    y_temp_4.append(rn.randrange(1, 51, 1)) 
    x_temp_5.append(x_temp_5[-1] + 1) 
    y_temp_5.append(rn.randrange(1, 51, 1)) 

    if len(x_temp_0) > 20: 
     del x_temp_0[0] 
     del y_temp_0[0] 
     del x_temp_1[0] 
     del y_temp_1[0] 
     del x_temp_2[0] 
     del y_temp_2[0] 
     del x_temp_3[0] 
     del y_temp_3[0] 
     del x_temp_4[0] 
     del y_temp_4[0] 
     del x_temp_5[0] 
     del y_temp_5[0] 

    ax11.clear() 
    ax11.plot(x_temp_0, y_temp_0) 
    ax11.plot(x_temp_1, y_temp_1) 
    ax11.plot(x_temp_2, y_temp_2) 
    ax11.plot(x_temp_3, y_temp_3) 
    ax11.plot(x_temp_4, y_temp_4) 
    ax11.plot(x_temp_5, y_temp_5) 
    plt.ylim(ymin = 0, ymax = 50) 
    plt.title("TEM:Pixel:Ch[]:T") 
    plt.xlabel("Seconds (s) Since Initiation") 
    plt.ylabel("Temperature (Degrees Celcius)") 
    plt.grid(True) 

def animate_rh(i): 
    """ Callback function for plotting pixel humidity values. 

    This function is the analogue of "animate_temp" for rh values. 
    """ 
    x_rh_0.append(x_rh_0[-1] + 1) 
    y_rh_0.append(rn.randrange(1, 51, 1)) 
    x_rh_1.append(x_rh_1[-1] + 1) 
    y_rh_1.append(rn.randrange(1, 51, 1)) 
    x_rh_2.append(x_rh_2[-1] + 1) 
    y_rh_2.append(rn.randrange(1, 51, 1)) 
    x_rh_3.append(x_rh_3[-1] + 1) 
    y_rh_3.append(rn.randrange(1, 51, 1)) 
    x_rh_4.append(x_rh_4[-1] + 1) 
    y_rh_4.append(rn.randrange(1, 51, 1)) 
    x_rh_5.append(x_rh_5[-1] + 1) 
    y_rh_5.append(rn.randrange(1, 51, 1)) 

    if len(x_rh_0) > 20: 
     del x_rh_0[0] 
     del y_rh_0[0] 
     del x_rh_1[0] 
     del y_rh_1[0] 
     del x_rh_2[0] 
     del y_rh_2[0] 
     del x_rh_3[0] 
     del y_rh_3[0] 
     del x_rh_4[0] 
     del y_rh_4[0] 
     del x_rh_5[0] 
     del y_rh_5[0] 

    ax12.clear() 
    ax12.plot(x_rh_0, y_rh_0) 
    ax12.plot(x_rh_1, y_rh_1) 
    ax12.plot(x_rh_2, y_rh_2) 
    ax12.plot(x_rh_3, y_rh_3) 
    ax12.plot(x_rh_4, y_rh_4) 
    ax12.plot(x_rh_5, y_rh_5) 
    plt.ylim(ymin = 0, ymax = 100) 
    plt.title("TEM:Pixel:Ch[]:RH") 
    plt.xlabel("Seconds (s) Since Initiation") 
    plt.ylabel("Relative Humidity (%)") 
    plt.grid(True) 

class Manager(): 
    """ Creates a tkinter GUI that monitors the module and pixels. 

    This class displays data pertinent to the upkeep of the module 
    and its pixels. Temperatures of various locations of the 
    hardware are (often) graphed, as are relative humidities. 
    Fan speeds, electrical properties, and electrical channel 
    control are also displayed/made accessible. 
    """ 

    def __init__(self): 
     self.root = tk.Tk() 
     self.root.title("Event Manager") 
     self.create_notebook() 

    def create_notebook(self): 

     self.root["padx"] = 5 
     self.root["pady"] = 5 

     notebook = ttk.Notebook(self.root) 
     frame01 = ttk.Frame(notebook) 
     frame02 = ttk.Frame(notebook) 
     notebook.add(frame01, text = "Pixel Health") 
     notebook.add(frame02, text = "Tab 2") 
     notebook.grid(row = 0, column = 0) 

     frame1 = ttk.Frame(frame01) 
     frame1.grid(row = 0, column = 0) 
     canvas11 = FigureCanvasTkAgg(fig1, frame1) 
     canvas11.show() 
     canvas11.get_tk_widget().grid(row = 0, column = 0) 
     canvas12 = FigureCanvasTkAgg(fig2, frame1) 
     canvas12.show() 
     canvas12.get_tk_widget().grid(row = 0, column = 1) 

     quit_button = ttk.Button(self.root, text = "Quit", command = self.root.destroy) 
     quit_button.grid(row = 4, column = 3) 

program = Manager() 
ani_1 = animation.FuncAnimation(fig1, animate_temp, interval = 1000) 
ani_2 = animation.FuncAnimation(fig2, animate_rh, interval = 1000) 
program.root.mainloop() 

但是,我知道,如果我是使每个图的次要情节单个数字并将两个动画回调合并为一个,这两个图都将根据需要同时更新。这通常不是最优的,因为它会降低我在第一个选项卡上组织小部件的总体自由度。也就是说,如果我做两个图同图的次要情节,那么他们必然会都占据了父电网同方(而我想每个小区占据网格的独立广场,这将让用户轻松比如说,只有三个图表会显示在这个页面上,除了其他的小部件)。

请注意,对应史诗库中的部分代码,目前注释掉。这是因为我一直在无法访问Epics或任何关联网络的平台上构建和测试代码。

我为其中码的部分被注释的程度和方式道歉;我被指示发表评论,好像读者对tkinter和python一般是新手一样。此外,如果使用的评论风格不同寻常或者代码难以阅读,我表示歉意。

谢谢你在前进, 柯蒂斯M.

回答

0

删除canvas11.show()canvas12.show()

(如果你告诉一个数字一次,这将是很难事后处理。)

然后您可以修改动画功能实际上在轴上的工作,他们应该。例如。 plt.ylim()将设置当前活动轴,其在这种情况下,要创建的最后一个的限制。而是直接使用轴来操纵其属性:

ax11.set_ylim(ymin = 0, ymax = 50) 
ax11.set_title("TEM:Pixel:Ch[]:T") 
ax11.set_xlabel("Seconds (s) Since Initiation") 
ax11.set_ylabel("Temperature (Degrees Celcius)") 
ax11.grid(True) 

通常,代码中有很多冗余。以下是它的修改版本,同时也展示了如何更有效地进行动画制作。

import matplotlib 
matplotlib.use("TkAgg") 
import matplotlib.pyplot as plt 
import sys 
if sys.version_info >= (3, 0): 
    import tkinter as tk 
    from tkinter import ttk 
else: 
    import Tkinter as tk 
    import ttk 

import random as rn 
import matplotlib.animation as animation 
from matplotlib import style 
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg 

style.use("seaborn-bright") 

fig1, ax11 = plt.subplots() 
ax11.set_ylim(ymin = 0, ymax = 50) 
ax11.set_title("TEM:Pixel:Ch[]:T") 
ax11.set_xlabel("Seconds (s) Since Initiation") 
ax11.set_ylabel("Temperature (Degrees Celcius)") 
ax11.grid(True) 

fig2, ax12 = plt.subplots() 
ax12.set_ylim(ymin = 0, ymax = 100) 
ax12.set_title("TEM:Pixel:Ch[]:RH") 
ax12.set_xlabel("Seconds (s) Since Initiation") 
ax12.set_ylabel("Relative Humidity (%)") 
ax12.grid(True) 

tempx = [[0],[0],[0],[0],[0]] 
tempy = [[0],[0],[0],[0],[0]] 
rhx = [[0],[0],[0],[0],[0]] 
rhy = [[0],[0],[0],[0],[0]] 

lines_temp = [] 
for i in range(5): 
    lines_temp.append(ax11.plot([],[])[0]) 

lines_rh = [] 
for i in range(5): 
    lines_rh.append(ax12.plot([],[])[0]) 


def animate(t, x, y, lines, ax): 
    for i in range(5): 
     x[i].append(x[i][-1] + 1) 
     y[i].append(rn.randrange(1, 51, 1)) 

    if len(x[0]) > 20: 
     for i in range(5): 
      del x[i][0] 
      del y[i][0] 

    for i in range(5): 
     lines[i].set_data(x[i],y[i]) 
     ax.relim() 
     ax.autoscale_view() 


class Manager(): 

    def __init__(self): 
     self.root = tk.Tk() 
     self.root.title("Event Manager") 
     self.create_notebook() 

    def create_notebook(self): 

     self.root["padx"] = 5 
     self.root["pady"] = 5 

     notebook = ttk.Notebook(self.root) 
     frame01 = ttk.Frame(notebook) 
     frame02 = ttk.Frame(notebook) 
     notebook.add(frame01, text = "Pixel Health") 
     notebook.add(frame02, text = "Tab 2") 
     notebook.grid(row = 0, column = 0) 

     frame1 = ttk.Frame(frame01) 
     frame1.grid(row = 0, column = 0) 
     canvas11 = FigureCanvasTkAgg(fig1, frame1) 
     #canvas11.show() 
     canvas11.get_tk_widget().grid(row = 0, column = 0) 
     canvas12 = FigureCanvasTkAgg(fig2, frame1) 
     print canvas12 
     #canvas12.show() 
     canvas12.get_tk_widget().grid(row = 0, column = 1) 

     quit_button = ttk.Button(self.root, text = "Quit", 
           command = self.root.destroy) 
     quit_button.grid(row = 4, column = 3) 

program = Manager() 
ani_1 = animation.FuncAnimation(fig1, animate, interval = 1000, 
           fargs=(tempx,tempy, lines_temp, ax11)) 
ani_2 = animation.FuncAnimation(fig2, animate, interval = 1000, 
           fargs=(rhx,rhy, lines_rh, ax12)) 
program.root.mainloop() 
+0

非常感谢您!这似乎解决了让这两个图形动画的问题!但是,现在出现了一个新问题 - 其中一个图表(显示与温度相关的变量)没有显示其标题或轴标题。这怎么解决?更新后的代码与其原始对象之间的唯一区别是没有canvas11.show()和canvas 12.show()。 –

+0

我想原版在第一个版本中没有标题或标签,对不对?我更新了答案。 – ImportanceOfBeingErnest