简体   繁体   English

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

[英]Python Tkinter: different behavior when opening the same window from another window

I used Tkinter to create a window with a custom auto-complete entry. 我用Tkinter创建了一个带有自定义自动完成条目的窗口。

When running directly the window with the auto-complete entry (with the "direct" command-line argument), the entry works fine and when typing underscore the entry suggests the 4 hard-coded string. 直接使用自动完成条目(带有“ direct”命令行参数)运行窗口时,该条目可以正常工作,当键入下划线时,该条目建议使用4个硬编码字符串。

When running this window after double-click event from another window (with the "indirect" command-line argument), the auto-complete entry doesn't work . 在另一个窗口(带有“间接”命令行参数)双击事件后运行该窗口时,自动完成输入无效 UPDATE: more precisely, the autocomplete shows the options on the first window (instead of the window with the autocomplete entry). 更新:更准确地说,自动完成功能在第一个窗口(而不是带有自动完成功能的窗口)上显示选项。

What is causing this inconsistency? 是什么导致这种不一致? How can I make it work in both cases? 如何在两种情况下都可以使用?

See MWE attached: 参见附件:

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

The problem is that you are creating more than one root window and running more than one event loop (though, only one is running at a time). 问题是您要创建多个根窗口并运行多个事件循环(尽管一次仅运行一个)。 Tkinter is designed to be run with exactly one instance of Tk , with mainloop() called exactly once. Tkinter设计为仅使用Tk一个实例运行,其中mainloop()恰好调用一次。 If you need additional windows you should create instances of Toplevel . 如果需要其他窗口,则应创建Toplevel实例。

Another part of the problem is that you don't give the listbox an explicit parent, so it will always appear in the root window. 问题的另一部分是您没有为列表框指定显式父项,因此它始终会出现在根窗口中。 You need to give the listbox an explicit parent. 您需要为列表框指定一个显式父项。 Specifically, it should be the same parent as the entry widget. 具体来说,它应与条目小部件具有相同的父级。

Assuming that the first element of *args is the parent (which is a bad assumption, but seems to hold up in this very specific case), a really quick fix is to do this: 假设*args的第一个元素是父元素(这是一个错误的假设,但在这种非常特殊的情况下似乎成立),一个真正的快速解决方案是:

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

A better (read: more clear) fix would be to explicitly declare parent as a keyword argument to __init__ , so you don't rely on a specific ordering of the arguments. 一个更好的(更清晰的阅读方法)解决方法是将parent明确声明为__init__的关键字参数,因此您不必依赖于参数的特定顺序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM