patternpythonMinor
A general purpose GUI data input, with validation, but unclear about best object design
Viewed 0 times
withbutdesigndatavalidationgeneralinputaboutunclearpurpose
Problem
When I'm throwing together prototype code, I don't want to spend extra time setting up parameters, or chasing down problems from entering parameters wrongly. So this is a general purpose, fairly easy to use, GUI data input helper, that hopefully takes care of most of the data entry whoopsies that frustrate just getting the damned target code working.
I really had trouble with deciding whether each entry line should be an object in its own right. The code shown here takes that approach, but as a drawback the field widths are fixed so that they line up nicely, and there's an update callback. In an earlier version, the top level widget built everything from individual
I changed whether I had lists of keys and entry lines, or a
Anyhow, it runs. Have I missed a better way of designing it that would have me feeling better about it? It has a
```
""" class to use as a general purpose GUI input widget
and a few convenience functions for validating data inputs
"""
import tkinter as tki
import tkinter.messagebox as tkm
class MyLabelEntry(tki.Frame):
""" a combination of label, entry and help button, for validated gui entry of data"""
def __init__(self, parent, text='label', data='', conv=None, update=None):
""" text optional used to label the entry
data optional used to initialise the Entry box
uses empty string if none supplied
conv optional conversion function, which also validates the entry
note t
I really had trouble with deciding whether each entry line should be an object in its own right. The code shown here takes that approach, but as a drawback the field widths are fixed so that they line up nicely, and there's an update callback. In an earlier version, the top level widget built everything from individual
Label, Entry and Button widgets, which took care of a lined-up layout automatically and didn't need the update callback, but was a bit untidier around keeping the corresponding elements together, and needed closures for trace callbacks to check the right data.I changed whether I had lists of keys and entry lines, or a
dict of entries keyed on the keys several times, neither seems really tidy, though the lists implementation I have now is not too bad. Anyhow, it runs. Have I missed a better way of designing it that would have me feeling better about it? It has a
__name__ == '__main__', so run it and have a play.```
""" class to use as a general purpose GUI input widget
and a few convenience functions for validating data inputs
"""
import tkinter as tki
import tkinter.messagebox as tkm
class MyLabelEntry(tki.Frame):
""" a combination of label, entry and help button, for validated gui entry of data"""
def __init__(self, parent, text='label', data='', conv=None, update=None):
""" text optional used to label the entry
data optional used to initialise the Entry box
uses empty string if none supplied
conv optional conversion function, which also validates the entry
note t
Solution
I ran your code and messed around a little with it, and it works pretty good. You also have a pretty cool idea here, that I might have to steal next time I do a TKinter GUI.
As far as high-level design, I agree with your decision to have each entry line be its own object—it's much easier to think about than the other approach you described, with the
I don't really like your list approach, though. It looks to me like you're doing parallel arrays, something that gets used in C a lot since it doesn't have a real dictionary type. You rely on the keys and values (entries) being at the same position in two different lists. Normally, I would prefer dictionaries. If order is important, you can use the
As I mentioned in my comment, I have some issues with the way you've named things. Per PEP-8, the standard is to use CamelCase for class names and snake_case for functions and methods. I realize that
I'd also recommend changing
Finally, I'd recommend a cleaner way to manage conversion functions inside
This approach would also strip out a lot of code in your
It could use a little cleaning up, but you've got some pretty well-designed code here, and a good idea for a helper class to speed along GUI development.
As far as high-level design, I agree with your decision to have each entry line be its own object—it's much easier to think about than the other approach you described, with the
GUI_Input class directly managing the TKinter objects. You've added enough behavior that these MyLabelEntry things aren't really the same as their constituent objects anymore, so it makes sense to make a new class that deals with that.I don't really like your list approach, though. It looks to me like you're doing parallel arrays, something that gets used in C a lot since it doesn't have a real dictionary type. You rely on the keys and values (entries) being at the same position in two different lists. Normally, I would prefer dictionaries. If order is important, you can use the
Collections.OrderedDict class, assuming you're using Python 2.7 or above. (It looks from your TKinter code like you're using Python 3.) To me, an ordered dictionary feels cleaner and more Pythonic, and there's no danger (however remote) of your keys and values getting out of sync. (I say this as someone who spent a year coding all my dictionaries as parallel lists because of the habits I developed after taking C++ at university.)As I mentioned in my comment, I have some issues with the way you've named things. Per PEP-8, the standard is to use CamelCase for class names and snake_case for functions and methods. I realize that
GUIInputs is horrible, though; in Java they would write GuiInputs, but PEP-8 prefers that you keep fully capitalized acronyms fully capitalized. To get around this, I'd recommend just changing the name. If I'm reading the code right, GUI_inputs is a container for MyLabelEntry instances, so you could call it something like LabelEntryContainer.I'd also recommend changing
MyLabelEntry. It's a pet peeve of mine, but I hate it when people name things with "my". It seems to be a Perlism; in that language, you declare variables with my the way Javascript and Scala declare them with var. I dislike "my" because it makes names longer without really adding more info. Really, what does MyLabelEntry have that LabelEntry doesn't? As far as I can tell, two extra letters.Finally, I'd recommend a cleaner way to manage conversion functions inside
MyLabelEntry. What you have now is basically a giant switch statement, which somewhat defies extensibility. Functions in Python are first-class, so you could instead store your conversion functions in a dictionary. I tried it out, and it looks like functions are even hashable, so you can use them as dictionary keys if that's what you need. Your dictionary values could be tuples or dictionaries which give the settings of variables in each case. Let me know if you need to see an example, and I'll edit to add.This approach would also strip out a lot of code in your
__init__ function working with conv. This is good, because it's usually considered better style to keep your __init__ function lean. If you need to do something complicated, you can write helpers (which would be private in Java) and call them from __init__. It could use a little cleaning up, but you've got some pretty well-designed code here, and a good idea for a helper class to speed along GUI development.
Context
StackExchange Code Review Q#52397, answer score: 6
Revisions (0)
No revisions yet.