I've created a couple of listbox widgets where selecting some items in one and then pressing a button below moves the items to the other.
This worked absolutely fine - but I wanted to reuse the container frame because the layouts of the 2 frames was identical (apart from the heading label, and the functions when the buttons were pressed). So I moved all the code except the button functions to a class "ColumnSelector".
However to move data from one "ColumnSelector" to the other I need references to the listboxes inside the instances. Below is the structure of what I would like to do, but I am not sure if this is possible.
I have tried some other ways such as creating the listbox outside of the ColumnSelector class and passing it through, but I ran into problems doing it that way too.
What would be the best way to reference widgets inside instances of other classes?
# Data to be included in second listbox widget
startingSelection = ('Argentina', 'Australia', 'Belgium', 'Brazil', 'Canada', 'China', 'Denmark')
# Two functions performed by the ColumnSelectors
def removeSelected(*args):
idxs = selectedColumns.listBox.curselection() # <- Does not reference correctly
if len(idxs)>=1:
for n in reversed(range(len(idxs))):
idx = int(idxs[n])
item = selectedColumns.listBox.get(idx)
selectedColumns.listBox.delete(idx)
availableColumns.listBox.insert(availableColumns.listBox.size(), item)
def addSelected(*args):
idxs = availableColumns.listBox.curselection() #<- Does not reference correctly
if len(idxs)>=1:
for n in reversed(range(len(idxs))):
idx = int(idxs[n])
item = availableColumns.listBox.get(idx)
availableColumns.listBox.delete(idx)
selectedColumns.listBox.insert(selectedColumns.listBox.size(), item)
# Create ColumnSelectors, pass heading title and function to perform
selectedColumns = ColumnSelector(self, "Columns to include in export", (), removeSelected).grid(column=0, row=0, sticky=(N,W))
availableColumns = ColumnSelector(self, "Available Columns", startingSelection, addSelected).grid(column=1, row=0, sticky=(N,W))
class ColumnSelector(ttk.Frame):
def __init__(self, parent, labelText, startingSelection, function ):
listBox = Listbox(self, height=5, selectmode='multiple')
removeColumnsButton = ttk.Button(self, text="Move", command=function)
#(etc...)
What would be the best way to reference widgets inside instances of other classes?
I think the most common-use case for this is reusing an object an indefinite number of times, as it looks like you're trying to do with some listboxes that are set up inside a frame. In this case, I think you should put as much repeatable code as possible in the subclass and create a method in it that returns just what you want. When you create an instance of the subclass inside your main class, then you can access its methods when you need to (ie, selectedColumns.get_all_listbox_values()).
One thing you should keep in mind is that the instance won't work right if you create it and grid it on the same line:
No
selectedColumns = ColumnSelector(self, "Columns to include in export", (), removeSelected).grid(column=0, row=0, sticky=(N,W))
selectedColumns.get_all_listbox_values()
>>> AttributeError: 'NoneType' object has no attribute 'get_all_listbox_values'
Yes
selectedColumns = ColumnSelector(self, "Columns to include in export", (), removeSelected)
selectedColumns.grid(column=0, row=0, sticky=(N,W))
selectedColumns.get_all_listbox_values()
>>> (0, 1, 2, etc)
Below is an example of one way to set up your script. There's a main class (App) and another class that inherits from Frame (MyEntry) that can be used in App multiple times. There's one button in the App class that prints out results from a method in MyEntry that calculates a couple of values. Hopefully it helps to give you some ideas about structuring your code.
class App(Frame):
'''the main window class'''
def __init__(self, parent):
Frame.__init__(self, parent)
# create instances of MyEntry, passing whatever operators as args
# we can make as many of these instances as we need in just a couple of lines
# and it retains readability
self.divide = MyEntry(self, '/')
self.multiply = MyEntry(self, '*')
self.divide.pack()
self.multiply.pack()
Button(self, text='Calculate', command=self._print_result).pack()
def _print_result(self):
'''print the return of the calculate method from the instances of
the MyEntry class'''
print self.divide.calculate()
print self.multiply.calculate()
class MyEntry(Frame):
'''creates two entries and a label and has a method to calculate
the entries based on an operator'''
def __init__(self, parent, operator): # include the operator as an arg
Frame.__init__(self, parent)
# make an instance variable from the operator to access it between methods
self.operator = operator
# make two entries
self.num1 = Entry(self)
self.num2 = Entry(self)
# grid the entries and a label which contains the operator
self.num1.grid(row=0, column=0)
Label(self, text=self.operator).grid(row=0, column=1)
self.num2.grid(row=0, column=2)
def calculate(self):
'''return the value of the two entries based on the operator specified'''
if self.operator is '/':
return int(self.num1.get()) / int(self.num2.get())
elif self.operator is '*':
return int(self.num1.get()) * int(self.num2.get())
else:
return
root = Tk()
App(root).pack()
mainloop()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With