Python Tkinter and the vanishing Combobox default value

This is one of those things that still catches me out – perhaps because I don’t code frequently enough to avoid it. Luckily, by now, my very confusion usually points me to the solution.

What Happens

So, the situation is this:

Here’s the actual example. As per usual with Tkinter, first I define the widget – although in the case of a combobox I precede it with a Tkinter string variable to hold the selected value and tell the combobox to use it for the textvariable

    t02_str_shell = m_tkntr.StringVar() 
    t02_cb_shell = m_tkntr.ttk.Combobox( t02_frame91, width = 20, textvariable = t02_str_shell) 
    t02_cb_shell['values'] = ( "Bash", "MS-DOS", "PowerShell" )

and follow that with a placement action – here I’m using the “pack” method.

    t02_cb_shell.pack( side = m_tkntr.LEFT)

And here is the part where I add a setting of the default value – note that I do this by assigning to the Tkinter string variable that I had set up.

    t02_str_shell.set( "Bash" )

And yet, as already described, this doesn’t seem to work.

Why It Happens

The short answer is:

In slightly deeper terms: there is nothing actually wrong with the code that I wrote. The statement settings the default value really does do that. However, as I’ve not yet written anything to use that value, the garbage collector immediately knows that the assignment to the Tkinter string variable was the mention it ever gets. By the logic of aggressive garbage collection that means it can be discarded – and not some time later, it will happen immediately.

Needless to say, when I first encountered this phenomenon it drove me bonkers – but, thankfully I did eventually work it out.

In practical terms, the solution is that when going through the steps of adding Tkinter elements to a design, be sure to always write some usage of the value somewhere else in the code.

For example, here is the line that I added – to a def elsewhere inside the scope of my wider Tkinker code – that then let me see the GUI acting as I want it to.

            i_str_shell = t02_str_shell.get()

Deeper and Deeper

Compared to compiled procedural programming, Python can be a weird beast at times. The question of quite where else I needed to put the code that would use my t02_str_shell variable can be a little hard to describe.

For example, note that when I say:

The Python garbage collector is a run-time actor and the conception you have of a variable as type in your source code, needs to always be projecting forward in time to what will happen when the code runs.

Is this Programming or Philosophy?

As an aside, this touches on a whole other topic about understanding a language like Python. For me personally, this is a bit ironic. I learned to program on the assumption of compiled software, so for the most part, the run-time conception was closely tied to exactly what my source code specified. I did therefore, have to understand well quite what the run-time actions of my code would be. When I would then occasionally use interpreted languages, I mostly understood them as late-in-the-day compilers. In both cases, for many of the things that most “dynamic” languages now do, I had to learn to learn to do them myself – e.g. monitoring stack and heap usages and bullet-proofing my use of pointers to ensure there were no (literal) loose ends.

As this is Tkinter we’re talking about, it helps to remember that Tkinter is not itself written in Python, so we’re somewhat talking about both Tkinter and its connection/realisation in CPython.

To be a little more existential, this raises the (perhaps pointless) question of whether it was Tkinter that garbage collected my absent combobox initial value, or if it was Python that decided I was never going to use the Tkinter String variable and so it never sent it to the Tkinter non-in-Python library.

The distinction is moot as either way, the solution was to add a line of Python code.

Exit mobile version