【问题标题】:Tkinter how to catch this particular key eventTkinter 如何捕捉这个特定的关键事件
【发布时间】:2017-04-09 22:34:22
【问题描述】:

问题描述

我在 Tkinter 中有一个应用程序,它使用一个显示搜索结果的列表框。当我按下command + down 箭头键时,我将焦点从搜索字段移到列表框中的第一项。这正是我想要的行为方式,而不是 down 箭头。

但是,我已经通过self.bind("<Down>", self.moveDown) 将向下箭头绑定到此列表框。我不明白为什么command + down 有效,而简单的down(我将其绑定到它)却无效。具体按下down箭头的结果如下 在按下command + down 时会得到预期的结果: 我怎样才能让down 表现得像command + down,以及为什么需要command

代码 sn-ps

def matches(fieldValue, acListEntry):
    pattern = re.compile(re.escape(fieldValue) + '.*', re.IGNORECASE)
    return re.match(pattern, acListEntry)
root = Tk()
img = ImageTk.PhotoImage(Image.open('imgs/giphy.gif'))
panel = Label(root, image=img)
panel.grid(row=1, column=0)
entry = AutocompleteEntry(autocompleteList, panel, root, matchesFunction=matches)
entry.grid(row=0, column=0)
root.mainloop()

AutocompleteEntry 为:

class AutocompleteEntry(Tkinter.Entry):
    def __init__(self, autocompleteList, df, panel, rdi, *args, **kwargs):
        self.df = df
        self.product_row_lookup = {key:value for value, key in enumerate(autocompleteList)}
        temp = df.columns.insert(0, 'Product_omschrijving')
        temp = temp.insert(1, 'grams')
        self.result_list = pd.DataFrame(columns=temp)
        self.panel = panel
        self.rdi = rdi
        # self.bind('<Down>', self.handle_keyrelease)

        # Listbox length
        if 'listboxLength' in kwargs:
            self.listboxLength = kwargs['listboxLength']
            del kwargs['listboxLength']
        else:
            self.listboxLength = 8
        # Custom matches function
        if 'matchesFunction' in kwargs:
            self.matchesFunction = kwargs['matchesFunction']
            del kwargs['matchesFunction']
        else:
            def matches(fieldValue, acListEntry):
                pattern = re.compile('.*' + re.escape(fieldValue) + '.*', re.IGNORECASE)
                return re.match(pattern, acListEntry)
            self.matchesFunction = matches

        Entry.__init__(self, *args, **kwargs)
        self.focus()
        self.autocompleteList = autocompleteList
        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.moveUp)
        self.bind("<Down>", self.moveDown)
        self.bind("<Return>", self.selection)
        self.listboxUp = False
        self._digits = re.compile('\d')


    def changed(self, name, index, mode):
        if self.var.get() == '':
            if self.listboxUp:
                self.listbox.destroy()
                self.listboxUp = False
        else:
            words = self.comparison()
            if words:
                if not self.listboxUp:
                    self.listbox = Listbox(width=self["width"], height=self.listboxLength)
                    self.listbox.bind("<Button-1>", self.selection)
                    self.listbox.bind("<Right>", self.selection)
                    self.listbox.bind("<Down>", self.moveDown)
                    self.listbox.bind("<Tab>", self.selection)
                    self.listbox.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height())
                    self.listboxUp = True

                self.listbox.delete(0, END)
                for w in words:
                    self.listbox.insert(END, w)
            else:
                if self.listboxUp:
                    self.listbox.destroy()
                    self.listboxUp = False
                else:
                    string = self.get()
                    if '.' in string:
                        write_to_file(self, string)

    def contains_digits(self, d):
        return bool(self._digits.search(d))


    def selection(self, event):
        if self.listboxUp:
            string = self.listbox.get(ACTIVE)
            self.var.set(string + ' ')
            self.listbox.destroy()
            self.listboxUp = False
            self.icursor(END)



    def moveDown(self, event):
        self.focus()
        if self.listboxUp:
            if self.listbox.curselection() == ():
                index = '0'
                print "ok"
            else:
                index = self.listbox.curselection()[0]
                print "blah"

            if index != END:
                self.listbox.selection_clear(first=index)
                print "noo"
                if index != '0':
                    index = str(int(index) + 1)

            self.listbox.see(index)  # Scroll!
            self.listbox.selection_set(first=index)
            self.listbox.activate(index)
        else:
            print "not up"


    def comparison(self):
        return [w for w in self.autocompleteList if self.matchesFunction(self.var.get(), w)]

【问题讨论】:

  • 您能否添加足够的代码以使您的代码可运行以便我对其进行测试?
  • 如果您删除了所有与问题无关的代码,并添加了足够的代码来实际重现问题,您的问题会更容易理解。在此处查看并遵循建议:How to create a Minimal, Complete, and Verifiable example
  • 对不起,我没有提供最简约的工作代码。我应该。但是提供的答案确实提供了解决方案!你们太棒了,谢谢。我被困了很长时间。

标签: python tkinter key-bindings


【解决方案1】:

command+down 和 down 应该产生相同的输出,除了 down 还在输入最后一个字母是问号框的条目上输入问号

这是因为按下命令,您的计算机会检查选项菜单以查看该键是否有快捷方式,如果没有,则不会执行任何操作。当 tkinter 将向下按钮注册为被按下时,因此触发了事件。

相比之下,在不按命令的情况下,Entry首先显示“down”的值,没有任何,然后执行事件绑定,你可以做的是,在事件中,删除最后一个条目的信。您可以在活动中通过self.delete(len(self.get())-1) 进行操作。或者在 event 末尾添加 return 'break' 以防止输入。

【讨论】:

    【解决方案2】:

    不幸的是,很难理解你真正的问题,因为你发布了太多不相关的代码而没有足够的相关代码。在我看来,您想要完成的是让用户在条目具有焦点时按下向下或向上箭头,并让列表框中的选择向下或向上移动。此外,问题的一部分似乎是您在输入小部件中看到了当您按下或向上时不想看到的字符。

    如果这是问题所在,解决方案相当简单。您需要做的就是让您的绑定返回字符串“break”,以防止处理默认绑定。它是插入字符的默认绑定。

    这是一个例子。运行该示例,然后按上下键移动列表框的选择。我省略了所有与自动完成相关的代码,因此您可以专注于事件绑定的工作方式。

    import Tkinter as tk
    
    class Example(object):
        def __init__(self):
    
            self.root = tk.Tk()
    
            self.entry = tk.Entry(self.root)
            self.listbox = tk.Listbox(self.root, exportselection=False)
            for i in range(30):
                self.listbox.insert("end", "Item #%s" % i)
    
            self.entry.pack(side="top", fill="x")
            self.listbox.pack(side="top", fill="both", expand=True)
    
    
            self.entry.bind("<Down>", self.handle_updown)
            self.entry.bind("<Up>", self.handle_updown)
    
        def start(self):
            self.root.mainloop()
    
        def handle_updown(self, event):
            delta = -1 if event.keysym == "Up" else 1
            curselection = self.listbox.curselection()
            if len(curselection) == 0:
                index = 0
            else:
                index = max(int(curselection[0]) + delta, 0)
    
            self.listbox.selection_clear(0, "end")
            self.listbox.selection_set(index, index)
    
            return "break"
    
    if __name__ == "__main__":
        Example().start()
    

    有关触发事件时发生的情况的相当详尽的解释,请参阅此答案:https://stackoverflow.com/a/11542200/7432

    【讨论】:

      【解决方案3】:

      同样,抛开自动完成的要求,我想出了一个解决方案,它使用ListboxScrollbar 的标准可用命令和事件。 &lt;&lt;ListboxSelect&gt;&gt; 允许您从任何列表中捕获选择的变化并对齐其他列表。此外,ScrollbarListbox 回调被定向到将内容传递到所有列表框的路由函数。

      # updownmultilistbox.py
      # 7/24/2020
      #
      # incorporates vsb to propagate scrolling across lists
      #
      
      import tkinter as tk
      
      
      class Example(object):
          def __init__(self):
              self.root = tk.Tk()
              self.listOfListboxes = []
              # self.active_lb = None
              self.vsb = tk.Scrollbar(orient='vertical', command=self.OnVsb)
              self.vsb.pack(side='right', fill='y')
      
              self.lb1 = tk.Listbox(self.root, exportselection=0,
                                    selectmode= tk.SINGLE, yscrollcommand=self.vsb_set)
              self.lb2 = tk.Listbox(self.root, exportselection=0,
                                    selectmode=tk.SINGLE, yscrollcommand=self.vsb_set)
              self.lb3 = tk.Listbox(self.root, exportselection=0,
                                    selectmode=tk.SINGLE, yscrollcommand=self.vsb_set)
      
              self.listOfListboxes.append(self.lb1)
              self.listOfListboxes.append(self.lb2)
              self.listOfListboxes.append(self.lb3)
      
              for i in range(30):
                  self.lb1.insert("end", "lb1 Item #%s" % i)
                  self.lb2.insert("end", "lb2 Item #%s" % i)
                  self.lb3.insert("end", "lb3 Item #%s" % i)
      
              self.lb1.pack(side="left", fill="both", expand=True)
              self.lb2.pack(side="left", fill="both", expand=True)
              self.lb3.pack(side="left", fill="both", expand=True)
      
              for lb in self.listOfListboxes:
                  lb.bind('<<ListboxSelect>>', self.handle_select)
      
              for lb in self.listOfListboxes:
                  lb.selection_set(0)
                  lb.activate(0)
              self.listOfListboxes[0].focus_force()
      
          def start(self):
              self.root.title('updownmultilistbox')
              self.root.mainloop()
      
          def OnVsb(self, *args):
              for lb in self.listOfListboxes:
                  lb.yview(*args)
      
          def vsb_set(self, *args):
              print ('vsb_set args: ', *args)
              self.vsb.set(*args)
              for lb in self.listOfListboxes:
                  lb.yview_moveto(args[0])
      
      
          def handle_select(self, event):
              # set evey list to the same selection
              print ('select handler: ', event, event.widget.curselection())
              # self.active_lb = event.widget
              for lb in self.listOfListboxes:
                  if lb != event.widget:
                      lb.selection_clear(0, 'end')    # have to avoid this for the current widget
                      lb.selection_set(event.widget.curselection())
                      lb.activate(event.widget.curselection())
      if __name__ == "__main__":
          Example().start()
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多