I am having trouble building a Python function that launches TkInter objects, with commands bound to menu buttons, using button specifications held in a dictionary.
SITUATION
I am building a GUI in Python using TkInter. I have written a Display class (based on the GuiMaker class in Lutz, "Programming Python") that should provide a window for data entry for a variety of entities, so the commands and display of any instance will vary. I would like to configure these entity-specific commands and displays in a dictionary file. The file is evaluated when the script is run, and its dictionary passed to a launcher function when a Display instance is called for. But the instances' commands can't find the instance methods I'm trying to bind to them.
SPECIFICATION IN FUNCTIONS WORKS
This isn't a problem when the Display instance is launched with configurations specified in a dedicated function. For instance this works fine:
def launchEmployee():
display = ''
menuBar = [('File', 0, [('Save', 0, (lambda: display.onSave()))])]
title = 'Employee Data Entry'
display_args = {'title': title,
'menuBar': menuBar}
display = DisplayScreen(**display_args)
The DisplayScreen subclasses from the GuiMaker, which has methods for processing the menuBar object to create a menu. It has an onSave() method.
Done this way, the Display instance finds and runs its own onSave() method when the 'Save' button is clicked.
SPECIFICATION FROM DICTIONARY FILE DOESN'T WORK
But this doesn't work when I try to launch the Display instance from a launcher function, pulling its specification from a dictionary held in a separate file.
config_file:
{'menuBar':[('File', 0, [('Save', 0, (lambda: display.onSave()))])],
'title': 'Employee Data Entry'}
script file:
config = eval(open('config_file', 'r').read())
def launchDisplay(config):
display = ''
display = DisplayScreen(**config)
Run this way, clicking 'Save' generates an error, saying there is no global object 'display'.
THEORY: DICTIONARY CASE LOOKS FOR OBJECTS IN SCOPE AT EVAL() CALL
I speculate that in the function case, 'display' is a string object, whose lack of the method onSave() isn't a problem for the the assigment to menuBar because its examination for the method is deferred inside the lambda function. When the Display instance is assigned to the 'display' object, this overloads the prior assignment of the string object, but Python still knows about 'display' and goes to it when asked for its onSave() method.
If so, the configuration case is failing because the 'display' object doesn't exist at all when the config dictionary is created by evaluation. This doen't cause an error at the eval() call because, again, the lambda function hides the object from inspection until called. But when called, Python goes looking for the 'display' object in scope at the moment of the eval() call, where it finds nothing, and then reports an error.
BUT: PUTTING EVAL() CALL IN SCOPE DOESN'T HELP
But I have tried moving the evaluation of the dictionary file into the function and after the creation of the 'display' string object, and this doesn't work either.
SO:
What is going on here?
How can I specify methods to be bound to commands in a dictionary to be accessed when instantiating the display object? It really seems better to specify these screens in a configuration file than in a host of duplicative functions.