I have a simple code to plot a figure. I want to manually change the range for the colorbar.
So, I added two Entries
and defined a second function change()
. I want to make this change for the colorbar to happen instantly without having the second button.
from tkinter import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Max")
label1.place(x=10, y=35)
label2 = Label(root, text="Min")
label2.place(x=10, y=60)
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
def plot():
global x, y
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn')
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
def change():
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn', vmin=entry1.get(), vmax=entry2.get())
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
button2 = Button(root, text="change", command=change)
button2.place(x=80, y=0)
root.mainloop()
I found this post Constantly Update Label Widgets From Entry Widgets TKinter, and I tried to use method 2, and I changed the code in this part:
...
def auto():
c.config(vmin=entry1.get(), vmax=entry2.get())
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
auto()
...
But as c
is a local variable, the code doesn't work. can anybody help me instantly update the colorbar range?
CodePudding user response:
So what You want in the end is to change colormap vmin
and vmax
, when user change Min and Max input. You don't need to constantly update colormap, but just on change of those inputs.
You can do that by tracing input change with update callback.
Also You had few issues with Your code:
- Min and Max values were swapped
- vmin and vmax were not set in
plot
method - not a big deal, but since You are using static x, y, z You can define it outside
plot
method and make it global
Here is modified code which does colormap update when Min and Max input is changed:
from tkinter import *
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Min")
label1.place(x=10, y=35)
label2 = Label(root, text="Max")
label2.place(x=10, y=60)
entry1 = Entry(root, textvariable=Min, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Max, width=5)
entry2.place(x=50, y=60)
# Define global variables
c, canvas = None, None
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x * y)
def plot():
global x, y, z, c, canvas
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn', vmin=entry1.get(), vmax=entry2.get())
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
canvas.draw()
def update_colormap(*args, **kwargs):
global c, canvas
if c is not None:
try:
# Get vmin and vmax
vmin, vmax = int(entry1.get()), int(entry2.get())
except ValueError:
# Could not convert values to int, non integer value
return
# Set new limits
c.set_clim(vmin, vmax)
# Update plot
canvas.flush_events()
canvas.draw()
# Trace change of Min and Max and call update_colormap as a callabck
Min.trace("w", update_colormap)
Max.trace("w", update_colormap)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
root.mainloop()
CodePudding user response:
You can bind the "<Key>"
event of the Entry widget to the change function as the callback, this will call the change function whenever, anything is typed within the entry widget.
entry1.bind('<Key>', lambda x : change())
Doing this for both entries would be adding these lines to your code -:
entry1.bind('<Key>', lambda x : change())
entry2.bind('<Key>', lambda x : change())
The full code will become -:
from tkinter import *
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
root.geometry("500x500")
Max, Min = IntVar(), IntVar()
label1 = Label(root, text="Max")
label1.place(x=10, y=35)
label2 = Label(root, text="Min")
label2.place(x=10, y=60)
entry1 = Entry(root, textvariable=Max, width=5)
entry1.place(x=50, y=35)
entry2 = Entry(root, textvariable=Min, width=5)
entry2.place(x=50, y=60)
def plot():
global x, y
x, y = np.mgrid[slice(0, 100), slice(0, 100)]
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn')
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
def change():
z = (x*y)
figure = Figure(figsize=(4, 4))
ax = figure.add_subplot(111)
c = ax.pcolormesh(x, y, z, cmap='YlGn', vmin=entry1.get(), vmax=entry2.get())
ax.figure.colorbar(c)
canvas = FigureCanvasTkAgg(figure, root)
canvas.get_tk_widget().place(x=0, y=80)
button1 = Button(root, text="Plot", command=plot)
button1.place(x=30, y=0)
button2 = Button(root, text="change", command=change)
button2.place(x=80, y=0)
entry1.bind('<Key>', lambda x : change())
entry2.bind('<Key>', lambda x : change())
root.mainloop()
NOTE:
The "<Key>"
event's callback is triggered whenever a key is pressed inside the widget, for more info take a look at events and bindings.