Display an array as an image Tkinter - python-3.x

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?

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()

how to use blit=true for animation subplot in python tkinter

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.

Memory used increases with tkinter notebook and matplotlib

I've got a small program that creates a GUI using tkinter. It contains a button that loads a .csv file, creates a notebook with as many tabs as columns in the csv file. Then, on every active tab (at least this is my intention) I have a plot created from a Figure.
The program works as expected , the only problem is that when switching Tabs, the memory used increases with each Tab click.
Memory usage was monitored using the Windows Task Manager.
After loading a csv file, I didn't see the used memory dropping when I chose not loading a new file.
If I don't call the plotting function, when creating only the Tabs, there is no memory issue.
I already tried to manually invoke the garbage collector with gc.collect(), but that didn't help. This is the code I have:
import matplotlib
matplotlib.use('TkAgg')
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import os
import sys
import tkinter as tk
from tkinter import messagebox as msg
from tkinter import ttk, filedialog
##import gc
class Graphs(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side='top', fill='both', expand=1)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.protocol('WM_DELETE_WINDOW', self._destroyWindow)
self.frames = {}
frame = StartPage(parent=container, controller=self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def _destroyWindow(self):
self.quit() # stops mainloop
self.destroy()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# initialize lists
self.tabs_list = []
self.hdrs = []
self.figs_list = []
self.ax_list = []
self.canvas_list = []
self.toolbars_list = []
# initialize Data Frame
self.df = pd.DataFrame()
self.nb = None
self.canvas = None
self.toolbar = None
# create LOAD button
self.btn = tk.Button(self, text = 'Load file', command=self.load_csv)
self.btn.pack()
def load_csv(self):
'''
Reset Data Frame;
Destroy notebook if exists;
Load CSV file.
'''
# reset Data Frame
self.df = pd.DataFrame()
# destroy notebook if exists
if self.nb:
self.nb.pack_forget()
self.nb.destroy()
self.nb = None
## gc.collect()
# Select CSV file
self.file_path = filedialog.askopenfilename()
if not self.file_path:
msg.showinfo('Select CSV file', "No file chosen.")
return
try:
# read csv file (exemple.csv)
self.df = pd.read_csv(self.file_path, header=0)
except:
msg.showinfo('Select CSV file', 'Not a csv file / corrupt file.')
return
print(self.df.head())
print(self.df.shape)
# get dimensions
self.m, self.n = self.df.shape
# build the abscissa x from first column
self.x = self.df.iloc[:,0]
# create the notebook
self.nb = ttk.Notebook(self)
# allow Tab navigation
self.nb.enable_traversal()
# add Tabs
for k in range(1, self.n):
hdr = self.df.columns[k]
self.hdrs.append(hdr)
tab = tk.Frame(self.nb, name=hdr.lower())
self.nb.add(tab, text=hdr)
self.tabs_list.append(tab)
self.nb.pack(fill='both', expand=1)
# virtual event after a new tab is selected
self.nb.bind("<<NotebookTabChanged>>", self.plotTH)
def plotTH(self, event):
'''
Plot each Column from Data Frame on its own Tab/Figure
'''
# get path of the selected Tab
tab_path = event.widget.nametowidget(event.widget.select())
# add selected Tab to the list of Tabs
self.tabs_list.append(tab_path)
# get the Tab index;
# When there are no tabs, .select() returns an empty string,
# but .index('current') throws an exception;
# nb.select() returns the Tab NAME (string) of the current selection
if self.nb.select():
i = self.nb.index('current')
# get the Tab text
tab_text = self.nb.tab(i)['text']
else:
return
# remove previous figures ... not sure...
# the used memory as seen in Task Manager still increases
if self.canvas_list:
for cnv in self.canvas_list:
cnv.figure.get_axes().clear()
cnv.get_tk_widget().pack_forget()
cnv.get_tk_widget().destroy()
cnv._tkcanvas.pack_forget()
cnv._tkcanvas.destroy()
cnv = None
if self.figs_list:
for fig in self.figs_list:
fig.delaxes(fig.gca())
plt.cla()
fig.clf()
fig.clear()
plt.close(fig)
self.figs_list = []
# remove toolbar
for widget in tab_path.winfo_children():
widget.pack_forget()
widget.destroy()
self.nb.update() #!!!!!!!!!!!!
######## gc.collect()
# prepare plotting
fig = Figure(figsize=(7, 5), dpi=100)
ax = fig.add_subplot(111)
ax.plot(self.x, self.df.iloc[:,i+1], 'b-', linewidth=1, label=tab_text)
ax.set_xlabel('index')
ax.set_title(self.hdrs[i], fontsize = 8)
ax.legend(loc='best')
ax.grid()
# add to list of figures
self.figs_list.append(fig)
# add to list of axes
self.ax_list.append(ax)
canvas = FigureCanvasTkAgg(fig, master=tab_path)
# add to list of canvases
self.canvas_list.append(canvas)
## self.canvas.draw()
canvas.draw_idle()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, tab_path)
# add to list of toolbars
self.toolbars_list.append(toolbar)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def clearPlot(self):
""" not used"""
pass
app = Graphs()
app.title('CSV Plots')
app.geometry('800x600+400+150')
app.resizable(True, True)
app.mainloop();
I created a working csv file using this code:
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(np.random.randn(50,4), columns=['ALPHA', 'BETA', 'GAMMA', 'DELTA'])
df.index.names = ['Rec']
df.index = df.index + 1
df.to_csv('example.csv', index=True)
print(df)
I'm sorry for the long post. I really don't know were to go from here, so any help would be greatly appreciated.
None of the following lists are ever purged; they hold references to large objects that cannot be garbage collected.
self.tabs_list = []
self.hdrs = []
self.figs_list = []
self.ax_list = []
self.canvas_list = []
self.toolbars_list = []
You probably should create an object that holds the data for each tab, and destroy/purge/reassign to None that when changing tabs.
I changed the code; I realized I was creating a new figure (with all the Artists) and a new canvas (FigureCanvasTkAgg) with every TabChanged event. Instead, I create a figure and its canvas at the moment of Tab addition to the notebook; I do the plot only when the TabChanged occurs and I take care to close all the plots and to destroy the previous notebook when performing a new CSV file load. Destroying the notebook removes the canvases too. Here is the code:
import matplotlib
import matplotlib.style as mplstyle
matplotlib.use('TkAgg')
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import os, sys, time
import tkinter as tk
from tkinter import messagebox as msg
from tkinter import ttk, filedialog
mplstyle.use('fast')
class Graphs(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side='top', fill='both', expand=1)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.protocol('WM_DELETE_WINDOW', self._destroyWindow)
self.frames = {}
frame = StartPage(parent=container, controller=self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def _destroyWindow(self):
self.quit() # stops mainloop
self.destroy()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# initialize Data Frame
self.df = pd.DataFrame()
self.nb = None
self.fig = None
self.canvas = None
self.toolbar = None
# create LOAD button
self.btn = tk.Button(self, text = 'Load file', command=self.load_csv)
self.btn.pack()
def load_csv(self):
''' Close the plots;
Reset Data Frame;
Destroy notebook if exists;
Load CSV file.
'''
# Setting interactive mode off
if plt.isinteractive():
plt.ioff()
plt.close("all")
# reset Data Frame
self.df = pd.DataFrame()
# initialize list
self.hdrs = []
try:
# destroy notebook if exists
self.nb.pack_forget()
self.nb.destroy()
except:
pass
self.nb = None
# Select CSV file
self.file_path = filedialog.askopenfilename()
if not self.file_path:
msg.showinfo('Select CSV file', "No file chosen.")
return
try:
# read csv file (exemple.csv)
self.df = pd.read_csv(self.file_path, header=0)
except:
msg.showinfo('Select CSV file', 'Not a csv file / corrupt file.')
return
# get dimensions
self.m, self.n = self.df.shape
# build the abscissa x from first column
self.x = self.df.iloc[:,0]
# create the notebook
self.nb = ttk.Notebook(self)
# allow Tab navigation
self.nb.enable_traversal()
# add Tabs
for k in range(1, self.n):
hdr = self.df.columns[k]
self.hdrs.append(hdr)
tab = tk.Frame(self.nb, name=hdr.lower())
self.nb.add(tab, text=hdr)
self.fig = plt.figure(num=hdr.lower(), clear=True, figsize=(7, 5), dpi=100)
# self.ax = self.fig.add_subplot()
self.canvas = FigureCanvasTkAgg(self.fig, master=tab)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(self.canvas, tab)
toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.nb.pack(fill='both', expand=1)
# virtual event after a new tab is selected
self.nb.bind("<<NotebookTabChanged>>", self.plotTH)
def plotTH(self, event):
'''
Plot each Column from Data Frame on its own Tab/Figure
'''
# Setting interactive mode on is essential: plt.ion()
if not plt.isinteractive():
plt.ion()
# tab index
i = self.nb.index('current')
# tab text
tab_text = self.nb.select().split('.')[-1]
# set current figure
cf = plt.figure(tab_text)
plt.clf()
# plotting
ax = plt.subplot(111)
ax.plot(self.x, self.df.iloc[:,i+1], 'b-', linewidth=1, label=tab_text)
ax.set_xlabel('index')
ax.set_title(self.hdrs[i], fontsize = 8)
ax.legend(loc='best')
ax.grid()
cf.canvas.draw()
app = Graphs()
app.title('CSV Plots')
app.geometry('800x600+400+150')
app.resizable(True, True)
app.mainloop()

Delivering data between instances of different classes in tkinter

Beginner to tkinder and the version of python is 3.6.
I'm trying to make a python GUI for data processing.
There're several instances of different classes designed for different jobs.
When the data is processed with function click_emd in EMDFrame class, it should be drawn in PreviewFrame class which uses matplotlib to show the data. However i have no idea how to pass the data between two classes.
I've searched similar questions but they didn't work.
Now I'm thinking about two possible solutions.
One is to find a way to pass data_processed to previewframe.cplot in emdframe.click_emd.
Another is to make full use MainPage class. It can acquire data_processed from one class and call the cplot in another class to drawn. But how can MainPage get noticed once the data_processed is generated?
Got confused and don't know what to do. Really appreciate for your patience.
There are two py code file.
Mainframe.py :
import tkinter as tk
from view import * # initiate the subframes
class MainPage():
def __init__(self, master=None):
self.root = master
root.geometry('%dx%d' % (800, 600))
self.createPage()
def createPage(self):
self.emdPage = EMDFrame(root) # subframe
self.emdPage.place(x=20, y=300)
self.preview = PreviewFrame(root)
self.preview.place(x=340, y=20)
self.aboutPage = AboutFrame(root)
menubar = Menu(root)
menubar.add_command(label='EMD', command=self.emd)
menubar.add_command(label='About', command=self.aboutDisp)
root['menu'] = menubar # set menu
def emd(self):
self.emdPage.place(x=20, y=300)
self.preview.place(x=340, y=20)
self.aboutPage.place_forget()
def aboutDisp(self):
self.emdPage.place_forget()
self.preview.place_forget()
self.aboutPage.place(x=20, y=300)
def plot_update(self, data, t):
self.preview.cplot(data, t)
root = tk.Tk()
root.title("Demo")
app = MainPage(root)
root.mainloop()
view.py :
from tkinter import *
from pyhht.emd import EMD
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
data = []
class EMDFrame(LabelFrame):
def __init__(self, master=None):
LabelFrame.__init__(self, master, width=300, height=250, text='EMD')
self.root = master
self.createPage()
def createPage(self):
b = Button(self, text='EMD', width=20, height=2, command=self.click_emd)
b.place(x=40, y=160, anchor='nw', width=80, height=40)
def click_emd(self):
global data
decomposer = EMD(data)
data_processed = decomposer.decompose()
# Deliever data_processed to PreviewFrame's cplot function
class PreviewFrame(LabelFrame):
def __init__(self, master=None):
LabelFrame.__init__(self, master, width=440, height=530, text='Preview')
self.root = master
self.createPage()
def cplot(self, data, t):
f = Figure(figsize=(4, 4.8), dpi=100)
a = f.add_subplot(111)
a.plot(t, data)
canvas = FigureCanvasTkAgg(f, master=self)
canvas.draw()
canvas.get_tk_widget().place(x=20, y=0)
def createPage(self):
t = np.arange(0.0, 3, 0.01)
s = np.sin(2 * np.pi * t)
self.cplot(s,t)
class AboutFrame(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.root = master
self.createPage()
def createPage(self):
Label(self, text='About').pack()
You aren't passing data between classes, you are passing data between two instances of classes.
In order for one instance to send data to another instance of a class, it needs to know about it. So in MainFrame.py we pass the reference of the preview class to the emdPage.
def createPage(self):
self.preview = PreviewFrame(root)
self.preview.place(x=340, y=20)
self.emdPage = EMDFrame(root,self.preview) # subframe
self.emdPage.place(x=20, y=300)
self.aboutPage = AboutFrame(root)
Then in the other view.py, update the class to keep track of the preview reference and then use it once the data has been processed.
class EMDFrame(LabelFrame):
def __init__(self, preview, master=None):
LabelFrame.__init__(self, master, width=300, height=250, text='EMD')
self.preview = preview
self.root = master
self.createPage()
def createPage(self):
b = Button(self, text='EMD', width=20, height=2, command=self.click_emd)
b.place(x=40, y=160, anchor='nw', width=80, height=40)
def click_emd(self):
global data
decomposer = EMD(data)
data_processed = decomposer.decompose()
# Deliever data_processed to PreviewFrame's cplot function
self.preview.cplot(.....)
(Code example is untested and incomplete but should show the principle)
(There is probably a better way to do it thought the MainPage class but this would involve a bit more code.)

How do I auto fit a Matplotlib figure inside a PySide QFrame?

I'm creating a simple PySide application that also uses MatPlotLib. However, when I add the figure into a QFrame, the figure doesn't automatically fit to the frame:
My graph is created using the following code:
class GraphView(gui.QWidget):
def __init__(self, name, title, graphTitle, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graphTitle = graphTitle
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.Title = gui.QLabel(self)
self.Title.setText(title)
self.layout = gui.QVBoxLayout()
self.layout.addStretch(1)
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def UpdateGraph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
And it's added to the main Widget like so:
# Create individual Widget/Frame (fftFrame)
fftFrame = gui.QFrame(self)
fftFrame.setFrameShape(gui.QFrame.StyledPanel)
self.FFTGraph = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', fftFrame)
Here's a working code sample that shows you how to get it working. I first thought it was because of the stretch you added to the layout, which will use up the additional space around the other widgets. But when I removed it, it still wouldn't resize. The 'easy' solution is to add a resizeEvent, which lets you define the size of your GraphView widget. In this case I just set its geometry to be that of the QFrame, though you might want to add some padding and make sure you set a sensible minimum size for the QFrame.
from PySide import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.fft_frame = FftFrame(self)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.fft_frame)
self.setLayout(self.layout)
self.setCentralWidget(self.fft_frame)
class FftFrame(QtGui.QFrame):
def __init__(self, parent=None):
super(FftFrame, self).__init__(parent)
self.setFrameShape(QtGui.QFrame.StyledPanel)
self.parent = parent
self.graph_view = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', self)
def resizeEvent(self, event):
self.graph_view.setGeometry(self.rect())
class GraphView(QtGui.QWidget):
def __init__(self, name, title, graph_title, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graph_title = graph_title
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self)
self.Title = QtGui.QLabel(self)
self.Title.setText(title)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.layout.setStretchFactor(self.canvas, 1)
self.setLayout(self.layout)
self.canvas.show()
def update_graph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

Resources