how to use blit=true for animation subplot in python tkinter - python-3.x

The following code is a simplified version of my project, and it could run and plot with animation, but I have to add blit=true in the animation to speed up (my original UI is much more complicated and slow). I know the animation function must return a sequence of Artist objects. here what is the artist object in my subplot_2? I tried subplot_2,a,f, none of them work. Many thanks
from multiprocessing import Process
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
##, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import tkinter as tk
from tkinter import ttk
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
f = Figure(figsize=(6,6), dpi=100)
subplot_1 = f.add_subplot(211)
subplot_2 = f.add_subplot(212)
LARGE_FONT= ("Verdana", 12)
style.use("ggplot")
def animate(i):
time = np.random.rand(2, 25)
data = np.random.rand(2, 25)
a = subplot_2.scatter(time,data,c='blue',s=2)
#return a
class home(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Graph ")
self.geometry("1000x1000")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
F=Graph
frame=Graph(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Graph)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def get_frame(self, frame_class):
return self.frames[frame_class]
##
class Graph(tk.Frame):
def __init__(self, parent, controller):
global canvas
tk.Frame.__init__(self, parent)
label = tk.Label(self, text=" ", font=LARGE_FONT)
label.pack(pady=120,padx=10)
collectbtn=Button(self,text='collect',command=self.clickstart)
collectbtn.place(x=200,y=100)
canvas = FigureCanvasTkAgg(f, self)
## canvas.draw()
canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
def clickstart(self):
animate(0)
#aniplot_photon = animation.FuncAnimation(f, animate, blit=True,interval=100)
aniplot_photon = animation.FuncAnimation(f, animate, interval=100)
canvas.draw()
app = home()
app.mainloop()
Updates:
Thanks to the William's answer, it works. However, I have further questions, if this subplot include many curves "subplot_2.scatter(x1..),subplot_2.scatter(x2..),subplot_2.scatter(x3..),subplot_2.scatter(x1..)...", or some subplots are updated from different threads (make objects and global them?), it will be hard to put all curves into return list. However, I found a simple solution for the above listed problems, I could just use return [subplot_2] and it will update all plots inside subplot_2, but the coordinates(x ticks) are lost (run and you will see). is there a simple way I could keep the coordinates when I use return [suplot_2]?
def animate(i):
time = np.random.rand(2, 25)
data = np.random.rand(2, 25)
a = subplot_2.scatter(time,data,c='blue',s=2)
return [subplot_2]

matplotlib.pyplot.scatter returns an artist (a PathCollection to be exact), so the artist you need to return from animate is stored in a. However, FuncAnimation requires a sequence of artists, so
return a
is insufficient (as this is a single artist, not a sequence). You can use one of
return [a]
or
return a,
to satisfy the "sequence of artists" requirement.

Related

update the plot in animation for tkinter in python

I have a complicated program and have problem to update the curve in animation, so I simplified the program and posted a similar post before previous post, but that example is simplified too much and could not represent the actual problem. So that example got fixed but the actual program still does not work. Here I made some changes and post the code again.
The program will try to update a straight line twice at time = 3 seconds and 5 seconds , the problem is
if I do not define the xlim and ylim, then if the update is out of range, we could not see it; if I define the xlim and ylim, then the animation will plot two different curves every time I use canvas.draw(). I also tried subplot1.clear() and replot, but same thing. when I set blit=False, everything is ok, but I have to use blit=true. It looks animation keeps two sets of data in the memory, any way to delete the old data?
from multiprocessing import Process
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
##, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import tkinter as tk
from tkinter import ttk
from tkinter import *
import matplotlib.pyplot as plt
import numpy as np
import time
f = Figure(figsize=(6,6), dpi=100)
subplot_1 = f.add_subplot(211)
subplot_2 = f.add_subplot(212)
a_count = 0
b_count = 0
LARGE_FONT= ("Verdana", 12)
style.use("ggplot")
count_plot = 0
first_replot,second_replot = 0,0
start_time = 0
draw_replot = 0
a_t = [1,2,3,4,5]
a_T = np.ones(5)
canvas_draw_timer = 0
def replot(update_flag):
global a_t, count_plot,theory_line,a_T
if update_flag == 1:
a_t = [3,4,5,6,7]
a_T = np.ones(5)*2
theory_line[0].set_data(a_t,a_T)
elif update_flag == 2:
a_t = [5,6,7,8,9]
a_T = np.ones(5)*1.5
theory_line[0].set_data(a_t,a_T)
else:
theory_line = subplot_1.plot(a_t,a_T,'c-')
canvas.draw()
def animate(i):
global a_count,b_count,first_replot,second_replot,canvas_draw_timer
time1 = np.random.rand(2, 25)
data = np.random.rand(2, 25)*2
#first time update the canvas
if (time.time() - start_time > 3) and (first_replot == 0):
replot(1)
first_replot = 1
#second time update the canvas
if (time.time() - start_time > 5) and (second_replot == 0):
replot(2)
second_replot = 1
#refresh the canvas per second, just example, many other places need to use canvas.draw() later
if time.time() - canvas_draw_timer > 1:
canvas.draw()
canvas_draw_timer = time.time()
a = subplot_1.scatter(time1,data,c='blue',s=2)
return a,
class home(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Graph ")
self.geometry("1000x1000")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
F=Graph
frame=Graph(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Graph)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def get_frame(self, frame_class):
return self.frames[frame_class]
##
class Graph(tk.Frame):
def __init__(self, parent, controller):
global canvas
tk.Frame.__init__(self, parent)
label = tk.Label(self, text=" ", font=LARGE_FONT)
label.pack(pady=120,padx=10)
collectbtn=Button(self,text='collect',command=self.clickstart)
collectbtn.place(x=200,y=100)
canvas = FigureCanvasTkAgg(f, self)
canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
def clickstart(self):
global theory_line,start_time
theory_line = subplot_1.plot(a_t,a_T,'c-')
#set the large x and y range first, otherwise blit = true could not plot out of range plots
f.axes[0].set_xlim([0,10])
f.axes[0].set_ylim([0,5])
start_time = time.time()
aniplot_photon = animation.FuncAnimation(f, animate, blit=True,interval=100)
canvas.draw()
app = home()
app.mainloop()

A Single Tkinter Window Which Moves Through Frames, Bringing Each Frame to the Foreground. One Frame needs to be split into two Sub-frames

The need for two different geometric plotters arises because i want to display a sophisticated GUI with text, entry fields, pictures, buttons, alongside an animated matplotlib graph on a FigureCanvasTkAgg with a NavigationToolbar2Tk. The NavigationToolbar2Tk fails to work when any geometric plotter other than pack() is used. I have tried over the course of the week several different methods of putting the NavigationToolbar2Tk into its own frame, but in the examples, only a single frame exists. I came to the conclusion that ideally, splitting my frame ~ PageOne ~ into two subframes or instantiating two frames which make up page one.
I have several frames as shown in my code below and my knowledge of python is rudimentary so i don't have the imagination or know how to circumvent this problem. I have gutted my code to show the problem concisely and Included my Imports.
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib.pylab import *
from matplotlib import style
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import matplotlib.ticker as mticker
import tkinter as tk
from tkinter import ttk
from tkinter import *
from PIL import Image, ImageTk
from mpl_toolkits.axes_grid1 import host_subplot
from gpiozero import CPUTemperature
import time
import datetime as dt
import pandas as pd
import numpy as np
#************************************************************************#
# Format Graph_1 [ax1] onto fig1 at subplot [ row = 1, col = 1, index = 1 ]
# Set Figure Text
font = {'size' : 9}
matplotlib.rc('font', **font)
# Setup Figure
fig1 = Figure()
# Define Axis 1 for Vin and Vout
ax1 = fig1.add_subplot(1, 1, 1)
#subplots_adjust(left=0.05, bottom=0.10, right=0.55, top=0.8, wspace=0.2)
ax1.minorticks_on()
ax1.grid(b=True, which='major', color='k', linestyle='-')
ax1.grid(b=True, which='minor', color='k', linestyle=':')
ax1.set_title("PI3740 Paramaters", fontsize = 12)
ax1.set_xlabel("Relative Time (s)", fontsize = 10)
ax1.set_ylabel("Voltage (V)", fontsize =10)
# Define Axis 2 for Iout Which is tied to Axis 1's X-Axis
ax2 = ax1.twinx()
ax2.set_ylabel("Output Current (A)")
# Parameters
x_len = 500 # Resolution [Number of Points in Window]
x_max = 2 # X-Axis Range [ (Step)ms Samp^-1 * (x_len)Samp = X_Range]
y_range = [0, 50] # Range of possible Y values to display
# Create figure for plotting
steps = (x_max/x_len)
stepms = steps * 1000
xs = np.arange(0, x_max, steps) # xs is a list from 0 to 10 in steps of 0.01 [A list refers to a 1D Array]
ys1 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vin
ys2 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Vout
ys3 = [0] * x_len # ys is a list indexed from ys[0] to ys[999] all containing 0 # Iout
ax1.set_ylim(y_range) # Y-Axis Voltage Range Set
ax2.set_ylim(0, 10) # Y-Axis Current Range Set
ax1.set_xlim(0, x_max) # X-Axis Shared Relative Time Range Set
# Create a blank line. We will update the line in animate
line1, = ax1.plot(xs, ys1, 'b-', label = "Vin")
line2, = ax1.plot(xs, ys2, 'g-', label = "Vout")
line3, = ax2.plot(xs, ys3, 'r-', label = "Iout")
# Create a Legend
ax1.legend([line1, line2],[line1.get_label(), line2.get_label()])
ax1.legend(bbox_to_anchor = (0.,0.99,1.,.102), loc = 3, ncol = 2, borderaxespad = 0., frameon = False)
ax2.legend([line3],[line3.get_label()])
ax2.legend(bbox_to_anchor = (1.00,0.99), loc = 'lower right', borderaxespad = 0., frameon = False)
#************************************************************************#
#**********************Animation Function********************************#
# This function is called periodically from FuncAnimation
def updateData(self):
# Drop down menu event flags
global ChartLoad
# Graph variables
global xs
global ys1
global ys2
global ys3
if ChartLoad == True:
# Read temperature (Celsius) from TMP102
temp_c = cpu.temperature
temp_c1 = temp_c + 5.0
temp_c2 = temp_c - 35.0
# Add y to list
ys1.append(temp_c)
ys2.append(temp_c1)
ys3.append(temp_c2)
# Limit y list to set number of items
ys1 = ys1[-x_len:]
ys2 = ys2[-x_len:]
ys3 = ys3[-x_len:]
# Update line with new Y values
line1.set_ydata(ys1)
line2.set_ydata(ys2)
line3.set_ydata(ys3)
return line1, line2, line3,
#************************************************************************#
#*******************Tkinter Window Initalization*************************#
class MyApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self,"EMEA Vicor Charging Application ")
img=tk.PhotoImage(file='/home/pi/Pictures/Vicor_Icon1.png')
self.tk.call('wm','iconphoto',self._w,img)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#*********************************************************
#******Function Required to Display Seperate Pages********
self.frames = {}
for F in (StartPage, HomePage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
#*********************************************************
#*********************************************************
#Start Page - Agreement
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#Page One - Primary Terminal
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
tk.Frame.__init__(self, parent, bg = "white")
# Two Frames Need to be Established on Page One (Which is a “Higher Order Frame”)
my_window = Tk()
frame_name = Frame(my_window)
frame_addr = Frame(my_window)
label_f = Label(frame_name, text = "Check")
label_f.grid(row = 0, column = 0)
label_g = Label(frame_addr, text = "Correct")
label_g.grid(row = 0, column = 0)
frame_name.grid(row = 0, column = 0)
frame_addr.grid(row = 0, column = 1)
#my_window.mainloop()
app = MyApp()
app.geometry("1010x700")
ani = animation.FuncAnimation(fig1, updateData, blit = True, interval = stepms)
app.mainloop()
Page One will contain my Canvas for the Graph and Tool Bar. however to simplify the problem into its fundamental, getting two frames to make up "Higher Order" frame PageOne, with each subframe containing a label. When I run the code, another window opens with the two labels displayed. This is more progress than from other solutions that i can't implement/don't understand, producing error messages i can't respond to. I am self taught at python, and have been following tutorials from the community. I just need some help implementing the solution. If I've gutted my code to much i can provide a more comprehensive code snippet. But the essence of the problem is, while displaying one of several frames on a Tkinter Window which move to the foreground when requested, how do you split one of these "High Order" frames into two frames as to allow two different geometric plotters to be used to structure each one.
Resources Used So Far:
https://www.youtube.com/watch?v=Mxk4cMBaH3g&list=PL6lxxT7IdTxGoHfouzEK-dFcwr_QClME_&index=37&t=0s
[Most Recent Attempt - Trying to Simplify the problem]
how to make two split up screen ( canvas) inside the python tkinter window
http://www.openbookproject.net/courses/python4fun/tkphone1.html
matplotlib Navigation Bar error 'FigureCanvasTkAgg' object has no attribute 'manager' in tkinter
Displaying Matplotlib Navigation Toolbar in Tkinter via grid
[Hit the Same Problem - One Master Frame Split Into Two Sub Frames]
[This Link is probably has the answer in it and if that is the case, apologies for repeating a question already asked on stack overflow)
EDIT. To Expand Further for Clarity take the code below:
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
how would i get PageTwo to compose of two different frames so i can use two different geometric plotters (pack() and grid()).

How to update matplotlib embedded into tkinter?

The problem is that I want to draw a plot by clicking on a button but it doesn't work. However, when I call draw from __init__, the plot appears on the screen.
Plotter.py
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class Plotter(FigureCanvasTkAgg):
def __init__(self, master):
self.figure = Figure(dpi=100)
super().__init__(self.figure, master=master)
self.axes = self.figure.add_subplot(111)
self.get_tk_widget().grid(column=0, row=0, sticky='nsew')
def draw(self):
self.axes.clear()
x_list = [x for x in range(0, 100)]
y_list = [x^3 for x in x_list]
self.axes.plot(x_list, y_list, color='y')
MainApplication.py
from tkinter import ttk
import tkinter as tk
import plotter
class MainApplication(ttk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(root)
self.grid(column=0, row=0, sticky='nsew')
frame = ttk.Frame(self, borderwidth=8)
frame.grid(column=0, row=0, sticky='nsew')
frame.rowconfigure(0, weight=1)
notes = ttk.Notebook(frame)
notes.grid(column=0, row=0, sticky='nsew')
notes.rowconfigure(0, weight=1)
page = ttk.Frame(notes)
notes.add(page, text='Picture')
plot = plotter.Plotter(page)
# plot.draw() # This call updates the plot
input_frame = ttk.Frame(self)
input_frame.grid(column=1, row=0, sticky='nsew')
# this binding doesn't update the plot
button = ttk.Button(input_frame, text='Plot', \
command=lambda: plot.draw())
button.grid(column=0, row=4, columnspan=2, sticky='ew')
root = tk.Tk()
MainApplication(root)
root.mainloop()
Personally I would write this up in a single class so that we can use class attributes and methods to control everything with ease. Also you do not need a lambda here. Just save the reference to the command button and not a lambda call. That said you were also overwriting the draw method of FigureCanvasTkAgg so change the draw() method to something else.
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import ttk
import tkinter as tk
class MainApplication(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
notes = ttk.Notebook(self)
notes.grid(column=0, row=0, sticky='nsew')
notes.rowconfigure(0, weight=1)
self.page = ttk.Frame(notes)
notes.add(self.page, text='Picture')
self.plotter()
input_frame = ttk.Frame(self)
input_frame.grid(column=1, row=0, sticky='nsew')
button = ttk.Button(input_frame, text='Plot', command=self.new_draw)
button.grid(column=0, row=4, columnspan=2, sticky='ew')
def plotter(self):
self.figure = Figure(dpi=100)
self.plot_canvas = FigureCanvasTkAgg(self.figure, self.page)
self.axes = self.figure.add_subplot(111)
self.plot_canvas.get_tk_widget().grid(column=0, row=0, sticky='nsew')
def new_draw(self):
self.axes.clear()
x_list = [x for x in range(0, 100)]
y_list = [x^3 for x in x_list]
self.axes.plot(x_list, y_list, color='y')
self.plot_canvas.draw_idle()
MainApplication().mainloop()
You overwrote the canvas' draw method without reimplementing it. But since you do not want to update your plot on every draw-event anyways, I'd suggest to call the method to update the plot differently, e.g. draw_lists. Inside draw_lists you would then need to call the draw method of the canvas (or in this case better draw_idle).
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class Plotter(FigureCanvasTkAgg):
def __init__(self, master):
self.figure = Figure(dpi=100)
super().__init__(self.figure, master=master)
self.axes = self.figure.add_subplot(111)
self.get_tk_widget().grid(column=0, row=0, sticky='nsew')
def draw_lists(self):
self.axes.clear()
x_list = [x for x in range(0, 100)]
y_list = [x^3 for x in x_list]
self.axes.plot(x_list, y_list, color='y')
self.draw_idle()
from tkinter import ttk
import tkinter as tk
class MainApplication(ttk.Frame):
def __init__(self, master, *args, **kwargs):
super().__init__(root)
self.grid(column=0, row=0, sticky='nsew')
frame = ttk.Frame(self, borderwidth=8)
frame.grid(column=0, row=0, sticky='nsew')
frame.rowconfigure(0, weight=1)
notes = ttk.Notebook(frame)
notes.grid(column=0, row=0, sticky='nsew')
notes.rowconfigure(0, weight=1)
page = ttk.Frame(notes)
notes.add(page, text='Picture')
plot = Plotter(page)
input_frame = ttk.Frame(self)
input_frame.grid(column=1, row=0, sticky='nsew')
# this binding doesn't update the plot
button = ttk.Button(input_frame, text='Plot', \
command=lambda: plot.draw_lists())
button.grid(column=0, row=4, columnspan=2, sticky='ew')
root = tk.Tk()
MainApplication(root)
root.mainloop()

Display an array as an image Tkinter

I'm attempting to display an image from a compressed DICOM data array using tkinter. I compressed the image to an 8-bit numpy array. I know that the array can be visualized, as I have visualized it using both cv2 and matplotlib. Below is how I created the data:
import cv2
import numpy as np
import pydicom #https://pydicom.github.io/pydicom/stable/api_ref.html
import os
import glob
from PIL import Image, ImageTk
import PIL
import tkinter as tk
from pathlib import Path
path = Path("C:/Users/H61972/Desktop/1-1058/DICOM")
os.chdir( path )
os.getcwd()
print(__doc__)
#Get dicom files sorted by filename
def get_dicom():
return glob.glob("**/IM*",recursive=True)#slices
#return one file to be read at a time
def load_image(dicom):
ds = pydicom.read_file(dicom)
#print(ds.SliceLocation)
data = np.array(ds.pixel_array)
#data = data - np.min(data)
x = np.max(data)/255
data = data/x
data = np.clip(data, 0, 255)
return data
dicom = get_dicom()
def process_frame():
global data
frame = load_image(dicom[10])
frame = cv2.equalizeHist(frame)
frame = cv2.blur(frame,(5,5))
return frame
And below is the Tkinter gui I am building:
class mainWindow():
def __init__(self):
self.root = tk.Tk()
self.frame = tk.Frame(self.root, width=500, height=400)
self.frame.pack()
self.canvas = tk.Canvas(self.frame, width=500,height=400)
self.canvas.place(x=-2,y=-2)
data= process_frame()
self.im=Image.frombytes('L', (data.shape[1],data.shape[0]), data.astype('b').tostring())
self.photo = ImageTk.PhotoImage(image=self.im)
self.canvas.create_image(0,0,image=self.photo,anchor=tk.NW)
self.root.update()
self.root.mainloop()
mainWindow()
Any advice would be much appreciated!
I found a solution to most of my problem, however I have created a new problem in doing so. Now when I run the program I create an extra window. Here are my changes:
class DICOM(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)#initialized tkinter
container = tk.Frame(self)#define our tkinter container
container.pack(side ="top", fill="both", expand=True)
container.grid_rowconfigure(0,weight=1)
container.grid_columnconfigure(0,weight=1)
self.frames = { }
#for F in (StartPage, Viewer):
frame = mainWindow(container, self)
self.frames[mainWindow] = frame
frame.grid(row =0, column =0, sticky ="nsew")
self.show_frame(mainWindow)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class mainWindow(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.root = tk.Toplevel(class_=self.controller)
# self.frame = tk.Frame(self.root)
# self.frame.pack()
self.canvas = tk.Canvas(self.root)
self.canvas.place(x=0,y=0)
data= process_frame()
self.im=Image.frombytes('L', (data.shape[1],data.shape[0]), data.astype('b').tostring())
self.photo = ImageTk.PhotoImage(image=self.im)
self.canvas.create_image(0,0,image=self.photo,anchor=tk.NW)
self.root.update()
#self.root.mainloop()
app = DICOM()
app.geometry("800x800")
app.title("Head CT Volumetric Analysis")
app.config(bg="gray")
app.mainloop()
How can I prevent this extra window from appearing?

python - calling plt.figure prevents Checkbutton from update - Tkinter

A GUI that works with different pages, one is a graph page using the animate function, and a second page should start a guide, the guide will depend on what you click in a checkbutton. I have created a post because I thought I was the checkbutton that was not well defined, but now i found that calling the f=plt.figure() is prevent the upgrade of the checkbutton.
See this code works for the Checkbutton: - Before the animate function .. But then the graphic update on the other page does not work since f is not defined in the animate function. If i put the animate function and f definition in the PageThree (GRAPHIC page) the update of the figure fails.
But when i allow the f=plt.figure() , graphic is fine, but checkbutton stays False all the time...
import matplotlib
from tkinter import *
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
import tkinter as tk
from tkinter import ttk
import numpy as np
import matplotlib.animation as animation
from matplotlib import style
from matplotlib import pyplot as plt
from scipy.misc import *
import pandas as pd
import webbrowser
import subprocess
import csv
import matplotlib.ticker as mticker
import matplotlib.dates as mdates
from os import startfile
from time import time
LARGE_FONT = ("Times", 11, "bold italic")
NORM_FONT = ("Helvetica", 9)
SMALL_FONT = ("Helvetica",7)
HELP_FONT=("Times", 9 , "bold")
pullDataMS=pd.read_table("test.raw.spectrum_table.tsv", header=1)
LevelMS=pullDataMS.groupby('msLevel')
rt=pullDataMS['rt']
TIC=pullDataMS['TIC']
MS1=LevelMS.get_group('ms1')
MS2=LevelMS.get_group('ms2')
### THIS CALL overplots the update? -.- ###
#f = plt.figure(facecolor="white")**
######################
def animate (i):
pullData=pd.read_table("InstrumentPerformance.txt", parse_dates=True,
dayfirst=True, index_col=1)
Names_In = ['Q1','Q2','Q3']
Inst=pullData.groupby('Instrument')
a = f.add_subplot(2,3,1)
a2 = f.add_subplot(2,3,2, sharex=a)
a3=f.add_subplot(2,3,3)
a4=f.add_subplot(2,3,4)
a5=f.add_subplot(2,3,5)
a7=f.add_subplot(2,6,11)
f.subplots_adjust(wspace=0.29)
a.clear()
a2.clear()
a3.clear()
a4.clear()
a5.clear()
a7.clear()
for Name in Names_In:
Q=Inst.get_group(Name)
y=Q['MS/MS recorded']
x=Q['Identified peptides']
a.plot(x, "o",label='['+Name+"]")
a.plot(x, 'k-', lw=0.7, label='_nolegend_')
a2.plot(y, 'o',label='['+Name+']')
a2.plot(y, 'k-', label='_nolegend_', lw=0.7)
a.xaxis.set_major_locator(mticker.MaxNLocator(5))
a.xaxis.set_major_formatter(mdates.DateFormatter("%d-%m-%Y"))
a.set_ylabel('# of unique peptide sequences')
a2.set_ylabel('# of MS/MS spectra')
a.legend(loc='upper left',fancybox=True, numpoints=1, shadow=True,prop={'size':9})
a2.legend(loc='upper left',fancybox=True, numpoints=1, shadow=True,prop={'size':9})
a.set_xlim(np.array([-20,20])+a.get_xlim())
a.set_ylim(np.array([0,2000])+a.get_ylim())
a2.set_xlim(np.array([-20,20])+a.get_xlim())
a2.set_ylim(np.array([0,5000])+a.get_ylim())
class PAL3_guide(tk.Tk):
def __init__(self,*args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage,Tut01,PageThree):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame=self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self,parent, controller):
tk.Frame.__init__(self,parent, background="white")
button_PAL = ttk.Button(self, text="Setup Guide", command=lambda:controller.show_frame(Tut01))
button_PAL.pack()
button2 = ttk.Button(self, text="Graph Page 1",
command=lambda: controller.show_frame(PageThree))
button2.pack()
class Tut01(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.parent=parent
self.initUI()
def initUI(self):
self.var=BooleanVar()
cb=Checkbutton(self,text="show", variable=self.var, command=self.onClick)
cb.select()
cb.place(x=50,y=50)
def onClick(self):
if self.var.get()== True:
print("hi")
else:
print("buh")
class PageThree(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="GraphPage", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
canvas = FigureCanvasTkAgg(f, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
toolbar=NavigationToolbar2TkAgg(canvas,self)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP,fill=tk.BOTH, expand=True)
app = PAL3_guide()
app.geometry("1280x920")
ani = animation.FuncAnimation(f, animate, interval=10000)
app.mainloop()
EDIT FOR MIXONE: yes i have tried to do something like :
def animate (f):
pullData=pd.read_table("InstrumentPerformance.txt", parse_dates=True,
dayfirst=True, index_col=1)
Names_In = ['Q1','Q2','Q3']
Inst=pullData.groupby('Instrument')
a = f.add_subplot(2,3,1)
a2 = f.add_subplot(2,3,2, sharex=a)...
...
and:
class PageThree(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="GraphPage", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
f=plt.figure()
ani = animation.FuncAnimation(f, animate(f), interval=10000)
canvas = FigureCanvasTkAgg(f, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
#toolbar=NavigationToolbar2TkAgg(canvas,self)
#toolbar.update()
#canvas._tkcanvas.pack(side=tk.TOP,fill=tk.BOTH, expand=True)
app = PAL3_guide()
app.geometry("1280x920")
app.mainloop()
In this case the Checkbutton works, but the figure is not updated but it is plotted in the beginning.

Resources