将两个组合框绑定到一个功能

时间:2015-12-08 04:44:01

标签: python python-2.7 user-interface tkinter

我在绑定功能方面遇到了麻烦。使用Dijkstra算法找到最短路径,我想要它,所以当两个comboxbox选择一个字母时,它会打印出结果,但我不知道如何去做。当我尝试运行它时,它会显示错误" shortestPath()只需要2个参数(给定1个)"。

from Tkinter import *
import ttk
import heapq

# GUI of the program.
class app():
    def __init__(self):
        self.root = Tk()
        self.fill_Combo()
        self.root.mainloop()

    def fill_Combo(self):
        self.combo1= ttk.Combobox(self.root,height=5, width=20)
        self.combo1['values'] = ('a','b','w','x','y','z')
        self.combo1.current(0)
        self.combo1.place(x=5, y = 75)
        self.combo1.bind("<<ComboboxSelected>>",self.shortestPath)

        self.combo2= ttk.Combobox(self.root,height=5, width=20)
        self.combo2['values'] = ('a','b','w','x','y','z')
        self.combo2.current(0)
        self.combo2.place(x=5, y=100)
        self.combo2.bind("<<ComboboxSelected>>",self.shortestPath)

    # Dijkstra's algorithm function.
    def shortestPath(start, end):
        queue,seen = [(0, start, [])], set()
        while True:
            (cost, v, path) = heapq.heappop(queue)
            if v not in seen:
                path = path + [v]
                seen.add(v)
                if v == end:
                    return cost, path
                for (next, c) in graph[v].iteritems():
                     heapq.heappush(queue, (cost + c, next, path))

                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},
               }

    cost, path = shortestPath(x, y)
    print cost, path

app()

2 个答案:

答案 0 :(得分:1)

(修改后反映您在问题中对代码所做的不可忽视的更改,并且更加面向对象。)

实际上,您问题中当前的代码会在NameError: name 'x' is not defined语句中引发cost, path = shortest_path(x, y),并且只有在修复后才会引发类似您提及的shortestPath() takes exactly 2 arguments (1 given)

问题是你需要定义一个正确的Tkinter事件处理函数,它被定义为通常只接收一个参数(如果它恰好是一个类方法,除了self),即触发它们的event

因此,在这种情况下,错误的原因是:shortestPath()期望将xy值传递给它,但它只收到一个 - "<<ComboboxSelected>>"来自Tkinter的活动。

这就是我的意思:

from Tkinter import *
import ttk
import heapq

# Program GUI
class App(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()

    def create_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)

    def create_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

    def combobox_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 algorithm
    def shortest_path(self, start, end):
        graph = self.graph  # copy to local var for faster access
        queue, seen = [(0, start, [])], set()
        while True:
            cost, v, path = heapq.heappop(queue)
            if v not in seen:
                path = path + [v]
                seen.add(v)
                if v == end:
                    return cost, path
                for next, c in graph[v].iteritems():
                     heapq.heappush(queue, (cost + c, next, path))

App()

答案 1 :(得分:0)

错误表明您的函数需要两个参数,这是真的。一个人被传入的原因仅仅是因为绑定是如何工作的。在tkinter中,当您创建绑定并且绑定触发时,该函数始终获取一个参数,该参数是包含该事件信息的对象。

解决方案很简单:为接受事件的组合框编写一个新函数,收集所需信息,然后进行计算。您不需要实际使用事件对象,但您需要接受它。

您选择使用面向对象的体系结构使这很容易实现:

self.combo1.bind("<<ComboboxSelected>>",self.on_combobox)
self.combo2.bind("<<ComboboxSelected>>",self.on_combobox)
...
def on_combobox(self, event):
    start = self.combo1.get()
    end = self.combo2.get()
    self.shortestPath(start, end)

请注意,此解决方案假定shortestPath是对象上的方法。您已按原样缩进,但未定义采用self参数。如果要将其称为对象方法,则应该为其指定self

def shortestPath(self, start, end):
    ...

发布的代码还存在一些其他问题,但它们与提出的问题无关。我想提供另一条建议:避免使用place。与使用packgrid相比,它使您的GUI更难编写和维护,并且使用place使您的GUI不太容忍系统之间的差异。例如,在我的机器上,组合框被切断,因为您计算的坐标不适合我的机器。您通常不会遇到gridpack的问题。