tkinter:如何创建一个自定义窗口小部件,该窗口小部件可以在不移动其他窗口小部件的情况下在另一个窗口小部件上显示和消失?

时间:2017-01-25 12:16:02

标签: python tkinter listbox

我之前发过一个关于“how to create a listbox that can appear and disappear over another widget without moving the other widget?”的问题。布莱恩奥克利回答了我的问题并提供了非常有用的解释和指示。

目前,我正在研究上一个问题的下一个级别方案。也就是说,我想自定义一个由标签和列表框组成的新窗口小部件。当鼠标指针单击新窗口小部件并且周围窗口小部件的位置不得更改时,它的列表框应该出现并消失在周围窗口小部件的前面。我试图应用我之前学到的东西,并修改了我之前的代码,试图实现我的目标。我的代码如下所示。

我遇到的问题是我的自定义窗口小部件的列表框只出现在自定义窗口小部件的框架内(绿色),即使我使用了.place布局管理器。布莱恩以前写过这个

  

place不会影响同一父母的其他孩子。

在这种情况下,我应该如何重构我的代码和窗口小部件,以便自定义窗口小部件中的列表框出现时会出现在自定义窗口小部件的相邻窗口小部件前面?在我的代码中,当单击self.label2时,我希望列表框显示在class 'App'中定义的self.newwidget前面。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

try:
    import tkinter as tk # Python 3 tkinter modules
except ImportError:
    import Tkinter as tk # Python 2 tkinter modules

class NewWidget(tk.Frame):
    def __init__(self, parent, lb_values):
        tk.Frame.__init__(self, parent, background='green', borderwidth=10,
                          pady=30)
        # Initialise local variable
        self.toggle = True
        # Create widgets of NewWidget
        self.label1 = tk.Label(
            self, text='Click me to see Names of Famous Folks')
        self.lbframe = tk.Frame(self, background='orange', borderwidth=10)
        self.lbframe.list= tk.Listbox(self.lbframe, height=5, width=10)
        # Put values into listbox
        for item in lb_values:
            self.lbframe.list.insert(tk.END, item)
        # Manage widgets layout
        self.label1.pack(fill="both", expand=True)
        self.lbframe.list.pack(fill="both", expand=True)
        # Set widget bindding
        self.label1.bind("<Button-1>", self.ShowHideListbox)

    def ShowHideListbox(self, event):
        if self.toggle: # Show
            self.toggle=False
            self.lbframe.place(
                in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw")
        else: # Hide
            self.toggle=True
            self.lbframe.place_forget()


class App(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent, background='pink')

        self.label1 = tk.Label(self, text='Welcome!')
        nlist = ['Peter', 'Scotty', 'Walter', 'Scott', 'Mary']
        self.newwidget = NewWidget(self, nlist)
        self.label2 = tk.Label(self, height=10,
                               text='The World of Famous Folks! Revealed!',)

        self.label1.grid(row=0, column=0, sticky='nsew', pady=10)
        self.newwidget.grid(row=1, column=0, sticky='nsew')
        self.label2.grid(row=2, column=0, sticky='nsew')

        self.label2.columnconfigure(2, weight=1)


if __name__ == '__main__':
    root = tk.Tk()
    root.geometry("300x300")
    app = App(root)
    app.pack(fill="both", expand=True)
    app.mainloop()

1 个答案:

答案 0 :(得分:2)

要使列表框显示在相邻小部件的顶部,我必须做两件事:

1)更改NewWidget类中包含列表框的框架的主窗口小部件:

self.lbframe = tk.Frame(self, background='orange', borderwidth=10)

变为

self.lbframe = tk.Frame(parent, background='orange', borderwidth=10)

使它与相邻的小部件具有相同的主控。

2)在NewWidget类中的所有其他小部件之后创建App实例:

class App(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent, background='pink')
        self.rowconfigure(1, weight=1)
        self.label1 = tk.Label(self, text='Welcome!')
        nlist = ['Peter', 'Scotty', 'Walter', 'Scott', 'Mary']
        # self.newwidget = NewWidget(self, nlist) # former position
        self.label2 = tk.Label(self, height=10,
                               text='The World of Famous Folks! Revealed!',)

        self.label1.grid(row=0, column=0, sticky='nsew', pady=10)
        self.label2.grid(row=2, column=0, sticky='nsew')
        self.label2.columnconfigure(2, weight=1)
        self.newwidget = NewWidget(self, nlist)
        self.newwidget.grid(row=1, column=0, sticky='nsew')