Python Tkinter:从另一个窗口打开同一窗口时的不同行为

时间:2015-07-11 11:28:26

标签: python autocomplete tkinter

我使用Tkinter创建了一个带有自定义自动完成条目的窗口。

当运行直接带有自动完成条目的窗口(带有“直接”命令行参数)时,该条目正常工作,当键入下划线时,该条目表示4个硬编码字符串

在从另一个窗口双击事件(使用“indirect”命令行参数)后运行此窗口时,自动完成条目不起作用。 更新:更准确地说,自动完成显示第一个窗口上的选项(而不是带有自动完成条目的窗口)。

造成这种不一致的原因是什么?如何在两种情况下都能使它工作?

见MWE附件:

from Tkinter import *
from ttk import Frame, Label, Style

class AutocompleteEntry(Entry):
    def __init__(self, contacts, mainComposeMailWindow, *args, **kwargs):
        Entry.__init__(self, *args, **kwargs)
        self.contacts = contacts    
        self.mainComposeMailWindow = mainComposeMailWindow
        self.var = self["textvariable"]
        if self.var == '':
            self.var = self["textvariable"] = StringVar()

        self.var.trace('w', self.changed)
        self.bind("<Right>", self.selection)
        self.bind("<Up>", self.up)
        self.bind("<Down>", self.down)

        self.lb_up = False

    def changed(self, name, index, mode):
        words = self.comparison()
        if words:            
            if not self.lb_up:
                self.lb = Listbox()
                self.lb.bind("<Double-Button-1>", self.selection)
                self.lb.bind("<Right>", self.selection)
                self.lb.place(x=self.winfo_x(), y=self.winfo_y()+self.winfo_height())
                self.lb_up = True

            self.lb.delete(0, END)
            for w in words:
                self.lb.insert(END,w)
        else:
            if self.lb_up:
                self.lb.destroy()
                self.lb_up = False

    def selection(self, event):
        if self.lb_up:
            self.var.set(self.lb.get(ACTIVE))
            self.lb.destroy()
            self.lb_up = False
            self.icursor(END)

    def up(self, event):
        if self.lb_up:
            if self.lb.curselection() == ():
                index = '0'
            else:
                index = self.lb.curselection()[0]
            if index != '0':                
                self.lb.selection_clear(first=index)
                index = str(int(index)-1)                
                self.lb.selection_set(first=index)
                self.lb.activate(index) 

    def down(self, event):
        if self.lb_up:
            if self.lb.curselection() == ():
                index = '0'
            else:
                index = self.lb.curselection()[0]
            if index != END:                        
                self.lb.selection_clear(first=index)
                index = str(int(index)+1)        
                self.lb.selection_set(first=index)
                self.lb.activate(index) 

    def comparison(self):
        return [w for w in self.contacts if w.lower().startswith(self.var.get().lower())]

    def get_content(self):
        return self.var.get()

class AutoCompleteWindow:
    def __init__(self):
        autocomplete_choices = ['_This', '_order', '_is', '_important']
        self.root = Tk()
        self.root.minsize(300,300)
        Label(self.root, text="Enter text:").grid(row=0)
        self.autocomplete_entry = AutocompleteEntry(autocomplete_choices, self, self.root, bd = 2, width=50)
        self.autocomplete_entry.grid(row=0, column=1)
        self.root.mainloop() 

def on_open_window(event):
    AutoCompleteWindow()

def makeWindow ():
    global select
    win = Tk()
    frame3 = Frame(win)
    frame3.pack()
    scroll = Scrollbar(frame3, orient=VERTICAL)
    select = Listbox(frame3, yscrollcommand=scroll.set, height=17, width=100)
    select.bind("<Double-Button-1>" , on_open_window)
    scroll.config (command=select.yview)
    scroll.pack(side=RIGHT, fill=Y)
    select.pack(side=LEFT,  fill=BOTH, expand=1)
    return win

def setSelect () :
    scrollbar_choices = ["first", "second", "third"]
    select.delete(0,END)
    for a_choice in scrollbar_choices:
        select.insert(END, a_choice)

def intro_window():
    win = makeWindow()
    setSelect ()
    win.mainloop()

if __name__ == "__main__":
    if sys.argv[1] == "indirect":
        intro_window()
    elif sys.argv[1] == "direct":
        AutoCompleteWindow()

1 个答案:

答案 0 :(得分:1)

问题是您正在创建多个根窗口并运行多个事件循环(但是,一次只运行一个)。 Tkinter设计为仅使用Tk的一个实例运行,mainloop()只调用一次。如果您需要其他窗口,则应创建Toplevel的实例。

问题的另一部分是您没有为列表框提供显式父级,因此它将始终显示在根窗口中。您需要为列表框指定一个显式父级。具体来说,它应该与条目小部件是同一个父级。

假设*args的第一个元素是父元素(这是一个不好的假设,但似乎在这个非常具体的情况下保持不变),一个非常快速的解决方法就是这样做:

class AutocompleteEntry(Entry):
    def __init__(self, contacts, mainComposeMailWindow, *args, **kwargs):
        self.parent = args[0]
        ...
    def changed(...):
        ...
        self.lb = Listbox(self.parent)

更好(读取:更清楚)修复将显式声明parent作为__init__的关键字参数,因此您不依赖于参数的特定顺序。