将两个组合框绑定到一个函数

2024-04-25 19:40:35 发布

您现在位置:Python中文网/ 问答频道 /正文

绑定到函数时遇到问题。使用Dijkstra的算法来寻找最短路径,我希望这样当两个组合框选择一个字母时,它会打印出结果,但不确定如何进行。当我尝试运行它时,它显示错误“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()

Tags: pathimportselfappqueuedefrootttk
2条回答

错误说明函数需要两个参数,这是真的。传入的原因很简单,因为绑定就是这样工作的。在tkinter中,当您创建绑定并触发绑定时,函数总是获取一个参数,该参数是一个包含有关事件信息的对象。在

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

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

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参数:

^{pr2}$

发布的代码还有一些其他问题,但它们与所提出的问题无关。不过,我想提供另一条建议:避免使用place。与使用pack或{}相比,它使您的GUI更难编写和维护,而使用place会使GUI对系统之间的差异的容忍度降低。例如,在我的机器上,组合框被切掉了,因为你计算的坐标不适合我的机器。通常,gridpack不会有这些问题。在

(修订以反映您对问题中的代码所做的不重要的更改,并且更加面向对象。)

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

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

所以在这个例子中,这就是错误的原因:shortestPath()期望向它传递一个x和{}值,但它只接收一个来自Tkinter"<<ComboboxSelected>>"事件。在

我的意思是:

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()

相关问题 更多 >