【问题标题】:Alternatives for Tkinter's wm_overridedirect method for Mac OS in PythonPython 中 Tkinter 的 Mac OS 的 wm_overridedirect 方法的替代方案
【发布时间】:2021-05-25 18:47:16
【问题描述】:

我制作了一个 Tkinter 小部件,它本质上是一个 Combobox,其列表 1) 在打字期间可见,2) 在打字期间缩短,3) 允许箭头键/输入功能。我使用这两个帖子(firstsecond)来找出一些功能。请参阅下面的课程代码和 MRE。

我的问题:我的 Search 小部件适用于 Windows,但不适用于 Mac OS,我相信这是因为 .wm_overrideredirect() 方法在 Mac OS 中的行为不同(至少对于大苏尔)与 Windows。我的预期功能是让TopLevel 小部件仅包含并显示Text 小部件而不接收焦点。下面的代码在 Windows 中实现了这一点,但在 Mac OS 中却没有。 Mac OS 的替代品是什么?换句话说,是否有任何替代方法可以让小部件扩展到root 之外?

在 Windows 10 上运行时单击条目小部件会执行以下操作:

在 Mac OS Big Sur 上运行时单击条目小部件会执行以下操作:

谢谢!

搜索小部件

from tkinter import Entry, Text, StringVar, Toplevel, WORD, END

class Search: 
    '''A widget with features of Entry and Combobox that allows for a dropdown list 
    that 1) dynamically reduces listed options to those that match user input and
    2) allows for arrow key/enter and mouseover/click functionality.'''
    def __init__(self, root, lst, width=15, allow_unlisted=True):
        self.root = root 
        self.lst = lst 
        self.current_lst = lst 
        self.width = width 
        self.allow_unlisted = allow_unlisted 
        self.indices = [0.0] * 2

        self.stringvar = StringVar() 
        self.stringvar.trace_add("write", self.updateList) 
        self.entry = Entry(root, width=self.width, font=("Arial", 9), textvariable=self.stringvar) 
        self.entry.bind("<FocusIn>", self.showList) 
        self.entry.bind("<FocusOut>", self.hideList) 
        self.entry.bind("<Up>", self.keyList) 
        self.entry.bind("<Down>", self.keyList) 
        self.entry.bind("<Return>", self.hideList)

        self.toplevel = None 
        self.text = None
        self.updateList()
    
    def showList(self, *args): 
        self.toplevel = Toplevel(self.root) 
        self.toplevel.wm_overrideredirect(True) 
        x = self.entry.winfo_rootx() 
        y = self.entry.winfo_rooty() + self.entry.winfo_height()
        self.toplevel.wm_geometry(f"+{x}+{y}")

        self.text = Text(self.toplevel, wrap=WORD, foreground="#333333")
        self.text.config(font=("Arial", 9), width=self.width)
        self.text.tag_configure("highlight", background="dodger blue", foreground="white") 
        self.text.bind("<Motion>", self.motionList) 
        self.text.bind("<Up>", self.keyList) 
        self.text.bind("<Down>", self.keyList) 
        self.text.bind("<Button-1>", self.hideList) 
        self.text.bind("<Return>", self.hideList)
        self.text.pack()
        self.updateList()
    
    def hideList(self, *args): 
        if self.text and self.text.winfo_ismapped():
            self.select()
        if self.toplevel:
            self.toplevel.destroy()
            self.toplevel = self.text = None
        self.root.focus_set()
    
    def updateList(self, *args): 
        var = self.stringvar.get() 
        self.indices = [0.0] * 2 
        self.current_lst = [item for item in self.lst if var.lower() in item.lower()] 
        if self.text: 
            self.text.tag_remove("highlight", 1.0, END)
            self.text.config(height=min(10, len(self.current_lst)))
            self.text.delete(1.0, END)
            self.text.insert(END, "\n".join(self.current_lst))
    
    def motionList(self, event): 
        index1 = float(self.text.index(f"@{event.x},{event.y} linestart"))
        self.highlight(index1) 
    
    def keyList(self, event):
        if event.keysym == "Up" and self.indices[0] > 0: 
            self.indices[0] -= 1 
        elif event.keysym == "Down" and self.indices[0] < len(self.current_lst): 
            self.indices[0] += 1
        if self.indices[0]: 
            self.highlight(self.indices[0]) 
        else: 
            self.indices = [0.0] * 2 
            self.text.tag_remove("highlight", 1.0, END) 
    
    def highlight(self, index1): 
        self.indices = [index1, float(self.text.index(f"{index1} lineend"))] 
        self.text.tag_remove("highlight", 1.0, END) 
        self.text.tag_add("highlight", *self.indices) 
    
    def select(self, *args):
        if self.indices[0]: 
            self.stringvar.set(self.text.get(*self.indices))
    
    def get(self): 
        user_input = self.stringvar.get() 
        if user_input in self.lst or self.allow_unlisted: 
            return user_input 
        else: return "" 
    
    def set(self, user_input): 
        self.stringvar.set(user_input) 
    
    def __getattr__(self, name): 
        return lambda *args, **kwargs: getattr(Entry, name)(self.entry, *args, **kwargs)

MRE

from tkinter import Tk, Label
from Search import Search # or however you want to import this

# example options for widget
options = ["Option 1", "Option 2", "Option 3", "Rabala", "Ploplepla",
           "This is a very long option compared to others",
           "OPTION 1", "option 2", "option 3", "option1", "option2",
           "option3", "oPTION1"]

root = Tk()

l0 = Label(root, text="Widget:")
r = Search(root, lst=options)
l0.grid(row=0, column=0, padx=10, pady=50)
r.grid(row=0, column=1, padx=10, pady=50)

root.mainloop()

【问题讨论】:

  • 为什么你认为这在 Big Sur 上不起作用?它似乎对我有用。这种行为与其他平台上的行为有何不同?
  • 您是否尝试过使用:&lt;tkinter.Tk/Toplevel&gt;.attributes("-type", "splash")?我没有 MacOS 设备来测试它,但我认为它应该可以工作。
  • @BryanOakley 如果这就是你的意思,它不会在 MacOS 上崩溃。我在上面添加了图片以显示功能差异。
  • @TheLizzard 感谢您的建议,我只是试了一下,得到了这个错误:_tkinter.TclError: bad attribute "-type": must be -alpha, -fullscreen, -modified, -notify, -titlepath, -topmost, or -transparent。也许这也取决于操作系统?
  • 我仍然不清楚有什么区别。您是说单击条目(无需键入)应该使下拉菜单出现吗?它对我来说就是这样。

标签: python macos tkinter


【解决方案1】:

看起来有些人可以让它在 MacOS Big Sur 上运行。一定是我机器的问题。谢谢大家的帮助。

【讨论】:

    猜你喜欢
    • 2011-07-07
    • 2016-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2011-02-09
    • 2015-06-19
    相关资源
    最近更新 更多