Embedding matplotlib into tkinter canvas opens two windows - python

The following code I am working on in not behaving the way I wish it to. I have embedded a matplotlib graph into a tkinter canvas. The program opens up two windows, one of which functions properly, and one of which is not necessary.I am not sure how to fix this. Here is the code, please ignore the unnecessary imports :)
import numpy as np
import sys
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
from matplotlib import cm
from numpy.random import random
from matplotlib.widgets import Button
import matplotlib.colors
import tkinter as tk
import matplotlib.backends.tkagg as tkagg
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
DEBUG_MODE = False #Debug mode - True = ON
MATRIX_WIDTH = 50
MATRIX_HEIGHT = 50
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
LED_COUNT = MATRIX_WIDTH * MATRIX_HEIGHT
REFRESH_RATE = 30 #REFRESH_RATE used to control FuncAnimation interval
MATRIX = random((50,50)) #Fills MATRIX as not to be null for first print
plt.rcParams['toolbar'] = 'None' #Disables matplotlib toolbar
fig = plt.figure(figsize=(3,3)) #'figsize' measured in inches
im = plt.imshow(MATRIX, interpolation='nearest', cmap=cm.Spectral)
plt.axis('off') #Turns off x, y axis
def data_gen(): #Generates amd populates MATRIX with pattern data
while True:
MATRIX = random((MATRIX_WIDTH, MATRIX_HEIGHT))
yield MATRIX
if (DEBUG_MODE): print("MATRIX yeilded")
def update(data): #Updates/prints new MATRIX from data_gen()
im.set_array(data)
if (DEBUG_MODE): print("Updated data")
root = tk.Tk()
label = tk.Label(root,text="Matrix Program").grid(column=0, row=0)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(column=0,row=1)
ani = animation.FuncAnimation(fig, update, data_gen, interval=REFRESH_RATE)
plt.show()
What needs to be done to this code so that it opens only one canvas from tkinter with the live matplotlib graph embedded?
How can I set the size of the canvas?

Do not call plt.show() if you want to show your figure inside a tk GUI. Best do not use pyplot at all when embedding.
On the other hand, you probably want to start the mainloop, tk.mainloop(), at some point.
Refer to the matplotlib example on how to embedd a matplotlib figure into tk.

Related

How to display audio at the right side of matplotlib

The following code display the image and audio in the top-bottom style:
Here is the test code:
import librosa
import matplotlib.pyplot as plt
import IPython.display as ipd
def plot_it(name, audio, sample_rate):
plt.figure(figsize=(8, 1))
plt.plot(audio)
plt.gca().set_title(name)
plt.show()
ipd.display(ipd.Audio(data=audio, rate=sample_rate))
Is it possible for changing the "top-bottom" style to "left-right" style for displaying the audio at the right side of the plt figure?
You can use a GridspecLayout which is similar to matplotlib's GridSpec. In order to direct to output into the needed grid cells, you can capture it using the Output widget:
import librosa
import matplotlib.pyplot as plt
import IPython.display as ipd
from ipywidgets import Output, GridspecLayout
def plot_it(name, audio, sample_rate):
grid = GridspecLayout(1, 2, align_items='center')
out = Output()
with out:
fig, ax = plt.subplots(figsize=(8, 1))
ax.plot(audio)
ax.set_title(name)
plt.close(fig)
ipd.display(ax.figure)
grid[0, 0] = out
out = Output()
with out:
ipd.display(ipd.Audio(data=audio, rate=sample_rate))
grid[0, 1] = out
ipd.display(grid)
name = 'nutcracker'
filename = librosa.example(name)
y, sr = librosa.load(filename)
plot_it(name, y, sr)
(It is essential to close the figure, otherwise you'll have double output of the figure. This is easier to do this using the OOP than the pyplot interface, that's why I changed your matplotlib code a bit)

add Seaborn Scatter plot to tkinter gui

I want to add this scatter plot to my tkinter gui. I ran this on my jupyter notebook and it works but I am not sure how to implement it to the tkinter gui.
enter image description here
this is the window the graph should be
enter image description here
this is the code I have on my jupyter notebook:
### Import libraries
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
### Import dataset
players_df = pd.read_csv("player_locations.txt")
players_df
### Draw Seaborn Scatter Plot to find location between lat and long
sns.scatterplot(x = players_df["longitude"], y = players_df["latitude"], hue = players_df["Player"])
this is the code on my gui for the analysis window:
from tkinter import *
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import csv
import pandas as pd
import seaborn as sns
from matplotlib import animation
from matplotlib.animation import FuncAnimation
from matplotlib import style
players_df = pd.read_csv("player_locations.txt")
players_df
class addAnalysis(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry("650x650+620+200")
self.title("Analysis")
self.resizable(False,False)
#Frames
self.top=Frame(self,height=150,bg='white')
self.top.pack(fill=X)
self.bottomFrame=Frame(self,height=500,bg='#f1dead')
self.bottomFrame.pack(fill=X)
#heading
self.heading = Label(self.top, text='Player Analysis', font='arial 25 bold',
fg='#000000', bg='white')
self.heading.place(x=230, y=60)
#buttons
graph_button = Button(self.bottomFrame,text=' Graph ',
font='arial 14 bold',command = self.plot)
graph_button.place(x=10,y=380)
exit_button = Button(self.bottomFrame,text=' Exit ',
font='arial 14 bold',command = self.destroy)
exit_button.place(x=10,y=440)
# this is the part where I implement the scatter plot
def plot():
# Draw Seaborn Scatter Plot to find location between lat and long
scatter = self.sns.scatterplot(x = players_df["longitude"], y = players_df["latitude"], hue = players_df["Player"])
I recently learned that tk can import PIL images. I needed to convert an numpy array with raw RGB data to tk and the advice was to use PIL.fromarray() to create a PIL image, then use ImageTk.PhotoImage() to import the PIL image into tk. I hope this information is useful to you.

How to make a ipywidget button update a specific axis of a matplotlib figure?

How can I make a ipywidget button in a Jupyter notebook update a plot in a specific axis?
I already know how to make a button update a plot when using a single axis, like so:
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
plt.plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
In this example, clicking the button updates the plot and shows a new set of 10 random points. I thought it would be simple to extend this to updating a specific axis, and attempted the following:
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
fig, ax = plt.subplots(ncols=2)
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
ax[0].plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
However, clicking the button in this example does not seem to do anything. I tried different combinations of adding/removing the plt.show() call, using fig.draw() instead, using fig.canvas.draw_idle(), etc, without much success. What's the correct, least "hacky" way of accomplishing this?
Note: This question is only about how to make a button update a plot, like my first example, instead of making the button update a specific axis only.
with this code
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib widget
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
fig, ax = plt.subplots(ncols=2)
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
ax[0].plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
I got this output

Embedding seaborn clustermap into tkinter window

I'm attempting to display a seaborn clustermap in a tkinter window.
I can embed seaborn heatmaps; however, clustermaps fail to appear in the tkinter window and an empty seaborn frame appears instead.
#Libraries
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
#Returns plot for tkinter to display
def create_plot():
#creates dataset
data = np.random.rand(50,11)
#Defines plot
plot, ax = plt.subplots(figsize=(11, 9))
#Does work - Outputs heatmap
#sns.heatmap(data, cmap='YlGnBu')
#Doesn't work - Should output clustermap
sns.clustermap(data,cmap='YlGnBu',metric = 'correlation',z_score=0)
return plot
#Generating tkinter window
root = tkinter.Tk()
figure = create_plot()
canvas = FigureCanvasTkAgg(figure, master=root)
canvas.draw()
canvas.get_tk_widget().pack()
tkinter.mainloop()
The clustermap can be correctly displayed inline with the following code:
%matplotlib inline
data = np.random.rand(50,11)
sns.clustermap(data,cmap='YlGnBu',metric = 'correlation',z_score=0)
Is there something different about clustermaps, how can they be embedded?
clustermap creates its own figure. So you need to return this figure to use it elsewhere.
#Libraries
import numpy as np
import seaborn as sns
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
#Returns plot for tkinter to display
def create_plot():
data = np.random.rand(50,11)
g = sns.clustermap(data,cmap='YlGnBu',metric = 'correlation',z_score=0)
return g.fig
#Generating tkinter window
root = tkinter.Tk()
figure = create_plot()
canvas = FigureCanvasTkAgg(figure, master=root)
canvas.draw()
canvas.get_tk_widget().pack()
tkinter.mainloop()
But be aware that seaborn creates a pyplot figure. Embedding a pyplot figure in a custom GUI may cause problems (note how none of the examples from the matplotlib page on embedding use pyplot!).

Matplotlib shows no reading if the tick labels get modified

I expect to hover my mouse over the plot and get clean data reading on the right side of the navigation bar in the automatically-generated plot window.
In my case (see code at the bottom), however, if I turn the y-axis tick labels into decibel, the y-reading in the navigation bar (bottom right corner) will disappear, like this:
Workaround: If you comment out the #PROBLEM code block in the code below, then the y-reading in the bottom right corner will be visible, like this:
The code I used to pack the widgets:
from os.path import abspath, dirname, join
import tkinter as tk
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk as NavigationToolbar
from scipy.io import wavfile
root = tk.Tk()
mainframe = tk.Frame(root)
mainframe.pack()
frame = tk.Frame(mainframe)
frame.pack()
figFrame = tk.Frame(frame)
toolFrame = tk.Frame(frame)
figFrame.pack(side='top', fill='both', expand=True)
toolFrame.pack(side='top', fill='both', expand=True)
# Place the figure
fig = plt.Figure()
figWidget = FigureCanvasTkAgg(fig, master=figFrame)
track = figWidget.get_tk_widget()
track.pack(side='top', fill='both', expand=True)
# Place the toolbar
toolbar = NavigationToolbar(figWidget, toolFrame)
toolbar.pack(side='top', fill='both', expand=True)
# Get data
SR, signal = wavfile.read(join(abspath(dirname(__file__)), 'y.wav'))
# Plot the signal read from wav file
ax = fig.add_subplot(111)
ax.set_title('Waveform and Spectrogram of a wav file')
ax.plot(signal)
ax.set_xlabel('Sample')
ax.set_ylabel('Amplitude')
# PROBLEM: Truncated y-readings in Toolbar
ax.set_ylabel('Amplitude (dB)')
ticks = ax.get_yticks()
t1 = 20*np.log10(-ticks[(ticks < 0)])
t2 = 20*np.log10(ticks[(ticks > 0)])
t1 = [float('{:.1f}'.format(i)) for i in t1]
t2 = [float('{:.1f}'.format(i)) for i in t2]
ticks = np.concatenate((t1, [-np.inf], t2))
ax.set_yticklabels(ticks)
# PROBLEM: END
plt.show()
root.mainloop()
I wonder where I did wrong. My guess is that when the ticks are hacked (my way), then there will be no reading at all.... If so, then that's a pity, because I only modified the ticks not the data.
It's clear that no useful y coordinate can be shown when you set the ticklabels manually; maybe that becomes clearer if you consider that you could have labelled the plot with "Apple", "Banana", "Cherry" - in that case what would the coordinate be when the mouse is halfway between "Banana" and "Cherry"?
You may however use a FuncFormatter to set the format of the tickslabels.
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
import numpy as np
signal = np.sin(np.linspace(0,12,300))*.7
fig, ax = plt.subplots()
ax.set_title('Waveform and Spectrogram of a wav file')
ax.plot(signal)
ax.set_xlabel('Sample')
ax.set_ylabel('Amplitude (dB)')
def fmt(x,pos=None):
if x==0:
return "-inf"
else:
return '{:.1f}'.format(20*np.log10(np.sign(x)*x))
ax.yaxis.set_major_formatter(FuncFormatter(fmt))
plt.show()

Categories

Resources