Integrating working code in Jupyter Lab into TkInter

Hi,

@ianhi

Forgive my little knowledge of tkinter, but I am trying to integrate the below code, which works well in Jupyter Lab into a tkinter GUI. For context, this is a scatter plot that on selecting any of the options changes the color of the points.

Here is some example code that works in Jupyter Lab:


%matplotlib ipympl
import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import mpl_interactions.ipyplot as iplt


def cluster_colouring(x, y, n_clusters, hashtags):
    if hashtags == '2 Agree':
        colour_list = agree
    else:
        colour_list = agree3

    if n_clusters == 'All':
        return colour_list

    elif n_clusters == 'News':
        color_array = np.array([list(color) for color in colour_list], dtype=np.float64)
        correct = np.array([0, 0.753, 1, 1], dtype=np.float64)
        idx = np.where(np.any(color_array != correct, axis=1))[0]
        color_array[idx, -1] = 0.01
        return color_array
    elif n_clusters == 'Unclear':
        color_array = np.array([list(color) for color in colour_list], dtype=np.float64)
        correct = np.array([0.388, 0.388, 0.388, 1.0], dtype=np.float64)
        idx = np.where(np.any(color_array != correct, axis=1))[0]
        color_array[idx, -1] = 0.01
        return color_array

agree3 = [(0.412, 1.0, 0.431, 1.0),
 (0.388, 0.388, 0.388, 1.0),
 (0.388, 0.388, 0.388, 1.0),
 (0.388, 0.388, 0.388, 1.0)] # RGBA value list

agree = [ (0.388, 0.388, 0.388, 1.0),
(0.412, 1.0, 0.431, 1.0),
(1.0, 0.0, 0.0, 1.0),
 (0.388, 0.388, 0.388, 1.0)] 

data = np.ndarray([[ -3.7766423,  -10.52704   ], [  0.79021263, -14.534051  ], [ -4.0262637,  -10.065537  ], [ -0.71750355, -11.4191885 ]]) # Numpy array with x and y values for each point.


fig, ax = plt.subplots(sharex=True, sharey=True)

controls = iplt.scatter(x=data[:, 0],
                        y=data[:, 1],
                        s=7,
                        linewidth=0.1,
                        n_clusters= {('All', 'News', 'Unclear')},
                        edgecolors='gray',
                        c=cluster_colouring,
                        hashtags={('2 Agree', '3 Agree')}
                        )
plt.axis('off')
plt.show()


Here is how I tried to integrate it into tkinter:

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import mpl_interactions.ipyplot as iplt
import tkinter as tk

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure

matplotlib.use('TkAgg')

def cluster_colouring(x, y, n_clusters, hashtags):
    if hashtags == '2 Agree':
        colour_list = agree
    else:
        colour_list = agree3

    if n_clusters == 'All':
        return colour_list

    elif n_clusters == 'News':
        color_array = np.array([list(color) for color in colour_list], dtype=np.float64)
        correct = np.array([0, 0.753, 1, 1], dtype=np.float64)
        idx = np.where(np.any(color_array != correct, axis=1))[0]
        color_array[idx, -1] = 0.01
        return color_array
    elif n_clusters == 'Unclear':
        color_array = np.array([list(color) for color in colour_list], dtype=np.float64)
        correct = np.array([0.388, 0.388, 0.388, 1.0], dtype=np.float64)
        idx = np.where(np.any(color_array != correct, axis=1))[0]
        color_array[idx, -1] = 0.01
        return color_array

agree3 = [(0.412, 1.0, 0.431, 1.0),
 (0.388, 0.388, 0.388, 1.0),
 (0.388, 0.388, 0.388, 1.0),
 (0.388, 0.388, 0.388, 1.0)] # RGBA value list

agree = [ (0.388, 0.388, 0.388, 1.0),
(0.412, 1.0, 0.431, 1.0),
(1.0, 0.0, 0.0, 1.0),
 (0.388, 0.388, 0.388, 1.0)] 

data = np.ndarray([[ -3.7766423,  -10.52704   ], [  0.79021263, -14.534051  ], [ -4.0262637,  -10.065537  ], [ -0.71750355, -11.4191885 ]]) # Numpy array with x and y values for each point.

root = tk.Tk()

fig, ax = plt.subplots(sharex=True, sharey=True)

controls = iplt.scatter(x=data[:, 0],
                        y=data[:, 1],
                        s=7,
                        linewidth=0.1,
                        n_clusters= {('All', 'News', 'Unclear')},
                        edgecolors='gray',
                        c=cluster_colouring,
                        hashtags={('2 Agree', '3 Agree')}
                        )
plt.axis('off')
plt.show()


canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
canvas.draw()
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()

button_quit = tk.Button(master=root, text="Quit", command=root.destroy)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
tk.mainloop()

I get it to show, both the plot and the buttons. But on button press, nothing changes, and I get the following error (reappearing every time I press a button):


Traceback (most recent call last):
  File "/Users/redacted/miniforge3/envs/VIZ/lib/python3.11/site-packages/matplotlib/cbook/__init__.py", line 309, in process
    func(*args, **kwargs)
  File "/Users/redacted/miniforge3/envs/VIZ/lib/python3.11/site-packages/mpl_interactions/helpers.py", line 344, in changeify_radio
    update({"new": labels.index(val)})
TypeError: Controls.slider_updated() missing 1 required positional argument: 'values'

However, when using the below example from the docs, everything works well (sliders change the plot and no errors come up):

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import mpl_interactions.ipyplot as iplt
import tkinter as tk

from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure

matplotlib.use('TkAgg')

root = tk.Tk()

x = np.linspace(0,np.pi,100)
tau = np.linspace(1,10, 100)
beta = np.linspace(.001,1)
def f(x, tau, beta):
    return np.sin(x*tau)*x**beta
fig, ax = plt.subplots()
controls = iplt.plot(x, f, tau=tau, beta=beta)
canvas = FigureCanvasTkAgg(fig, master=root)  # A tk.DrawingArea.
canvas.draw()
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()

button_quit = tk.Button(master=root, text="Quit", command=root.destroy)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
tk.mainloop()

So any help would be appreciated! This is on Mac, M1, and using Pycharm.

Hi @amt you seem to have discovered in mpl-interactions’ handling of matplotlib radio widgets! I have a fix that I will try to make a PR for and release this weekend (after I help several people move house…)

Great to hear! Hope the moving went well! Thanks for your work!

should be fixed in version 0.23.2 Release 0.23.2 · mpl-extensions/mpl-interactions · GitHub

Just tried it and it works! Thanks !