How to modify this tkinter GUI code to include classes and methods? - python

I have a fully functioning tkinter GUI (or so it seems...) but I would like to clean up the code a bit by creating classes and methods for those classes. Would someone be able to go through this code and re-organise it into classes and methods so it still functions in the same way, but if possible could you also annotate/ include notes to a certain degree? I would like this to be a learning experience for me as well.
One other thing that is causing an issue in the program is python freezing when I try to use the quit button. I don't believe this is a result of the quit button code because python had the same issue when I simply closed the GUI window prior to adding the quit function. Is this a result of me being on a mac, and perhaps some functioning issues related to that? Or is this an issue that can be resolved in my code?
On a side note to this: If anyone knows of any cool aesthetic things I can do with a GUI on a mac please answer with ideas! This program currently starts at a main window that has a descriptive label, a combobox of graph options, a button that launches a new window with the selected graph to look at concerning the data this is modeled off of, a gif that adds some pzazz to the window, and a quit button. I am using the arc theme which has made it look nicer, but would welcome any suggestions or ideas, in particular with graphing because I only really have the data to make bar graphs it seems but would love to be making bubble plots or something cool like that if I could do so with discrete, qualitative data.
Thanks for any help!
Here's my code:
import tkinter as tk
from ttkthemes import ThemedStyle
from tkinter import *
from tkinter.ttk import *
from PIL import ImageTk,Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
final_year_totals_df = get_totals_df(df, "Service Year")
regional_totals_df = get_totals_df(df, "Location")
ethnicity_totals_df = get_totals_df(df, "Ethnicity")
age_totals_df = get_totals_df(df, "Age band")
service_type_totals_df = get_service_df(df)
top_10_types_df = get_top_10(service_type_totals_df)
cause_totals_df = get_totals_df(df, "Cause of injury")
top_10_causes_df = get_top_10(cause_totals_df)
def pick_graphs():
graph = ACC_combo.get()
result = None
title = None
if graph == "Service Year":
result = final_year_totals_df
title = "ACC Concussion Claims by Year 2010-2019"
elif graph == "Service Type":
result = top_10_types_df
title = "ACC Concussion Claims for Top 10 Treatment Service Types 2010-2019"
elif graph == "Cause of Injury":
result = top_10_causes_df
title = "ACC Concussion Claims for Top 10 Causes of Injury 2010-2019"
elif graph == "Location":
result = regional_totals_df
title = "ACC Concussion Claims by Region 2010-2019"
elif graph == "Age Band":
result = age_totals_df
title = "ACC Concussion Claims by Age Bracket 2010-2019"
elif graph == "Ethnicity":
result = ethnicity_totals_df
title = "ACC Concussion Claims by Ethnicity 2010-2019"
return result, title
def display_graph():
window = tk.Toplevel()
window.geometry("1500x1200")
style = ThemedStyle(window)
style.set_theme("arc")
graph, title = pick_graphs()
fig = Figure(figsize = (3,3), dpi = 100)
fig.subplots_adjust(left = 0.2)
a = fig.add_subplot(111)
a.xaxis.set_ticks_position('none')
a.yaxis.set_ticks_position('none')
a.xaxis.set_tick_params(pad=5)
a.yaxis.set_tick_params(pad=10)
a.grid(b=True, color='grey', linestyle='-.', linewidth=0.5, alpha=0.2)
a.set_title(title, loc='left', pad=10)
graph.plot(kind='barh', legend=False, ax=a, color='crimson')
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
exit_button = Button(window, text = "Back To Main", command = window.destroy)
exit_button.pack()
def _quit():
root.quit()
root.destroy()
root = Tk()
root.title('ACC Concussion Data 2010-2019')
root.geometry("600x500")
style = ThemedStyle(root)
style.set_theme("arc")
root_label = Label(root, text = 'Welcome to my GUI!' + '\n' +
'Please select the variable you would like to explore from the menu options below.' +
'\n' + 'The resulting pop-up window will display an interactive graph of a specific
aspect' + '\n' + 'of the data compiled by the ACC about concussion claims filed from
2010-2019.')
root_label.pack(side=TOP)
graph_button = Button(root, text = "Graph it!", command = display_graph)
graph_button.pack()
quit_button = Button(root, text="Exit", command=_quit)
quit_button.pack(side=BOTTOM)
ACC_combo = Combobox(root, values = ["Service Year", "Service Type", "Cause of Injury",
"Location", "Age Band", "Ethnicity"])
ACC_combo.set("Service Year")
ACC_combo.pack()
photo = PhotoImage(file = "/Users/oscarevans/Desktop/concussion_GIF.gif")
label = Label(image = photo)
label.pack()
root.mainloop()

Related

ttkwidgets Timeline widget start and end bug

I'm trying to make a timeline widget using ttkwidgets but want the timeline to represent years. When I changed the start and finish to be years (ex. 2000 - 2010) I noticed that the markers I made did not show up on the timeline. Does anyone know how to fix this bug?
Here's the code I have:
import tkinter as tk
from ttkwidgets import TimeLine # have to install
window = tk.Tk()
timeline = TimeLine(
window,
categories={'Q' + str(key): {"text": "Category Q{}".format(key)} for key in range(0, 5)},
height=100,
start = 2000.0,
finish = 2010.0,
extend=True
)
menu = tk.Menu(window, tearoff=False)
menu.add_command(label="Some Action", command=lambda: print("Command Executed"))
timeline.tag_configure("Q1", right_callback=lambda *args: print(args), menu=menu, foreground="green",
active_background="yellow", hover_border=2, move_callback=lambda *args: print(args))
timeline.create_marker("Q1", 2001.0, 2002.0, background="white", text="Change Color", tags=("Q1",), iid="1")
timeline.create_marker("Q2", 2002.0, 2003.0, background="green", text="Change Category", foreground="white", iid="2",
change_category=True)
timeline.draw_timeline()
timeline.grid()
window.mainloop()
I believe the issue is calculating the pixel positions in the create_marker() function (lines 498-499)
https://github.com/TkinterEP/ttkwidgets/blob/master/ttkwidgets/timeline.py
I tried changing those lines to
x1 = (start - self._start) / self._resolution * self._zoom_factor
x2 = (self._finish - (self._finish - finish) - self._start) / self._resolution * self._zoom_factor
But now I seem to have introduced another bug since the code is not running consistently (I get a Python is not responding message)

How can i avoid making two tkinter windows?

I have made a GUI in which on user values a line is drawn, and when you click on the line another window opens where you can select something. And when you press ok it should print on the terminal. I think because i am creating a new tkinter window when i click on the line, i am unable to retrieve the user selection.How can i solve this problem? Your help is greatly appreciated.
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import style
import tkinter as tk
from tkinter import ttk
from tkinter import *
import random
import numpy as np
LARGE_FONT = ('Verdana',12)
style.use('ggplot')
from matplotlib import pyplot as plt
class PageOne(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
label = tk.Label(self,text='Experiment', font = LARGE_FONT)
label.pack(pady=10,padx=10)
self.adding_widgets()
button2= ttk.Button(self,text='Validate', command=self.draw)
button2.pack()
button3= ttk.Button(self,text='Erase', command=self.dlet)
button3.pack()
self.lines_pts=[]
self.f1= Figure(figsize=(5,5),dpi=100)
self.b=self.f1.add_subplot(1,1,1)
self.b.set_xlim([0,10])
self.b.set_ylim([0,10])
self.canvas = FigureCanvasTkAgg(self.f1, self)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side=tk.TOP,fill=tk.BOTH,expand=True)
self.f1.canvas.mpl_connect('pick_event', self.on_pick)
self.lines =[]
def adding_widgets(self,*args):
labfram=ttk.LabelFrame(self,width=100,height=100,text='Enter Member Coordinates',labelanchor=N)
labfram.pack()
l1=ttk.Label(labfram,text='x0')
l1.pack(side='left')
self.x0=Entry(labfram,width=10,)
self.x0.pack(side='left')
l2=ttk.Label(labfram,text='y0')
l2.pack(side='left')
self.y0=Entry(labfram,width=10)
self.y0.pack(side='left')
l3=ttk.Label(labfram,text='x1')
l3.pack(side='left')
self.x1=Entry(labfram,width=10)
self.x1.pack(side='left')
l4=ttk.Label(labfram,text='y1')
l4.pack(side='left')
self.y1=Entry(labfram,width=10)
self.y1.pack(side='left')
def draw(self):
p0 = float(self.x0.get()), float(self.y0.get())
p1 = float(self.x1.get()), float(self.y1.get())
self.lines_pts.append((p0,p1))
for p0,p1 in self.lines_pts:
x0, y0, x1, y1 = *p0, *p1
X = x0,x1
Y = y0,y1
ax = self.b.plot(X,Y, 'r', linewidth=4,picker=5 )
self.lines.append(ax)
self.canvas.draw()
def dlet(self):
self.b.lines.remove(self.b.lines[-1])
self.canvas.draw()
def on_pick(self,event):
w=Tk()
w.title('Channel Select')
w.geometry('250x50')
n = StringVar()
ch = ttk.Combobox(w, width = 15 , textvariable = n)
ch['values'] = ('CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8','CH9','CH10')
ch.grid(column=1,row=0)
ch_label = ttk.Label(w,text='Select Your Channel')
ch_label.grid(column=0,row=0)
ch_button = ttk.Button(w,text='OK',command=lambda: print ('value is:'+ n.get()))
ch_button.grid(column=1,row=1)
for line in self.lines:
line = event.artist
xdata, ydata = line.get_data()
ind = event.ind
print('on pick line:', np.array([xdata[ind], ydata[ind]]).T)
app = PageOne()
app.mainloop()
It's here do you really want to create a new window?
def on_pick(self,event):
w=Tk()
w.title('Channel Select')
w.geometry('250x50')
Take note that when you do this w=Tk() you are creating an instance of an object a NEW OBJECT which is Tk() so that's why it creates a new window. What is your goal are you really trying to pop up a window whenever you click cause if not you I think you can remove this.
----UPDATE---
So the reason why your window keeps popping out is because on your function you create an instance of an object with it. Then I tried putting the w=Tk() away from the function but still it would create or show that object cause it is within the mainloop.
Alternative solution is you can check if once that window exists or its state is normal then you can just do a focus.
Here is the code that I've added only on your on_pick function. I also added on your __init__ method a self.w = None to just set the w variable to None initially.
OVERALL this are only the changes made
def on_pick(self,event):
try:
if self.w.state() == "normal":
self.w.focus()
except BaseException as on_pick_error:
self.w=Toplevel()
self.w.title('Channel Select')
self.w.geometry('250x50')
n = StringVar()
ch = ttk.Combobox(self.w, width = 15 , textvariable = n)
ch['values'] = ('CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8','CH9','CH10')
ch.grid(column=1,row=0)
ch_label = ttk.Label(self.w,text='Select Your Channel')
ch_label.grid(column=0,row=0)
ch_button = ttk.Button(self.w,text='OK',command=lambda: print ('value is:'+ n.get()))
ch_button.grid(column=1,row=1)
for line in self.lines:
line = event.artist
xdata, ydata = line.get_data()
ind = event.ind
print('on pick line:', np.array([xdata[ind], ydata[ind]]).T)
You might wonder what this does, it just checks if the state of your self.w which happens to be your window that pops out, it just checks if its state is equal to "normal" meaning that it is active, then when it is it will just do a .focus() and focus on that current window. Else it would create a new window which is this self.w=Toplevel() and so on.
try:
if self.w.state() == "normal":
self.w.focus()
except BaseException as on_pick_error:
self.w=Toplevel()
self.w.title('Channel Select')
self.w.geometry('250x50')
Why is it Toplevel instead of Tk?
I would suggest to have it Toplevel however it is up to you to decide since Toplevel and Tk I think might just have the same properties but do note they are not the same so it's up to you.

Embedding a MatPlotLib Graph in Tkinter [.grid method], and Customizing MatPlotLib's Navigation Toolbar

I've been having problems with embedding my MatPlotLib Graph in Tkinter, and after doing some searching on Google, and the MatPlotLib website, the best I could get was the standard method:
import tkinter
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
fig = Figure(figsize=(5, 4), dpi=100)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update()
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
Now if I try to replace the packing layout with a .grid (and remove the .pack() parameters), I get a bunch of errors, and no matter how many Google searches I have tried, all of the methods of embedding a MatPlotLib graph in Tkinter are only using the pack method. Can someone help me out with this? I want to embed the graph, but using the grid method, as the rest of the layout of my GUI application is .grid layout.
Another problem I'm having with the navigation toolbar in Tkinter is the fact that the navigation toolbar can apparently be customized (At least according to SentDex [5:18]). He doesn't seem to go over how I can do this, which makes it difficult for me, because I'm not very happy with MatPlotLib's buttons (They look very archaic and outdated).
Can someone please help me out with this? When I only put the graph in, it seems to work just fine, but I get issues when trying to put in the Navigation Toolbar with the graph as well. Any help on this would be appreciated. Thanks!
Here is a simple plot and navigation toolbar inside tkinter window using grid geometry manager only.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
window = tk.Tk()
btn = tk.Label(window, text='A simple plot')
btn.grid(row=0, column=0, padx=20, pady=10)
x = ['Col A', 'Col B', 'Col C']
y = [50, 20, 80]
fig = plt.figure(figsize=(4, 5))
plt.bar(x=x, height=y)
# You can make your x axis labels vertical using the rotation
plt.xticks(x, rotation=90)
# specify the window as master
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=0, ipadx=40, ipady=20)
# navigation toolbar
toolbarFrame = tk.Frame(master=window)
toolbarFrame.grid(row=2,column=0)
toolbar = NavigationToolbar2Tk(canvas, toolbarFrame)
window.mainloop()
Output GUI
I have not worked on customizing the navigation toolbar so I haven't included any solution for that part. But I'll look into it surely and update you if I find something useful. Hope you find this helpful.
I managed to get a simple app to control a matplotlib graph with window resizing.
I haven't used the navigation bar feature so that might be an addition to this framework
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from google.cloud import bigquery
import os, json, sys
from time import time
import pandas as pd
class SliderGraph:
def __init__(self, master):
self.master = master
# with open(self.resource_path(config_file)) as fp:
# self.config = json.load(fp)
# os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = self.resource_path(creds_file)
# self.projectID = self.config['projectID']
# self.datasetID = self.config['datasetID']
# last_used_freq = self.config['last_used_frequency']
# last_used_delta = self.config['last_used_delta']
self.bounds = 1
self.frame = tk.Frame(master)
self.fig = Figure()
row = 0
self.ax = self.fig.add_subplot(111)
self.ax.set_xlabel("Run Numbers")
self.ax.set_ylabel("UDD")
self.ax.set_ylim([-self.bounds,self.bounds])
self.canvas = FigureCanvasTkAgg(self.fig, master=master) # , width=win_width, height=(win_height-50))
self.canvas.draw()
self.canvas.get_tk_widget().grid(row=row, columnspan=2, sticky='nsew')
row+=1
self.table_label = tk.Label(master, text="Enter BigQuery Table Name")
self.table_label.grid(row=row, column=0)
# row += 1
self.table_name = tk.Entry(master)
# self.table_name.insert(0,self.config['last_used_table'])
self.table_name.grid(row=row, column=1, sticky='ew')
row += 1
self.get_table_button = tk.Button(master, text="Get Table Data and Plot", command=self.plot_data)
self.get_table_button.grid(row=row, columnspan=2)
row += 1
self.frequency_slider = tk.Scale(master, from_=400, to=4500, orient=tk.HORIZONTAL, command=self.update_plot)
# self.frequency_slider.set(last_used_freq)
self.frequency_slider.grid(row=row,columnspan=2, sticky="nsew")
row += 1
self.frequency_entry = tk.Entry(master)
# self.frequency_entry.insert(0,last_used_freq)
self.frequency_entry.grid(row=row, columnspan=2)
row += 1
self.delta_slider = tk.Scale(master, from_=-500, to=500, orient=tk.HORIZONTAL, command=self.update_plot)
# self.delta_slider.set(last_used_delta)
self.delta_slider.grid(row=row, columnspan=2, sticky="ensw")
row += 1
self.delta_entry = tk.Entry(master)
# self.delta_entry.insert(0, last_used_delta)
self.delta_entry.grid(row=row, columnspan=2)
row += 1
self.get_table_button = tk.Button(master, text="Autoscale", command=self.autoscale)
self.get_table_button.grid(row=row,columnspan=2)
row += 1
tk.Grid.columnconfigure(master, 0, weight=1)
tk.Grid.columnconfigure(master, 1, weight=1)
tk.Grid.rowconfigure(master, 0, weight=5)
for x in range(1,row):
tk.Grid.rowconfigure(master, x, weight=0)
master.protocol('WM_DELETE_WINDOW', self.close)
self.df = None
self.frequency_list = []
self.series = None
self.elapsed = time()*1000
def plot_data(self):
self.ax.clear()
self.client = bigquery.Client(project=self.projectID)
self.tableID = f"`{self.datasetID}.{self.table_name.get()}`"
QUERY = (
f"SELECT * EXCEPT (testCount,elapsed_run_time_ms_,moleculeValue,onboardTemp1,onboardTemp2,temp,tempControl,\
logamp, txrx,timestamp) FROM {self.tableID} ORDER BY runCount ASC;"
)
# query_job = self.client.query(QUERY)
# rows = query_job.result()
self.df = pd.read_gbq(QUERY,self.projectID)
for col in self.df.columns:
if 'runCount' in col:
continue
self.frequency_list.append(int(col.replace('_','')))
self.frequency_slider.configure(from_=min(self.frequency_list),to=max(self.frequency_list))
self.delta_slider.configure(from_=-max(self.frequency_list),to=max(self.frequency_list))
freq = f'_{self.frequency_slider.get()}'
freq2 = f'_{self.frequency_slider.get() + self.delta_slider.get()}'
self.series = self.df[freq] - self.df[freq2]
self.series.plot(ax=self.ax)
self.ax.set_ylim([-self.bounds,self.bounds])
self.fig.canvas.draw()
self.fig.canvas.flush_events()
pass
def update_plot(self, newslider):
try:
self.ax.clear()
freq = f'_{self.frequency_slider.get()}'
freq2 = f'_{self.frequency_slider.get() + self.delta_slider.get()}'
self.series = self.df[freq] - self.df[freq2]
self.series.plot(ax=self.ax)
zero = self.series.mean()
self.ax.set_ylim([zero-self.bounds, zero+self.bounds])
self.fig.canvas.draw()
self.fig.canvas.flush_events()
# self.master.update_idletasks()
if ((time()*1000)-self.elapsed > 100):
self.elapsed = time()*1000
self.frequency_entry.delete(0,'end')
self.frequency_entry.insert(0,freq.replace('_',''))
self.delta_entry.delete(0,'end')
self.delta_entry.insert(0,self.delta_slider.get())
except:
pass
def play_runs(self):
pass
def autoscale(self):
self.ax.clear()
self.series.plot(ax=self.ax)
self.ax.relim()
self.ax.autoscale()
zero = self.series.mean()
self.bounds = self.series.max() - self.series.min()
self.ax.set_ylim([zero-self.bounds,zero+self.bounds])
self.fig.canvas.draw()
self.fig.canvas.flush_events()
def close(self):
self.master.destroy()
if __name__=="__main__":
root = tk.Tk()
slider_fig = SliderGraph(root)
root.mainloop()

Matplotlib in Tkinter

I am trying to plot a graph using Matplotlib in tkinter. Here the graph should plot all the values of 'a' in the range 0-24. My code is as follows
import math
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
def att_func(d=0, n=0, z=0):
# Getting User inputs from the UI
d = d_user.get()
n = n_user.get()
z = z_user.get()
a = (-9.87 * math.sin(2 * ((2 * math.pi * (d - 81)) / 365)) + n * z)
a_label.configure(text=a)
return (a)
#Plotting the graph
class App:
def __init__(self, master):
frame = tkinter.Frame(master)
self.nbutton_graph = tkinter.Button(frame, text="Show Graph", command=self.graph)
self.nbutton_graph.pack()
f = Figure(figsize=(5, 5), dpi=100)
ab = f.add_subplot(111)
self.line, = ab.plot(range(24))
self.canvas = FigureCanvasTkAgg(f, self)
self.canvas.show()
self.canvas.get_tk_widget().pack()
def graph(self):
day_elevation_hrs = []
for i in range(24):
day_elevation_hrs.append(att_func(i, 0, 0)[0])
self.canvas.draw()
return
root = tkinter.Tk()
app = App(root)
# User Inputs
d_user = IntVar()
n_user = DoubleVar()
z_user = DoubleVar()
nlabel_d = Label(text="Enter d").pack()
nEntry_d = Entry(root, textvariable=d_user).pack()
nlabel_n = Label(text="Enter n").pack()
nEntry_n = Entry(root, textvariable=n_user).pack()
nlabel_z = Label(text="Enter z").pack()
nEntry_z = Entry(root, textvariable=z_user).pack()
# Displaying results
nlabel_a = Label(text="a is").pack()
a_label = Label(root, text="")
a_label.pack()
root.mainloop()
Here I am able to calculate what i need. But when I am trying to plot the same, I am unable to. I tried as many modification I could. But seems to be in a stale mate. I am sure that I am going wrong somewhere. but can't figure out where.
when i try to plot the same graph with matplotlib, with out tkinter, it works. but when I try to do it in a UI with tkinter i am unable to.. Here is the code for plotting graph in matplotlib without tkinter.
import matplotlib.pylab as pl
day_elevation_hrs=[]
for i in range(24):
day_elevation_hrs.append(att_func(i, 0, 0)[0])
pl.title("Elevation of a in range i")
pl.plot(day_elevation_hrs)
Your code won't run as posted, but I can see two definite problems.
First, your App is a frame that contains a canvas, but you never add the frame to the root window. Because of that, your canvas will be invisible.
Add the following code after you create an instance of App:
app.pack(side="top", fill="both", expand=True)
Second, you are making a common mistake when defining the button to display the graph. The command attribute takes a reference to a function. However, you are calling the graph() function, and using the result as the value of the command attribute.
In other words, change this:
self.nbutton_graph = Tk.Button(self, text="Show Graph", command=self.graph())
to this:
self.nbutton_graph = Tk.Button(self, text="Show Graph", command=self.graph)
Notice the lack of () after self.graph. This could be the reason why you are seeing errors like 'App' object has no attribute 'line', since you are calling the graph function before you fully initialize all your variables.
This documentation shows that the second explicit parameter for FigureCanvasTkAgg.__init__ should be the master. (It's really a keyword parameter.)
So, have you tried changing that line to ...
self.canvas = FigureCanvasTkAgg(f, master=master)

Tkinter menu showing pylab plot: quit button not working properly

I am writing a GUI application using TKinter.
Basically I have a menu where I can select different functions. One of this is supposed to plot a graph, so it opens a figure plot.
On the main GUI I placed a "QUIT" button to close the application.
Here is a sample of my code:
Main.py
from Tkinter import *
import ALSV_Plots
tk = Tk()
tk.title('ALS Verification v01-00')
tk.geometry('500x282')
def doneButton():
tk.quit()
def plotCoarseX():
plot = ALSV_Plots.CoarseXPlot(showImage = True)
plot.plotFunction()
menubar = Menu(tk)
plotMenu = Menu(menubar, tearoff=0)
plotMenu.add_command(label="Coarse X plot", command=plotCoarseX)
quitButton = Button(tk,
compound = LEFT,
image = exitIcon,
text =" QUIT",
font = ('Corbel', 10),
command = doneButton)
quitButton.place(x = 400, y = 240)
tk.mainloop()
ALSV_Plots.py
import pylab
import sharedVar
class CoarseXPlot():
def __init__(self, showImage = True):
self.show = showImage
def plotFunction(self):
xSrcSlice, xLightSetSlice] = sharedVar.coarseXResult
pylab.ioff()
figNum = getFigNumber()
fig = pylab.figure(figNum, figsize=(10.91954, 6.15042))
text = 'Coarse X determination\nX=%.5e, beam 4-Sigma=%.5e' % (beamPosition, beam4SigmaSize)
fig.text(0.5, 0.95, text, horizontalalignment='center', verticalalignment='center')
pylab.xlabel('X')
pylab.ylabel('Light')
pylab.plot(xSrcSlice, xLightSetSlice, 'bd')
pylab.grid(True)
if self.show:
pylab.show()
pylab.close()
return fig
Problem: when I select the plot function from the menu the figure is correctly displayed. I close it manually, but when I try to quit the application by clicking the "quit" button I have to press it twice in order to close the application.
Do you have any idea why this is happening?
I found the solution myself. Apparently the "show()" method in my matplotlib was set as blocking by default. So I solved the issue by forcing the "block" parameter to "False":
pylab.show(block = False)
I also removed the calls to:
pylab.ioff()
pylab.close()

Categories