【问题标题】:python - binding to a tkinter widget is called twice if set() is called on a StringVar()python - 如果在 StringVar() 上调用 set(),则调用两次绑定到 tkinter 小部件
【发布时间】:2018-03-05 18:31:45
【问题描述】:

我有一个条目、一个列表框(下拉列表)和另一个列表框。每当在条目中键入超过 3 个字符时。查找完成列表并将其插入到下拉列表中,然后显示下拉列表。如果从下拉列表中选择了一个项目。它的值应该填充条目并且条目应该再次获得焦点并且光标应该转到条目的末尾。然后,当按下 Enter 键时,条目的值应插入到另一个列表框中。

this 实用程序的大力帮助下,我为此开发了一个代码,并且代码运行良好。除了,我意识到每当我从下拉列表中选择一个选项时,相应的方法都会被调用两次(我在控制台中从同一个东西中得到两个打印)。但是如果我选择下拉菜单的第一个选项,它会被调用一次(这在另一种情况下应该实际发生)但焦点不会转到条目(这是一个问题)。

这是我的代码:

from tkinter import *


class Autocomplete(Frame, object):
    def __init__(self, width, height, entries, *args, **kwargs):
        super(Autocomplete, self).__init__(*args, **kwargs)
        self._entries = entries
        self.listbox_height = height
        self.entry_width = width
        self.text = StringVar()
        self.entry = Entry(
            self,
            textvariable=self.text,
            width=self.entry_width
        )
        self.frame = Frame(self)
        self.listbox = Listbox(
            self.frame,
            height=self.listbox_height,
            width=self.entry_width
        )
        self.dropdown = Listbox(
            self.frame,
            height=self.listbox_height,
            width=self.entry_width,
            background="#cfeff9"
        )

    def build(self):
        self.text.trace("w", lambda name, index, mode, text=self.text: self._update_autocomplete())
        self.entry.bind("<Return>", lambda event,: self._add_course())
        self.entry.focus_set()
        self.entry.pack()
        self.frame.pack()
        self.listbox.grid(column=0, row=0, sticky=N)
        self.dropdown.bind("<<ListboxSelect>>", self._select_entry)
        self.dropdown.grid(column=0, row=0, sticky=N)
        self.dropdown.grid_forget()
        return self

    def _update_autocomplete(self):
        self.dropdown["height"] = self.listbox_height
        self.dropdown.delete(0, END)
        text = self.text.get()
        if len(text) < 3:
            self.dropdown.grid_forget()
            return
        else:
            for entry in self._entries:
                if text.lower() in entry.strip().lower():
                    self.dropdown.insert(END, entry)
        listbox_size = self.dropdown.size()
        if not listbox_size:
            self.dropdown.insert(END, "No results found for '{}'")
            self.dropdown["height"] = 1
        else:
            if listbox_size <= self.dropdown["height"]:
                self.dropdown["height"] = listbox_size
        self.dropdown.grid(column=0, row=0, sticky=N)

    def _select_entry(self, event):
        widget = event.widget
        value = widget.get(int(widget.curselection()[0]))
        print(value)
        self.text.set(value)
        self.entry.focus_set()
        self.entry.icursor(END)

    def _add_course(self):
        self.listbox.insert(END, self.text.get())

那么我在这里错过了什么?

顺便说一句,对代码的任何一般改进也将不胜感激。

我是这样称呼它的:

from tkinter import *
from autocomplete import Autocomplete
from main import *

courses = load_courses_from_file("courses.txt")
root = Tk()
autocomplete_frame = Autocomplete(
    60,
    10,
    list(set(course.name + ", " + course.instructor for course in courses))
).build().pack()
mainloop()

【问题讨论】:

  • 您的代码不会像发布的那样运行。
  • @BryanOakley 你是什么意思?你的意思是我没有调用它?如果是这样,我编辑了帖子。否则,你确定吗? bc 我想我正在运行它,我想我复制粘贴了代码!

标签: python data-binding tkinter binding listbox


【解决方案1】:

当您单击项目时,列表框的选择会发生变化——这是列表框的默认行为。这会导致条目小部件值发生更改,从而触发对_update_autocomplete 的调用。该函数删除列表框中的所有内容,导致选择再次更改。

【讨论】:

  • 我明白了!当单击第一个元素时,所选元素的编号保持为 1,因此它不会改变。虽然我仍然不明白为什么焦点没有改变。以及我应该如何解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-03
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多