Home > Software design >  how to instantly update the colorbar range of a matplotlib from values of entries?
how to instantly update the colorbar range of a matplotlib from values of entries?

Time:03-04

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.

  • Related