在 tkinter 列表框上使用 curselection(),但首先选择 returns 一个空元组
Using curselection() on a tkinter listbox, but first selection returns an empty tuple
我正在尝试使用带有来自 tkinter 的字体的列表框来更改文本字段的字体。它工作正常,除了第一次选择列表框。
第一次单击列表框时,我会收到此错误消息 IndexError: tuple index out of range
,带有列表框选择的元组将是 ()
。但是,下一个选择将完全正常。然后我会得到一个这样的元组 (number,)
这是什么原因?如果我绑定 <Double-Button-1>
而不是 <Button-1>
,那么第一个选择(双击列表框)工作正常。
import tkinter
from tkinter import font
class EnkelTeksteditor:
def __init__(self):
self.hovedvindu = tkinter.Tk()
self.tekstomraadet = tkinter.Text(self.hovedvindu, height=10, width=30)
self.tekstomraadet.grid(column=0, row=0)
self.scrollbar = tkinter.Scrollbar(self.hovedvindu, orient=tkinter.VERTICAL,
command=self.tekstomraadet.yview)
self.scrollbar.grid(column=1, row=0, sticky=(tkinter.N, tkinter.S))
self.tekstomraadet.config(yscrollcommand=self.scrollbar.set)
self.hovedvindu.title("Enkel teksteditor")
self.fontlistbox = tkinter.Listbox(self.hovedvindu, selectmode=tkinter.SINGLE)
self.fontene = font.families()
for fonten in self.fontene:
self.fontlistbox.insert(tkinter.END, fonten)
self.fontlistbox.grid(column=3, row=0, sticky=(tkinter.N, tkinter.S))
self.fontscroller = tkinter.Scrollbar(self.hovedvindu, orient=tkinter.VERTICAL,
command=self.fontlistbox.yview)
self.fontlistbox.config(yscrollcommand=self.fontscroller.set)
self.fontscroller.grid(column=4, row=0, sticky=(tkinter.N, tkinter.S))
self.fontlistbox.bind("<Button-1>", self.endre_font_listbox)
self.fontlistbox.bind("<Key-Return>", self.endre_font_listbox)
tkinter.mainloop()
def endre_font_listbox(self, hendelse):
valgte_indekser = self.fontlistbox.curselection()
print(valgte_indekser)
if valgte_indekser:
font_tekst = self.fontene[valgte_indekser[0]]
ny_font = font.Font(size=10, weight=bold_tekst, slant=italic_tekst, family=font_tekst)
self.tekstomraadet.config(font=ny_font)
if __name__ == "__main__":
gui = EnkelTeksteditor()
当您绑定到 <Button-1>
时,绑定的函数会在列表框有机会处理事件之前触发。这就是为什么您可以 return "break"
从绑定函数中停止事件。
在此处查看此代码:
import tkinter as tk
def return_break(event):
return "break"
root = tk.Tk()
text = tk.Text(root)
text.pack()
text.bind("<Key>", return_break)
root.mainloop()
只要你按下一个按钮,tkinter 就会调用你的函数 return_break
。该函数 returns "break"
告诉 tkinter 停止事件。这就是事件永远不会到达 text
小部件的原因。
类似地,当您绑定到 <Button-1>
时,您的函数会在列表框了解鼠标按下之前被调用。当您将它更改为 <ButtonRelease-1>
时,它起作用了,因为当您释放鼠标按钮时调用该函数并且列表框有时间处理 <Button-1>
事件。
另一种解决问题的方法是将您的代码更改为:
def endre_font_listbox(self, hendelse):
self.fontlistbox.after(1, self._endre_font_listbox)
def _endre_font_listbox(self):
valgte_indekser = self.fontlistbox.curselection()
print(valgte_indekser)
...
这告诉 tkinter 在调用您的函数之前等待 1 毫秒(这是列表框处理事件的足够时间)(_endre_font_listbox
)。
永远记住,您的函数在小部件的处理程序之前被调用。
列表框在选择发生变化时生成一个特殊的虚拟事件。您应该使用它而不是绑定到密钥。因为您要绑定到键 press,所以您的绑定会在默认绑定之前触发。导致选择更改的是默认绑定。
绑定虚拟事件的方法如下:
self.fontlistbox.bind("<<ListboxSelect>>", self.endre_font_listbox)
除了解决调用事件回调的顺序问题外,这还有一个额外的好处,即如果用户使用键盘更改选择,它也将起作用。
我正在尝试使用带有来自 tkinter 的字体的列表框来更改文本字段的字体。它工作正常,除了第一次选择列表框。
第一次单击列表框时,我会收到此错误消息 IndexError: tuple index out of range
,带有列表框选择的元组将是 ()
。但是,下一个选择将完全正常。然后我会得到一个这样的元组 (number,)
这是什么原因?如果我绑定 <Double-Button-1>
而不是 <Button-1>
,那么第一个选择(双击列表框)工作正常。
import tkinter
from tkinter import font
class EnkelTeksteditor:
def __init__(self):
self.hovedvindu = tkinter.Tk()
self.tekstomraadet = tkinter.Text(self.hovedvindu, height=10, width=30)
self.tekstomraadet.grid(column=0, row=0)
self.scrollbar = tkinter.Scrollbar(self.hovedvindu, orient=tkinter.VERTICAL,
command=self.tekstomraadet.yview)
self.scrollbar.grid(column=1, row=0, sticky=(tkinter.N, tkinter.S))
self.tekstomraadet.config(yscrollcommand=self.scrollbar.set)
self.hovedvindu.title("Enkel teksteditor")
self.fontlistbox = tkinter.Listbox(self.hovedvindu, selectmode=tkinter.SINGLE)
self.fontene = font.families()
for fonten in self.fontene:
self.fontlistbox.insert(tkinter.END, fonten)
self.fontlistbox.grid(column=3, row=0, sticky=(tkinter.N, tkinter.S))
self.fontscroller = tkinter.Scrollbar(self.hovedvindu, orient=tkinter.VERTICAL,
command=self.fontlistbox.yview)
self.fontlistbox.config(yscrollcommand=self.fontscroller.set)
self.fontscroller.grid(column=4, row=0, sticky=(tkinter.N, tkinter.S))
self.fontlistbox.bind("<Button-1>", self.endre_font_listbox)
self.fontlistbox.bind("<Key-Return>", self.endre_font_listbox)
tkinter.mainloop()
def endre_font_listbox(self, hendelse):
valgte_indekser = self.fontlistbox.curselection()
print(valgte_indekser)
if valgte_indekser:
font_tekst = self.fontene[valgte_indekser[0]]
ny_font = font.Font(size=10, weight=bold_tekst, slant=italic_tekst, family=font_tekst)
self.tekstomraadet.config(font=ny_font)
if __name__ == "__main__":
gui = EnkelTeksteditor()
当您绑定到 <Button-1>
时,绑定的函数会在列表框有机会处理事件之前触发。这就是为什么您可以 return "break"
从绑定函数中停止事件。
在此处查看此代码:
import tkinter as tk
def return_break(event):
return "break"
root = tk.Tk()
text = tk.Text(root)
text.pack()
text.bind("<Key>", return_break)
root.mainloop()
只要你按下一个按钮,tkinter 就会调用你的函数 return_break
。该函数 returns "break"
告诉 tkinter 停止事件。这就是事件永远不会到达 text
小部件的原因。
类似地,当您绑定到 <Button-1>
时,您的函数会在列表框了解鼠标按下之前被调用。当您将它更改为 <ButtonRelease-1>
时,它起作用了,因为当您释放鼠标按钮时调用该函数并且列表框有时间处理 <Button-1>
事件。
另一种解决问题的方法是将您的代码更改为:
def endre_font_listbox(self, hendelse):
self.fontlistbox.after(1, self._endre_font_listbox)
def _endre_font_listbox(self):
valgte_indekser = self.fontlistbox.curselection()
print(valgte_indekser)
...
这告诉 tkinter 在调用您的函数之前等待 1 毫秒(这是列表框处理事件的足够时间)(_endre_font_listbox
)。
永远记住,您的函数在小部件的处理程序之前被调用。
列表框在选择发生变化时生成一个特殊的虚拟事件。您应该使用它而不是绑定到密钥。因为您要绑定到键 press,所以您的绑定会在默认绑定之前触发。导致选择更改的是默认绑定。
绑定虚拟事件的方法如下:
self.fontlistbox.bind("<<ListboxSelect>>", self.endre_font_listbox)
除了解决调用事件回调的顺序问题外,这还有一个额外的好处,即如果用户使用键盘更改选择,它也将起作用。