Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is there an alternative for deepcopy when working with tkinter?

Running into an issue while working with tkinter, here’s a simple example of what I’m doing

import tkinter as tk
from tkinter import ttk
from typing import Any, Callable, Mapping, NamedTuple, Type


values = {
        'eat': ('something', 'nice'),
        'dirt': ('something', 'else'),
        'kid': ('something'), '': tuple()
    }

class MyTuple(NamedTuple):
    widget: Type[tk.Widget]
    config: Mapping[str, Any] = {}
    postcommand: Callable[[ttk.Combobox], None] = None


class Reg():
    def __init__(self, var):
        self.var = var
        self.widgets = (
                MyTuple(ttk.Label),
                MyTuple(ttk.Combobox, {'values': tuple(values.keys()), 'textvariable': self.var}),
                MyTuple(ttk.Combobox, postcommand=self.cb),
                MyTuple(ttk.Combobox, postcommand=self.other_cb),
            )

    def display(self, frame: tk.Tk):
        for w in self.widgets:
            widget = w.widget(frame, **w.config)
            widget.pack()
            if not w.postcommand is None:
                widget['postcommand'] = lambda widget=widget : w.postcommand(widget)

    def cb(self, combobox: ttk.Combobox):
        combobox['values'] = values[self.var.get()]

    def other_cb(self, combobox: ttk.Combobox):
        combobox['values'] = tuple(values.keys())


class GUI(tk.Tk):
    def __init__(self):
        super().__init__()
        v = tk.StringVar()
        reg = Reg(v)
        reg.display(self)

_ = GUI()
_.mainloop()

When running this, it seems that w gets overwritten and the same postcommand is set for all of the comboboxes. I tried changing display to look like this:

from copy import deepcopy

...

    def display(self, frame: tk.Tk):
        for w in self.widgets:
            widget = w.widget(frame, **w.config)
            widget.pack()
            if not w.postcommand is None:
                command = deepcopy(w.postcommand)
                widget['postcommand'] = lambda widget=widget : command(widget)

And I get the error TypeError: cannot pickle '_tkinter.tkapp' object.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Is there an alternative to deepcopy that I can use? All I could find was a way to overwrite the pickle function, but I can’t figure out how to apply that here, since I can’t even figure out what tkinter object is part of the deepcopy.

Alternatively, is there a way to fix the scoping issue with w?

(Using python 3.8.10)

>Solution :

The lambda that calls w.postcommand is not capturing the correct value of w. What happens if you change the lambda inside your loop to this?

widget['postcommand'] = lambda widget=widget, w=w: w.postcommand(widget)

Further reading: What do lambda function closures capture?

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading