Python tkinter hangs when I try to close graphs - python

I've tried using Tk to make a function that will allow users to look at a graph and select if points are wrong. I had this working before, but now my code is hanging every time I run it and try to go to the next graph. Specifically when I click "next graph" and it should run
def _quit(self):
self.master.destroy() # stops mainloop
When I manually stop the code I get this message:
File "C:\Users\laura\Anaconda3\lib\tkinter\__init__.py", line 1429, in mainloop
self.tk.mainloop(n)
KeyboardInterrupt
Any suggestions? I've read using root.destroy() but I haven't been able to get that to work
Thanks!
def config_plot():
tk, ax = plt.subplots()
ax.set(title='Are the peaks okay?')
return (tk, ax)
class matplotlibSwitchGraphs:
def __init__(self, master):
self.master = master
self.frame = Frame(self.master)
self.frame.pack(expand=YES, fill=BOTH)
self.fig = Figure(figsize=(12,7))
self.ax = self.fig.gca()
self.canvas = FigureCanvasTkAgg(self.fig, self.master)
self.config_window()
self.graphIndex = 0
self.draw_graph_one()
def config_window(self):
toolbar = NavigationToolbar2Tk(self.canvas, self.master)
toolbar.update()
self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
print('connect')
self.canvas.mpl_connect("button_press_event", self.on_button_press)
self.button = Button(self.master, text="YES, next graph", command=self._quit) #this is where it gets stuck!
self.button.pack(side=BOTTOM)
self.button_switch = Button(self.master, text="NO", command=self.switch_graphs)
self.button_switch.pack(side=BOTTOM)
def draw_graph_one(self):
self.ax.clear() # clear current axes
self.ax.plot(data)
self.ax.plot(peaks,loc_peaks, marker='o', linestyle='none')
# self.ax.set(xlim=(touchdown_cut[-15]-50,toeoff_cut[-5]+50))
self.ax.set(title='Check the data')
self.canvas.draw()
def draw_graph_two(self):
self.ax.clear()
self.ax.plot(data)
self.ax.set(title='Click all the incorrect peaks')
self.ax.plot(peaks,loc_peaks, marker='o', linestyle='none')
self.canvas.draw()
def on_button_press(self, event):
print('xdata, ydata:', event.xdata, event.ydata)
# return (event.xdata)
global ix
ix = event.xdata
global clicks
clicks.append((ix))
array1 = np.asarray(peaks)
idx1 = (np.abs(array1 - event.xdata)).argmin()
global peaks_adj
global loc_peaks_adj
peaks_adj = np.delete(peaks, idx1)
loc_peaks_adj = np.delete(loc_peaks, idx1)
self.canvas.flush_events()
self.ax.clear()
self.ax.plot(data)
self.ax.set(title='Click all the incorrect peaks')
self.ax.plot(peaks_adj,loc_peaks_adj, marker='o', linestyle='none')
self.canvas.draw()
return (clicks)
def _quit(self):
self.master.destroy() # stops mainloop
def switch_graphs(self):
# Need to call the correct draw, whether we're on graph one or two
self.graphIndex = (self.graphIndex + 1 ) % 2
if self.graphIndex == 0:
self.draw_graph_one()
else:
self.draw_graph_two()
def main():
root = Tk()
matplotlibSwitchGraphs(root)
root.mainloop()
if __name__ == '__main__':
main()

Try changing root.destroy() to self.destroy()as you are working with classes. If using function through a button remove the()after command likecommand=self.destroy`.
Also don't use global, use self. to create a class variable if you want to access the variable in multiple function.
As far as for lags, I would recommend you to use update and update_idletasks.

Related

Matplotlib FuncAnimation in Tkinter - Axes labels not being displayed/retained

I am using Python with TKinter as GUI; and Matplotlib's FuncAnimation, to display 2 graphs next to each other. These graphs are representing sensor readings (temperature, humidity), and I would like to label both the x-axis and y-axis. (The matplotlib graph title is not required, but I am including it for testing purposes)
I'll try not to divulge unnecessary details; but in simple terms, I have 2 classes representing the sensors, "temperature" and "humidity", and another class "main_window" to combine both the classes into the Tkinter GUI.
This is a preview of the main.py file; including the main_window class.
class main_window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Indoor Monitoring System")
frame1 = tk.Frame(self)
frame1.grid(column=0, row=0)
frame2 = tk.Frame(self)
frame2.grid(column=1, row=0)
temperature(frame1, self)
humidity(frame2, self)
if __name__ == "__main__":
runObj = main_window()
runObj.mainloop()
and this is a preview of the temperature class
class temperature(tk.Frame):
def __init__(self, parent, controller):
pd.set_option('display.float_format', lambda x: '%.2f' % x)
plt.style.use('fivethirtyeight')
self.x_values = []
self.y_values = []
self.counter = 0
self.fig = plt.figure(figsize=(8, 4), dpi=100)
self.ax1 = self.fig.add_subplot(111)
self.ax1.set(xlabel="Reading Counter", ylabel="Degrees Celcius")
label = tk.Label(parent, text="Temperature Graph", font=("Arial", 12)).grid(column=0, row=0)
canvas = FigureCanvasTkAgg(self.fig, master=parent)
canvas.get_tk_widget().grid(column=0, row=1)
self.ani1 = FuncAnimation(self.fig, self.animate, interval=10000, blit=False)
def animate(self, i):
df = pd.read_csv('C:\\log.csv')
x_temporary_values = df["Counter"]
y_temp_values = df["Temp"]
currrow = x_temporary_values[self.counter]
if currrow is not None:
self.x_values.append(currrow)
self.y_values.append(y_temp_values[self.counter])
self.ax1.plot(self.x_values, self.y_values)
# self.ax1.set(xlabel="Reading Counter", ylabel="Degrees Celcius")
self.counter += 1
The humidity class is very similar.
I am trying to set the xlabel and ylabel as such:
self.ax1.set(xlabel="Reading Counter", ylabel="Degrees Celcius", title="Temperature")
I've also tried:
self.ax1.set_title("Temperature")
self.ax1.set_xlabel("Reading Counter")
self.ax1.set_ylabel("Degrees Celcius")
The title and y_label are initially displayed. After refreshing the graph via the "animate" funxction, the title is retained, the y_label is not. The x_label is never shown.
I have also tried to re-call the self.ax1.set() in the animation function. Does not make a difference.
What am I doing wrong?
Note: that this setup is the only one that worked for me, in the context of trying to animate the graphs. Most other setups that I was more accustomed to, do not work with FuncAnimation.
Also, note that the UI is not refined by any means (especially font sizes). I am concerned with fixing the label issues first.

Is there a way to delay the on_changed() call for matplotlib sliders?

I am currently working to create a tkinter GUI which shows a matplotlib figure with two sliders. The sliders are to be used for selecting a range of data (they are connected to vertical lines on the plot).
The issue I am running into is that when I change the sliders quickly a few times the GUI freezes. I believe this is due to the on_changed() call I am using to make the sliders update. When you move the slider quickly I believe on_changed() is being called multiple times at a faster rate than the program can keep up with, thus causing the freeze.
Is there any way to add a delay to the on_changed() call so it only looks to see if the slider has been changed every given interval of time (ex. 100 ms)?
Below is example code I found which freezes in the way I described.
import tkinter as tk
from tkinter.ttk import Notebook
from tkinter import Canvas
from tkinter import messagebox as msg
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.widgets import Slider, Button, RadioButtons
class LukeOutline(tk.Tk):
def __init__(self):
# Inherit from tk.Tk
super().__init__()
# Title and size of the window
self.title('Luke Outline')
self.geometry('600x400')
# Create the drop down menus
self.menu = tk.Menu(self,bg='lightgrey',fg='black')
self.file_menu = tk.Menu(self.menu,tearoff=0,bg='lightgrey',fg='black')
self.file_menu.add_command(label='Add Project',command=self.unfinished)
self.menu.add_cascade(label='File',menu=self.file_menu)
self.config(menu=self.menu)
# Create the tabs (Graph, File Explorer, etc.)
self.notebook = Notebook(self)
graph_tab = tk.Frame(self.notebook)
file_explorer_tab = tk.Frame(self.notebook)
# Sets the Graph Tab as a Canvas where figures, images, etc. can be added
self.graph_tab = tk.Canvas(graph_tab)
self.graph_tab.pack(side=tk.TOP, expand=1)
# Sets the file explorer tab as a text box (change later)
self.file_explorer_tab = tk.Text(file_explorer_tab,bg='white',fg='black')
self.file_explorer_tab.pack(side=tk.TOP, expand=1)
# Add the tabs to the GUI
self.notebook.add(graph_tab, text='Graph')
self.notebook.add(file_explorer_tab, text='Files')
self.notebook.pack(fill=tk.BOTH, expand=1)
# Add the graph to the graph tab
self.fig = Figure()
graph = FigureCanvasTkAgg(self.fig,self.graph_tab)
graph.get_tk_widget().pack(side='top',fill='both',expand=True)
EllipseSlider(self.fig)
#------------------------------------------------------
def quit(self):
'''
Quit the program
'''
self.destroy()
#------------------------------------------------------
def unfinished(self):
'''
Messagebox for unfinished items
'''
msg.showinfo('Unfinished','This feature has not been finished')
#------------------------------------------------------
def random_graph(self):
x = list(range(0,10))
y = [i**3 for i in x]
fig = Figure()
axes = fig.add_subplot(111)
axes.plot(x,y,label=r'$x^3$')
axes.legend()
return fig
#----------------------------------------------------------
class EllipseSlider():
#------------------------------------------------------
def __init__(self,fig):
self.fig = fig
# Initial values
self.u = 0. #x-position of the center
self.v = 0. #y-position of the center
self.a = 2. #radius on the x-axis
self.b = 1.5 #radius on the y-axis
# Points to plot against
self.t = np.linspace(0, 2*np.pi, 100)
# Set up figure with centered axes and grid
self.ax = self.fig.add_subplot(111)
self.ax.set_aspect(aspect='equal')
self.ax.spines['left'].set_position('center')
self.ax.spines['bottom'].set_position('center')
self.ax.spines['right'].set_color('none')
self.ax.spines['top'].set_color('none')
self.ax.xaxis.set_ticks_position('bottom')
self.ax.yaxis.set_ticks_position('left')
self.ax.set_xlim(-2,2)
self.ax.set_ylim(-2,2)
self.ax.grid(color='lightgray',linestyle='--')
# Initial plot
self.l, = self.ax.plot(self.u+self.a*np.cos(self.t),
self.v+self.b*np.sin(self.t),'k')
# Slider setup
self.axcolor = 'lightgoldenrodyellow'
self.axb = self.fig.add_axes([0.25, 0.1, 0.65, 0.03], facecolor=self.axcolor)
self.axa = self.fig.add_axes([0.25, 0.15, 0.65, 0.03], facecolor=self.axcolor)
self.sb = Slider(self.axb, 'Y Radius', 0.1, 2.0, valinit=self.b)
self.sa = Slider(self.axa, 'X Radius', 0.1, 2.0, valinit=self.a)
# Call update as slider is changed
self.sb.on_changed(self.update)
self.sa.on_changed(self.update)
# Reset if reset button is pushed
self.resetax = self.fig.add_axes([0.8,0.025,0.1,0.04])
self.button = Button(self.resetax, 'Reset', color=self.axcolor, hovercolor='0.975')
self.button.on_clicked(self.reset)
# Color button setup
self.rax = self.fig.add_axes([0.025, 0.5, 0.15, 0.15], facecolor=self.axcolor)
self.radio = RadioButtons(self.rax, ('red', 'blue', 'green'), active=0)
self.radio.on_clicked(self.colorfunc)
#------------------------------------------------------
def update(self, val):
'''
Updates the plot as sliders are moved
'''
self.a = self.sa.val
self.b = self.sb.val
self.l.set_xdata(self.u+self.a*np.cos(self.t))
self.l.set_ydata(self.u+self.b*np.sin(self.t))
#------------------------------------------------------
def reset(self, event):
'''
Resets everything if reset button clicked
'''
self.sb.reset()
self.sa.reset()
#------------------------------------------------------
def colorfunc(self, label):
'''
Changes color of the plot when button clicked
'''
self.l.set_color(label)
self.fig.canvas.draw_idle()
#----------------------------------------------------------
if __name__ == '__main__':
luke_gui = LukeOutline()
luke_gui.mainloop()
es = EllipseSlider()
(UPDATE):
I have implemented the correction you showed and the code looks to work as intended except the vertical sliders I am using now do not update their position until I move the slider a second time. Please see the section from my code below:
# Call update if slider is changed
self.sa.on_changed(self.schedule_update)
self.sb.on_changed(self.schedule_update)
def update(self, value):
print(value)
self.vert_a.set_xdata(value)
self.vert_b.set_xdata(self.sb.val)
root.update()
def schedule_update(self, new_value):
if self.after_id:
root.after_cancel(self.after_id)
self.after_id = root.after(1000, self.update, new_value)
root = tk.Tk()
mainapp = MainApp(root)
root.mainloop()
Tkinter has a method named after for delaying the execution of a command. Your slider can use this to delay the effect of the slider for a second or two. If the slider moves, it can cancel the previously scheduled command and submit a new one.
Here's an example using plain tkinter for simplicity. Run the code and then move the slider quickly or slowly. You'll see that the label will only update after you haven't moved the slider for a full second.
import tkinter as tk
after_id = None
def schedule_update(new_value):
global after_id
if after_id:
root.after_cancel(after_id)
after_id = root.after(1000, update_label, new_value)
def update_label(new_value):
label.configure(text=f"New value: {new_value}")
root = tk.Tk()
label = tk.Label(root, text="", width=20)
scale = tk.Scale(
root, from_=1, to=100,
orient="horizontal", command=schedule_update
)
label.pack(side="top", fill="x", padx=20, pady=(10,0))
scale.pack(side="bottom", fill="x", padx=20, pady=10)
root.mainloop()

How to disconnect matplotlibs event handler?

I am trying to find a way to disconnect matplotlib's event handler by using mpl_disconnect. So far I followed the instructions here and here to learn how to disconnect but unfortunately it did not work for me.
With the following code I am able to connect button_press_event to the callback function on_press by using a checkbutton. After unchecking cid prints 0 so it should be disconnected but the callback function still fires.
I am using python 3.7.4 and matplotlib 3.1.1.
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.image as mpimg
class MainApplication(Tk.Frame):
def __init__(self, parent, *args, **kwargs):
Tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
parent.iconify
parent.grid_rowconfigure(1, weight=1)
parent.grid_columnconfigure(1, weight=1)
top_frame = Tk.Frame(parent)
top_frame.grid(row=0)
mid_frame = Tk.Frame(parent)
mid_frame.grid(row=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_aspect('equal')
canvas = FigureCanvasTkAgg(self.fig, mid_frame)
canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
canvas._tkcanvas.grid(row=0, column=0, sticky="nsew")
img = mpimg.imread('stinkbug.png') # insert image file here
self.ax.imshow(img)
self.fig.canvas.draw()
self.var1 = Tk.IntVar()
chkbx1 = Tk.Checkbutton(top_frame, text='connect', variable=self.var1, command=self.de_activate)
chkbx1.grid(row=0, column=0, sticky="w")
def de_activate(self):
print('checkbutton: '+str(self.var1.get()))
self.cidpress = 0
if self.var1.get() == 1:
self.cidpress = self.fig.canvas.mpl_connect('button_press_event', self.on_press)
print('on_press connected (cid='+str(self.cidpress)+')')
else:
self.fig.canvas.mpl_disconnect(self.cidpress)
print('on_press disconnected (cid='+str(self.cidpress)+')')
def on_press(self, event):
if event.inaxes != self.ax: return
print('button pressed')
if __name__ == '__main__':
root = Tk.Tk()
MainApplication(root).grid(row=0, column=0, sticky="nsew")
root.mainloop()
To disconnect, you must pass the original cid to mpl_disconnect, but you're resetting self.cidpress before the if .. else .. block, so you're always requesting the disconnection of cid 0. Remove self.cidpress = 0 and place it right after self.fig.canvas.mpl_disconnect(self.cidpress):
def de_activate(self):
print('checkbutton: '+str(self.var1.get()))
if self.var1.get() == 1:
self.cidpress = self.fig.canvas.mpl_connect('button_press_event', self.on_press)
print('on_press connected (cid='+str(self.cidpress)+')')
else:
self.fig.canvas.mpl_disconnect(self.cidpress)
self.cidpress = 0 # <<<<<<<<<<<<<<<<<<<<
print('on_press disconnected (cid='+str(self.cidpress)+')')

How to prevent multiple graph plot in tkinter canvas

Im using function plot_graph() which is triggered everytime you open Graph window, my question is: How to plot graph with this function everytime on same place?
plot_graph function looks like this:
def plot_graph(self,event):
df_1 = self.controller.df_1
df_2 = self.controller.df_2
df_3 = self.controller.df_3
df_4 = self.controller.df_4
f = Figure(figsize=(5, 5), dpi=100)
a = f.add_subplot(111)
a.plot(df_1['mean'])
a.plot(df_2['mean'])
a.plot(df_3['mean'])
a.plot(df_4['mean'])
a.legend(['bsl morning', '1st expo', 'bsl noon', '2nd expo'])
canvas = FigureCanvasTkAgg(f, self)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
And im calling this function with self.bind("<<ShowGraph>>", self.plot_graph)
After second call of this function program starts create second graph under first one, on and on. Output of program, as you can see on image.I want to prevent this and have only one graph.
Thank you for help!
I believe you have two choices:
destroy the canvas before creating a new one. See Using tkinter -- How to clear FigureCanvasTkAgg object if exists or similar?
(I think this is a better method) create the figure/canvas in the init section, and reuse those objects to plot new data:
The following demonstrates option #2:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import tkinter
import numpy as np
class MyClass:
def __init__(self, frame):
self.frame = frame
self.fig = Figure(figsize=(5, 5), dpi=100)
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, self.frame)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
self.button = tkinter.Button(self.frame, text="plot", command=self.plot_graph)
self.button.pack()
def plot_graph(self):
x, y = np.random.random(size=(2, 10))
self.ax.cla()
self.ax.plot(x, y)
self.canvas.draw()
root = tkinter.Tk()
MyFrame = tkinter.Frame(root)
MyClass(MyFrame)
MyFrame.pack()
root.mainloop()

Why does Python Tkinter Canvas draw causes a freeze? [duplicate]

I have written a piece of code where I have a simple GUI with an canvas. On this canvas I draw a Matplot. The Matplot is updated every second with data from an SQ Lite DB which I fill with some fake Sensor information (just for testing at the moment).
My Problem was that the redrawing of the canvas causes my window/gui to lag every second. I even tried to update the plot in another thread. But even there I get an lag.
With my newest Code i got most of my things working. Threading helps to prevent my GUI/Window from freezing while the Canvas is updated.
The last thing I miss is to make it Thread safe.
This is the message I get:
RuntimeError: main thread is not in main loop
Here is my newest working code with threading:
from tkinter import *
import random
from random import randint
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
from datetime import datetime
continuePlotting = False
def change_state():
global continuePlotting
if continuePlotting == True:
continuePlotting = False
else:
continuePlotting = True
def data_points():
yList = []
for x in range (0, 20):
yList.append(random.randint(0, 100))
return yList
def app():
# initialise a window and creating the GUI
root = Tk()
root.config(background='white')
root.geometry("1000x700")
lab = Label(root, text="Live Plotting", bg = 'white').pack()
fig = Figure()
ax = fig.add_subplot(111)
ax.set_ylim(0,100)
ax.set_xlim(1,30)
ax.grid()
graph = FigureCanvasTkAgg(fig, master=root)
graph.get_tk_widget().pack(side="top",fill='both',expand=True)
# Updated the Canvas
def plotter():
while continuePlotting:
ax.cla()
ax.grid()
ax.set_ylim(0,100)
ax.set_xlim(1,20)
dpts = data_points()
ax.plot(range(20), dpts, marker='o', color='orange')
graph.draw()
time.sleep(1)
def gui_handler():
change_state()
threading.Thread(target=plotter).start()
b = Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
b.pack()
root.mainloop()
if __name__ == '__main__':
app()
Here the idea without a thread:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import sqlite3
from datetime import datetime
from random import randint
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
root.update_idletasks()
f = Figure(figsize=(5,5), dpi=100)
x=1
ax = f.add_subplot(111)
line = ax.plot(x, np.sin(x))
def animate(i):
# Open Database
conn = sqlite3.connect('Sensor_Data.db')
c = conn.cursor()
# Create some fake Sensor Data
NowIs = datetime.now()
Temperature = randint(0, 100)
Humidity = randint(0, 100)
# Add Data to the Database
c = conn.cursor()
# Insert a row of data
c.execute("insert into Sensor_Stream_1 (Date, Temperature, Humidity) values (?, ?, ?)",
(NowIs, Temperature, Humidity))
# Save (commit) the changes
conn.commit()
# Select Data from the Database
c.execute("SELECT Temperature FROM Sensor_Stream_1 LIMIT 10 OFFSET (SELECT COUNT(*) FROM Sensor_Stream_1)-10")
# Gives a list of all temperature values
x = 1
Temperatures = []
for record in c.fetchall():
Temperatures.append(str(x)+','+str(record[0]))
x+=1
# Setting up the Plot with X and Y Values
xList = []
yList = []
for eachLine in Temperatures:
if len(eachLine) > 1:
x, y = eachLine.split(',')
xList.append(int(x))
yList.append(int(y))
ax.clear()
ax.plot(xList, yList)
ax.set_ylim(0,100)
ax.set_xlim(1,10)
ax.grid(b=None, which='major', axis='both', **kwargs)
label = tk.Label(root,text="Temperature / Humidity").pack(side="top", fill="both", expand=True)
canvas = FigureCanvasTkAgg(f, master=root)
canvas.get_tk_widget().pack(side="left", fill="both", expand=True)
root.ani = animation.FuncAnimation(f, animate, interval=1000)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
Here is my DB Schema:
CREATE TABLE `Sensor_Stream_1` (
`Date` TEXT,
`Temperature` INTEGER,
`Humidity` INTEGER
);
Your GUI process must not run in any thread. only the dataacquisition must be threaded.
When needed , the data acquired are transfered to the gui process (or the gui process notified from new data available) . I may need to use a mutex to share data resource between acquisition thread and gui (when copying)
the mainloop will look like :
running = True
while running:
root.update()
if data_available:
copydata_to_gui()
root.quit()
I had the same problem with tkinter and using pypubsub events was my solution.
As comments above suggested, you have to run your calculation in another thread, then send it to the gui thread.
import time
import tkinter as tk
import threading
from pubsub import pub
lock = threading.Lock()
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.label = tk.Label(root, text="Temperature / Humidity")
self.label.pack(side="top", fill="both", expand=True)
def listener(self, plot_data):
with lock:
"""do your plot drawing things here"""
self.label.configure(text=plot_data)
class WorkerThread(threading.Thread):
def __init__(self):
super(WorkerThread, self).__init__()
self.daemon = True # do not keep thread after app exit
self._stop = False
def run(self):
"""calculate your plot data here"""
for i in range(100):
if self._stop:
break
time.sleep(1)
pub.sendMessage('listener', text=str(i))
if __name__ == "__main__":
root = tk.Tk()
root.wm_geometry("320x240+100+100")
main = MainApplication(root)
main.pack(side="top", fill="both", expand=True)
pub.subscribe(main.listener, 'listener')
wt = WorkerThread()
wt.start()
root.mainloop()
This function is called every second, and it is outside the normal refresh.
def start(self,parent):
self.close=False
self.Refresh(parent)
def Refresh(self,parent):
'''your code'''
if(self.close == False):
frame.after( UpdateDelay*1000, self.Refresh, parent)
The function is called alone, and everything that happens inside it does not block the normal operation of the interface.

Categories

Resources