Binding Two Comboboxes To One Function
Solution 1:
(Revised to reflect the not insignificant changes you made to the code in your question and to be much more object-oriented.)
Actually, the code currently in your question raises a NameError: name 'x' is not defined
on the cost, path = shortest_path(x, y)
statement, and only after fixing that would it raise something like the shortestPath() takes exactly 2 arguments (1 given)
you mention.
The problem is you need to define a proper Tkinter
event handler function, which are defined to normally only receive one argument (in addition to self
if it happens to be a class method), namely the event
which triggered them.
So in this case that's the cause of the error: shortestPath()
expects an x
and y
value to be passed to it but it's only receiving one — the "<<ComboboxSelected>>"
event from Tkinter
.
Here's what I mean:
from Tkinter import *
import ttk
import heapq
# Program GUIclassApp(object):
def__init__(self):
self.root = Tk()
self.graph = {
'a': {'w': 16, 'x': 9, 'y': 11},
'b': {'w': 11, 'z': 8},
'w': {'a': 16, 'b': 11, 'y': 4},
'x': {'a': 9, 'y': 12, 'z': 17},
'y': {'a': 11, 'w': 4, 'x': 12, 'z': 13},
'z': {'b': 8, 'x': 17, 'y': 13},
}
self.termini = {} # storage for node ids of each terminus
self.create_gui_widgets()
self.root.mainloop()
defcreate_gui_widgets(self):
values = tuple(self.graph.keys()) # valid node ids
self.combo1 = self.create_combobox('start', values, 5, 75, 5, 20)
self.combo2 = self.create_combobox('end', values, 5, 100, 5, 20)
defcreate_combobox(self, terminus, values, x, y, height, width):
" Utility to create ComboBox of node ids for a terminus of route. "
combobox = ttk.Combobox(self.root, height=height, width=width)
combobox['values'] = values
combobox.terminus = terminus
combobox.current(0)
self.termini[combobox.terminus] = combobox.get() # set to current value
combobox.place(x=x, y=y)
combobox.bind("<<ComboboxSelected>>", self.combobox_event_handler)
return combobox
defcombobox_event_handler(self, event):
" Event handler for both ComboBoxes. "
combobox = event.widget # ComboBox triggering event
self.termini[combobox.terminus] = combobox.get() # set selected node id# if both a start and end terminus are defined and different, then# find and display shortest path between them
start, end = self.termini.get('start'), self.termini.get('end')
if start and end and start != end:
cost, path = self.shortest_path(start, end)
print('cost: {}, path: {}'.format(cost, path))
# Dijkstra's search algorithmdefshortest_path(self, start, end):
graph = self.graph # copy to local var for faster access
queue, seen = [(0, start, [])], set()
whileTrue:
cost, v, path = heapq.heappop(queue)
if v notin seen:
path = path + [v]
seen.add(v)
if v == end:
return cost, path
fornext, c in graph[v].iteritems():
heapq.heappush(queue, (cost + c, next, path))
App()
Solution 2:
The error states that your function needs two arguments, which is true. The reason one is getting passed in is simply because that's how bindings work. In tkinter, when you create a binding and the binding fires, the function always gets an argument which is an object that contains information about the event.
The solution is simple: write a new function for your comboboxes that accepts the event, gathers the information it needs, and then does the calculation. You don't need to actually use the event object, but you need to accept it.
Your choice of using an object-oriented architecture makes this very easy to implement:
self.combo1.bind("<<ComboboxSelected>>",self.on_combobox)
self.combo2.bind("<<ComboboxSelected>>",self.on_combobox)
...
defon_combobox(self, event):
start = self.combo1.get()
end = self.combo2.get()
self.shortestPath(start, end)
Note that this solution assumes that shortestPath
is a method on the object. You've indented it like it is, but it isn't defined to take the self
parameter. You should give it the self
parameter if you want to call it as an object method:
defshortestPath(self, start, end):
...
There are a few other problems with the code as posted, but they are unrelated to the question that was asked. I would like to offer one other piece of advice though: avoid the use of place
. It makes your GUIs harder to write and maintain than when using pack
or grid
, and using place
makes your GUI less tolerant of differences between systems. For example, on my machine the comboboxes were chopped off because the coordinates you calculated aren't right for my machine. You typically won't have those problems with grid
and pack
.
Post a Comment for "Binding Two Comboboxes To One Function"