首页 > 解决方案 > 如何在 Tkinter 窗口中绘制 Matplotlib 图?

问题描述

我需要你的一些想法来尝试自己实施。我有一个Tk()带有 2 个选项卡的主窗口,但我现在将专注于一个(group_stage_frame):

root = tk.Tk()

my_notebook = ttk.Notebook(root)
my_notebook.pack()

group_stage_frame = Frame(my_notebook, width=1080, height=1080)
knockout_stage_frame = Frame(my_notebook, width=1080, height=1080)

group_stage_frame.pack(fill="both", expand=1)
knockout_stage_frame.pack(fill="both", expand=1)

my_notebook.add(group_stage_frame, text="GROUP STAGE")
my_notebook.add(knockout_stage_frame, text="KNOCKOUT STAGE")

group_A_label = Label(group_stage_frame, text="GROUP A").grid(row=0, column=0)
buttonA1 = Button(group_stage_frame, text="Russia 5-0 Saudi Arabia", width=25, command=A1window).grid(row=1, column=0)

root.mainloop()

按钮A1功能:

def A1window():
    def showPlayersA1():
        heat_pass_map(df, clickedRussia.get())
        heat_pass_map(df, clickedArabia.get())
    
    rootA1 = tk.Toplevel()
    rootA1.title("Russia 5-0 Saudi Arabia")
    
    #events
    with open(r'C:\Users\Catalin\Desktop\WorldCupData\data\events\7525.json', encoding = 'utf-8') as f:
        data = json.load(f)
    df = pd.json_normalize(data, sep = "_")
    
    #lineup
    with open(r'C:\Users\Catalin\Desktop\WorldCupData\data\lineups\7525.json', encoding = 'utf-8') as f:
        lineup = json.load(f)
        
    #Dropdown menu Russia lineup
    clickedRussia = tk.StringVar()
    clickedRussia.set("Choose any Russian player")

    options_list_Russia = []
    for i in range(14):
        options_list_Russia.append(lineup[0]['lineup'][i]['player_name'])
    
    dropRussia = tk.OptionMenu(rootA1, clickedRussia, *options_list_Russia)
    dropRussia.config(width=50)
    dropRussia.pack(side = "top", anchor = CENTER)
    
    #Dropdown menu Saudi Arabia lineup
    clickedArabia = tk.StringVar()
    clickedArabia.set("Choose any Saudi Arabia player")
    
    options_list_Arabia = []
    for i in range(14):
        options_list_Arabia.append(lineup[1]['lineup'][i]['player_name'])

    dropArabia = tk.OptionMenu(rootA1, clickedArabia, *options_list_Arabia)
    dropArabia.config(width=50)
    dropArabia.pack(side = "top", anchor = CENTER)
    
    buttonCompare = tk.Button(rootA1, text="COMPARE", command=showPlayersA1).pack(side = "top", anchor = CENTER)

我正在为每个国家/地区的选定玩家绘制热图和传球地图以比较图表:

def draw_pitch(ax):
    # size of the pitch is 120, 80
    #Create figure

    #Pitch Outline & Centre Line
    plt.plot([0,0],[0,80], color="black")
    plt.plot([0,120],[80,80], color="black")
    plt.plot([120,120],[80,0], color="black")
    plt.plot([120,0],[0,0], color="black")
    plt.plot([60,60],[0,80], color="black")

    #Left Penalty Area
    plt.plot([14.6,14.6],[57.8,22.2],color="black")
    plt.plot([0,14.6],[57.8,57.8],color="black")
    plt.plot([0,14.6],[22.2,22.2],color="black")

    #Right Penalty Area
    plt.plot([120,105.4],[57.8,57.8],color="black")
    plt.plot([105.4,105.4],[57.8,22.5],color="black")
    plt.plot([120, 105.4],[22.5,22.5],color="black")

    #Left 6-yard Box
    plt.plot([0,4.9],[48,48],color="black")
    plt.plot([4.9,4.9],[48,32],color="black")
    plt.plot([0,4.9],[32,32],color="black")

    #Right 6-yard Box
    plt.plot([120,115.1],[48,48],color="black")
    plt.plot([115.1,115.1],[48,32],color="black")
    plt.plot([120,115.1],[32,32],color="black")

    #Prepare Circles
    centreCircle = plt.Circle((60,40),8.1,color="black",fill=False)
    centreSpot = plt.Circle((60,40),0.71,color="black")
    leftPenSpot = plt.Circle((9.7,40),0.71,color="black")
    rightPenSpot = plt.Circle((110.3,40),0.71,color="black")

    #Draw Circles
    ax.add_patch(centreCircle)
    ax.add_patch(centreSpot)
    ax.add_patch(leftPenSpot)
    ax.add_patch(rightPenSpot)

    #Prepare Arcs
    # arguments for arc
    # x, y coordinate of centerpoint of arc
    # width, height as arc might not be circle, but oval
    # angle: degree of rotation of the shape, anti-clockwise
    # theta1, theta2, start and end location of arc in degree
    leftArc = Arc((9.7,40),height=16.2,width=16.2,angle=0,theta1=310,theta2=50,color="black")
    rightArc = Arc((110.3,40),height=16.2,width=16.2,angle=0,theta1=130,theta2=230,color="black")

    #Draw Arcs
    ax.add_patch(leftArc)
    ax.add_patch(rightArc)

def heat_pass_map(data, player_name):
    pass_data = data[(data['type_name'] == "Pass") & (data['player_name'] == player_name)]
    action_data = data[(data['player_name']==player_name) & (data['type_name'] != 'Substitution')]
    
    fig=plt.figure()
    fig.set_size_inches(7, 5)
    ax=fig.add_subplot(1,1,1)
    draw_pitch(ax)
    plt.axis('off')

    #passes
    for i in range(len(pass_data)):
        # we also differentiate different half by different color
        color = "blue" if pass_data.iloc[i]['period'] == 1 else "red"
        ax.annotate("", xy = (pass_data.iloc[i]['pass_end_location'][0], pass_data.iloc[i]['pass_end_location'][1]), xycoords = 'data', xytext = (pass_data.iloc[i]['location'][0], pass_data.iloc[i]['location'][1]), textcoords = 'data', arrowprops=dict(arrowstyle="->",connectionstyle="arc3", color = color))
      
    #heatmap
    x_coord = [i[0] for i in action_data["location"]]
    y_coord = [i[1] for i in action_data["location"]]
    sns.kdeplot(x = x_coord, y = y_coord, shade = "True", thresh = 0, cmap = "Greens", n_levels = 10)
    
    #create arrow legend
    blue_arrow = mlines.Line2D([], [], c='blue', marker=r'$\rightarrow$', markersize=15, linestyle='None', label='First half pass')
    red_arrow = mlines.Line2D([], [], c='red', marker=r'$\rightarrow$', markersize=15, linestyle='None', label='Second half pass')
    
    plt.ylim(0, 80) # need this, otherwise kde plot will go outside
    plt.xlim(0, 120)
    plt.legend(handles=[blue_arrow, red_arrow], loc='upper right')
    plt.title(str(player_name)+ "'s pass map")
    plt.show()

所以,基本上,如果我能以某种方式将 2 个图绘制到我的 rootA1 窗口中,我想要一些想法。我阅读了有关画布小部件的信息,但我认为这并没有太大帮助,因为 rootA1(当我单击主机上的按钮时弹出的第二个框架)位于 A1window 函数中。

我想到的另一个想法是在 main 函数中创建 rootA1 并将其隐藏,直到我按下按钮。

想请教各位大佬的意见,谢谢!

标签: pythonpython-3.xmatplotlibtkintertkinter-canvas

解决方案


推荐阅读