I have a problem that i really cannot solve. It's been hours of searching. I have this simple tkinter window with one combobox, one figure on a FigureCanvasTkAgg and a Matplotlib TextBox. The thing is that i want to print something on console when i press Enter (on the TextBox). I do this by using text_box.on_submit() command. TextBox can be found here Matplotlib Widgets. If i don't touch the combobox, it works well. However when i pick something (an option) from combobox, it takes focus and enter (sumbit command) does not work for the TextBox. I cant event write on the TextBox but instead when i type i write on the combobox. And even if i click on the TextBox, i can't solve it...
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
win = tk.Tk()
fig,ax = plt.subplots()
ax.plot([1,7,3,6,9,3,6])
canvas = FigureCanvasTkAgg(fig, master=win)
canvas.get_tk_widget().pack()
canvas.draw()
textboxax = plt.axes([0.92, 0.35, 0.06, 0.07])
text_box = TextBox(textboxax, '', initial='',color='gray',hovercolor='white')
text_box.on_submit(lambda x: print('helloo'))
cb = ttk.Combobox(win,values=['red','white','black','yellow','blue'])
cb.pack()
win.mainloop()
The simplest way to overcome this might be to set focus to the widget that is clicked using .focus_set()
Here is an example:
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
def focus_widget(event):
event.widget.focus_set()
win = tk.Tk()
win.bind('<Button>', focus_widget)
fig,ax = plt.subplots()
ax.plot([1,7,3,6,9,3,6])
canvas = FigureCanvasTkAgg(fig, master=win)
canvas.get_tk_widget().pack()
canvas.draw()
textboxax = plt.axes([0.92, 0.35, 0.06, 0.07])
text_box = TextBox(textboxax, '', initial='',color='gray',hovercolor='white')
text_box.on_submit(lambda x: print('helloo'))
cb = ttk.Combobox(win,values=['red','white','black','yellow','blue'])
cb.pack()
win.mainloop()
Related
I was wondering how you can completely delete a plot from a tkinter window.
Assuming i would have a tkinter project like the following:
import tkinter as tk
from pandas import DataFrame
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
data1 = {'Country': ['US','CA','GER','UK','FR'],
'GDP_Per_Capita': [45000,42000,52000,49000,47000]
}
df1 = DataFrame(data1,columns=['Country','GDP_Per_Capita'])
root= tk.Tk()
figure1 = plt.Figure(figsize=(6,5), dpi=100)
ax1 = figure1.add_subplot(111)
bar1 = FigureCanvasTkAgg(figure1, root)
bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
df1 = df1[['Country','GDP_Per_Capita']].groupby('Country').sum()
df1.plot(kind='bar', legend=True, ax=ax1)
ax1.set_title('Country Vs. GDP Per Capita')
def close_plot():
plt.close(figure1)
button_delete = Button(root, text='Delete', command = lambda: close_plot()).place(height=30, width = 100, rely=0.02, relx = 0.4)
root.mainloop()
I veen trying to use matplotlib.pyplot.close within a button but it doens't seem to work.
Anyone have a clue how to get rid of this plot.
Thank you very much!
Your figure is embedded in a tkinter widget. You need to keep a reference to that widget, and use its methods to add/remove it from the tkinter window:
Here we use pack_forget(): the widget is removed from the window, but still exists, and could be reused later.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def remove_plot():
w.pack_forget() # here you remove the widget from the tk window
# w.destroy()
if __name__ == '__main__':
# data
x, y = [1, 2, 3, 4], [1, 4, 9, 16]
# matplotlib stuff
figure1 = plt.Figure(figsize=(6, 5), dpi=100)
ax1 = figure1.add_subplot(111)
ax1.plot(x, y)
ax1.set_title('Country Vs. GDP Per Capita')
# tkinter stuff
root = tk.Tk()
bar1 = FigureCanvasTkAgg(figure1, root)
w = bar1.get_tk_widget()
w.pack(side=tk.LEFT, fill=tk.BOTH) # here you insert the widget in the tk window
button_delete = tk.Button(root, text='Remove Plot', command=remove_plot)
button_delete.place(height=30, width=100, rely=0.02, relx=0.4) # place is an odd choice of geometry manager, you will have to adjust it every time the title changes
root.mainloop()
Alternatively, if you don't need that figure any longer, you could use w.destroy (commented line) to destroy the widget.
I am having problems using matplotlib and tkinter at the same time.
I am trying to create a matplot graphic with radio buttons and embed it in tkinter
Following some examples and documentation over the Internet, I have created the following code:
import random
import matplotlib
import tkinter as Tk
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')
root = Tk.Tk()
root.wm_title("Embedding in TK")
class TKInterGUI():
def __init__(self, master,fig):
self.fig = fig
self.master = master
def test(self):
canvas = FigureCanvasTkAgg(self.fig[0], self.master)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
ax = self.fig[0].add_axes([0.10, 0.7, 0.15, 0.15],facecolor='yellow')
r = RadioButtons(ax, ('2 Hz', '4 Hz', '0 Hz'))
fig = []
fig.append(plt.Figure(figsize=(5,5), dpi=100))
my_gui = TKInterGUI(root,fig)
my_gui.test()
Tk.mainloop()
This code generate the graphic and the radio buttons like intended. BUT the radio buttons do not work. They get completely irresponsive.
Now if I change the radio Button code to the main program like the code bellow, it all works fine:
import random
import matplotlib
import tkinter as Tk
import matplotlib.pyplot as plt
from matplotlib.widgets import RadioButtons
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')
root = Tk.Tk()
root.wm_title("Embedding in TK")
class TKInterGUI():
def __init__(self, master,fig):
self.fig = fig
self.master = master
def test(self):
canvas = FigureCanvasTkAgg(self.fig[0], self.master)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
fig = []
fig.append(plt.Figure(figsize=(5,5), dpi=100))
my_gui = TKInterGUI(root,fig)
my_gui.test()
ax = fig[0].add_axes([0.10, 0.7, 0.15, 0.15],facecolor='yellow')
r = RadioButtons(ax, ('2 Hz', '4 Hz', '0 Hz'))
Tk.mainloop()
Can anyone explain why the first code does not work, but the second one does?
I am trying to display a GUI with a heatmap and scales/sliders with the scales/sliders changing the values in the heatmap.
I can display the heatmap and sliders and can read from the sliders but I cannot get the heat map to update after I have moved the sliders.
I have tried putting the code (I think) updates the heatmap in a function which is called whenever the scale/slider is moved but I am clearly missing something.
import tkinter
from tkinter import ttk
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def myFunc(value):
print (mySlider.get())
array[1][2]=mySlider.get()
#I think I need to put something here to update the heatmap when the
#scale/slider is changed but do not know what
figure, ax = plt.subplots()
ax.imshow(array)
canvas.get_tk_widget().pack()
root = tkinter.Tk()
root.title("Something")
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
ax.imshow(array)
canvas = plt.Figure()
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().pack()
mySlider = tkinter.Scale(root, from_=0, to=15, orient=HORIZONTAL, command=myFunc)
mySlider.pack()
Like this:
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def myFunc(value):
print (mySlider.get())
array[1][2]=mySlider.get()
im.set_array(array)
canvas.draw()
root = tk.Tk()
root.title("Something")
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
im = ax.imshow(array)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().pack()
mySlider = tk.Scale(root, from_=0, to=15, orient=tk.HORIZONTAL, command=myFunc)
mySlider.pack()
root.mainloop()
However tkinter is not needed here. matplotlib has a slider built in (I assume you know since you imported it) which is a lot easier to implement:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
def myFunc(value):
array[1][2]=value
im.set_array(array)
array = ([[1,2,3,4],
[3,9,1,5],
[8,4,1,7],
[2,4,9,1]])
figure, ax = plt.subplots()
im = ax.imshow(array)
ax_slider = plt.axes([0.1, 0.1, 0.8, 0.03]) # [left, bottom, width, height]
slide = Slider(ax_slider, '', 0, 15, valinit=0)
slide.on_changed(myFunc)
plt.show()
I would like to pick lines that have been drawn on an image by using cv2.line and do something with them. To realise that I had a look on matplotlibs picker and even found a good example here. As I am going to use tkinter for GUI I added it to my MWE.
Code from example which works fine:
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import numpy as np
root = Tk.Tk()
root.iconify()
x = np.linspace(0, 10, 100)
fig, ax = plt.subplots()
for i in range(1, 10):
ax.plot(x, i * x + x, picker=5)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def on_pick(event):
event.artist.set_visible(not event.artist.get_visible())
fig.canvas.draw()
fig.canvas.callbacks.connect('pick_event', on_pick)
root.mainloop()
Now my code using OpenCV which does not work:
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 numpy as np
import cv2
root = Tk.Tk()
root.iconify()
img = np.zeros([100,100,3],dtype=np.uint8)
img.fill(255)
cv2.line(img,(10,10),(60,90),(100,149,237),2)
fig = Figure()
ax = fig.add_subplot(111)
ax.set_aspect('equal')
ax.plot()
ax.imshow(img)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def onpick1(event):
event.artist.set_visible(not event.artist.get_visible())
fig.canvas.draw()
fig.canvas.callbacks.connect('pick_event', onpick1)
root.mainloop()
In both examples onpick1 should set the lines visibility on or off by a single mouse click but it doesn't. My assumption is that it has something to do with the way it is plotted or how I draw the lines (ax.plt vs cv2.line). I would be very happy about any help. Thanks!
For a demonstration of a Graph algorithm i need to draw a networkx graph to a Tkinter Canvas and be able to modify that graph (and the plot) at runtime.
I have pieced together the following code (I hope it is the minimal code leading to my problem, but I'm new to this so I'm not sure):
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import Tkinter as Tk
import networkx as nx
from tkMessageBox import showinfo
root = Tk.Tk()
root.wm_title("Animated Graph embedded in TK")
root.wm_protocol('WM_DELETE_WINDOW', root.quit())
f = plt.figure(figsize=(5,4))
a = f.add_subplot(111)
plt.axis('off')
# the networkx part
G=nx.complete_graph(5)
nx.draw_networkx(G,pos=nx.spring_layout(G),ax=a)
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def next_graph():
if G.order():
a.cla()
G.remove_node(G.nodes()[-1])
nx.draw(G, pos=nx.circular_layout(G), ax=a)
canvas.draw()
b = Tk.Button(root, text="next",command=next_graph)
b.pack()
Tk.mainloop()
My problem now is this:
The first display of the graph is like I want it (backgroundcolor-wise), but after you first click 'Next' the backgroundcolor of the graph changes to white. I have tried changing the background color of the figure and the canvas.
I don't even know what brings that change about, I think it is simply drawing to the same canvas twice.
How can I modify the code to have the graph always have the same background color?
On an unrelated note: the root.quit() I added does not help in ending the application properly. This might be stupid on my side, but what did go wrong here?
I think you are very close. If you use nx.draw_networkx() in your event loop then it works (turn off the axis there too).
Here is your example with those modifications and also with a single layout computed at the beginning that is reused in the loop:
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import Tkinter as Tk
import networkx as nx
from tkMessageBox import showinfo
root = Tk.Tk()
root.wm_title("Animated Graph embedded in TK")
# Quit when the window is done
root.wm_protocol('WM_DELETE_WINDOW', root.quit)
f = plt.figure(figsize=(5,4))
a = f.add_subplot(111)
plt.axis('off')
# the networkx part
G=nx.complete_graph(5)
pos=nx.circular_layout(G)
nx.draw_networkx(G,pos=pos,ax=a)
xlim=a.get_xlim()
ylim=a.get_ylim()
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def next_graph():
if G.order():
a.cla()
G.remove_node(G.nodes()[-1])
nx.draw_networkx(G, pos, ax=a)
a.set_xlim(xlim)
a.set_ylim(ylim)
plt.axis('off')
canvas.draw()
b = Tk.Button(root, text="next",command=next_graph)
b.pack()
Tk.mainloop()