First some background - I am developing a Python program which has thousands of lines spread across many files. I have intermediate programming skills - and no commercial OOP experience, and have taught myself Python recently. I have come across a problem which I can solve, in a number of different ways, but I'm not really happy with any of my solutions - they don't feel quite right from an OOP point of view. I have tried to make things generic so they can be used for lots of different options, but by doing so it seems massively convoluted.
I have distilled the problem down into a comparatively small piece of code for review. I have a main object, and I have an object which holds some options (which are just true/false for now, but could be other things in the future). I then have a third object (a tkinter button) that each show the status of the option (via being sunken or raised), and when clicked, change the value.
I felt the right way to do this would be for the functionality (in italics above) to be contained entirely in the toggleButton
class. At first I just passed it self.options.Option1
(or whatever), but that didn't work. I then read up on does Python pass by value or reference, which (I know I am simplifying here) seemed to say that when you pass an object it passes the object, but when you pass something like an integer or boolean it passes the value. So I changed it to what you see now - where you pass the variable, an integer representing the variable, and changing the options class to have a function to change the option. It all seems so needlessly verbose. Is there a better way?
from Tkinter import *
class optionsClass():
_OPTION1 = 1
_OPTION2 = 2
def __init__(self):
self.Option1 = True
self.Option2 = False
def change(self, optionid):
if optionid == optionsClass._OPTION1:
if self.Option1 == True:
self.Option1 = False
else:
self.Option1 = True
if optionid == optionsClass._OPTION2:
if self.Option2 == True:
self.Option2 = False
else:
self.Option2 = True
class toggleButton(Button):
def __init__(self, master, targetObject, targetoptionid, initialval
, **kwargs):
Button.__init__(self, master, command = self.callback, **kwargs)
self.targetoptionid = targetoptionid
self.targetObject = targetObject
if initialval==True:
self.config(relief=SUNKEN)
else:
self.config(relief=RAISED)
def callback(self):
self.targetObject.change(self.targetoptionid)
if self.cget("relief") == SUNKEN:
self.config(relief=RAISED)
else:
self.config(relief=SUNKEN)
class mainframe(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.options = optionsClass()
option1box = toggleButton(self, self.options, optionsClass._OPTION1,
self.options.Option1, text="Option 1")
option1box.grid(column=0, row=0)
option2box = toggleButton(self, self.options, optionsClass._OPTION2,
self.options.Option2, text="Option 2")
option2box.grid(column=0, row=1)
testbutton= Button(self, command = self.outputoptions,
text="Return option values")
testbutton.grid(column=0, row=2)
def outputoptions(self):
print "Option 1 is " + str(self.options.Option1)
print "Option 2 is " + str(self.options.Option2)
top = Tk()
m = mainframe(top)
m.pack()
top.mainloop()
Things to bear in mind...
- I would like the options class to work for things other than boolean without much difficulty.
- I like the convenience and readability of the main class to simply say
self.options.Option1
. - Ideally there will still be three classes, as mainframe and option do a lot more in the actual program, and
toggleButton
is used multiple times and is therefore a good candidate to be a class.