Home > database >  Tkinter Button behaviour after matplotlib plt.show()
Tkinter Button behaviour after matplotlib plt.show()

Time:11-29

I am writing a python tkinter-based GUI that should show Matplotlib-Plots in new Windows whenever I hit a button. Plots shall be non-exclusive, I want to be able to bring up as many Plots as I would like. (Original App has more than one button, I shortened it below)

The Problem is: When I click one of my buttons the plot appears correctly. When I close the plot again the behaviour of the used button becomes spooky:

  1. at MacOS it appears pushed on Mouse-over
  2. at Windows it stays pushed for the rest of runtime

On both OS'es it keeps working perfectly fine though. Only the graphics of the button are weird after first usage. I believe it has something to do with the running plt.show() blocking the GUI framework somehow, but I can not nail it down.


class Simulator:
    
    def __init__(self) -> None:
        self.startGUI()

    def startGUI(self):
        self.window = tk.Tk()
        frmCol2 = tk.Frame(pady=10, padx=10)
        self.btnDraw = tk.Button(master = frmCol2, text="Draw Something", width=20)
        self.btnDraw.grid(row = 1, column = 1)
        self.btnDraw.bind("<Button-1>", self.drawSth)
        frmCol2.grid(row=1, column=2, sticky="N")

        self.window.mainloop()

    def drawSth(self, event):
        if self.btnDraw["state"] != "disabled":
            self.visualizer.plotSth(self.scenario)

Plotting itself is then done by the object visualizer of the following class:


class RadarVisualizer:

    def plotClutterVelocities(self, scenario):
        scArray = np.array(scenario)
        
        plt.figure()

        plt.plot(scArray[:,0], scArray[:,1])
        plt.title("Some Title")
        plt.grid()
        plt.show()

I checked the MPL Backend: It is TkAGG. I furthermore tried to put the plotting in a different thread which makes python crying a lot. It seems to expect the plots to be started in the same Thread. Maybe because the backend I am using is also Tkinter based.

CodePudding user response:

An example with many windows without plt.show(). Sample taken from here.

import tkinter as tk
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

i = 0
new_windows = []

data1 = {'country': ['A', 'B', 'C', 'D', 'E'],
         'gdp_per_capita': [45000, 42000, 52000, 49000, 47000]
         }
df1 = pd.DataFrame(data1)

data2 = {'year': [1920, 1930, 1940, 1950, 1960, 1970, 1980, 1990, 2000, 2010],
         'unemployment_rate': [9.8, 12, 8, 7.2, 6.9, 7, 6.5, 6.2, 5.5, 6.3]
         }
df2 = pd.DataFrame(data2)

data3 = {'interest_rate': [5, 5.5, 6, 5.5, 5.25, 6.5, 7, 8, 7.5, 8.5],
         'index_price': [1500, 1520, 1525, 1523, 1515, 1540, 1545, 1560, 1555, 1565]
         }
df3 = pd.DataFrame(data3)


def f_1(win):
    global df1
    figure1 = plt.Figure(figsize=(6, 5), dpi=100)
    ax1 = figure1.add_subplot(111)
    bar1 = FigureCanvasTkAgg(figure1, win)
    bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
    df = df1[['country', 'gdp_per_capita']].groupby('country').sum()
    df.plot(kind='bar', legend=True, ax=ax1)
    ax1.set_title('Country Vs. GDP Per Capita')


def f_2(win):
    global df2
    figure2 = plt.Figure(figsize=(5, 4), dpi=100)
    ax2 = figure2.add_subplot(111)
    line2 = FigureCanvasTkAgg(figure2, win)
    line2.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
    df = df2[['year', 'unemployment_rate']].groupby('year').sum()
    df.plot(kind='line', legend=True, ax=ax2, color='r', marker='o', fontsize=10)
    ax2.set_title('Year Vs. Unemployment Rate')


def f_3(win):
    global df3
    figure3 = plt.Figure(figsize=(5, 4), dpi=100)
    ax3 = figure3.add_subplot(111)
    ax3.scatter(df3['interest_rate'], df3['index_price'], color='g')
    scatter3 = FigureCanvasTkAgg(figure3, win)
    scatter3.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
    ax3.legend(['index_price'])
    ax3.set_xlabel('Interest Rate')
    ax3.set_title('Interest Rate Vs. Index Price')


def new_window():
    global i
    new = tk.Toplevel(root)
    if i == 0:
        f_1(new)
    if i == 1:
        f_2(new)
    if i == 2:
        f_3(new)
    new_windows.append(new)
    i  = 1
    if i > 2:
        i = 0


root = tk.Tk()

tk.Button(root, text='new_window', command=new_window).pack()

root.mainloop()
  • Related