创建这个类的两个实例,会产生奇怪的行为tkinter-python



我创建了一个从ttk.Entry继承的自定义占位符条目类。问题是,它只适用于实例化一次,如果我为同一条目创建两个实例,它将开始显示一些奇怪的效果。

类别及其用途:

import tkinter as tk
from tkinter import ttk
class PlaceholderEntry(ttk.Entry):
'''
Custom modern Placeholder Entry box, takes positional argument master and placeholdern
Use acquire() for getting output from entry widgetn
Use shove() for inserting into entry widgetn
Use remove() for deleting from entry widgetn
Use length() for getting the length of text in the widgetn
BUG 1: Possible bugs with binding to this classn
BUG 2: Anomalous behaviour with config or configure method
'''
def __init__(self, master, placeholder, **kwargs):
# style for ttk widget
self.s = ttk.Style()
self.s.configure('my.TEntry')
# init entry box
ttk.Entry.__init__(self, master, style='my.TEntry', **kwargs)
self.text = placeholder
self.__has_placeholder = False  # placeholder flag
# add placeholder if box empty
self._add()
# bindings of the widget
self.bind('<FocusIn>', self._clear)
self.bind('<FocusOut>', self._add)
self.bind_all('<Key>', self._normal)
self.bind_all('<Button-1>', self._cursor)
def _clear(self, *args):  # method to remove the placeholder
if self.get() == self.text and self.__has_placeholder:  # remove placeholder when focus gain
self.delete(0, tk.END)
self.s.configure('my.TEntry', foreground='black',
font=(0, 0, 'normal'))
self.__has_placeholder = False  # set flag to false
def _add(self, *args):  # method to add placeholder
if self.get() == '' and not self.__has_placeholder:  # if no text add placeholder
self.s.configure('my.TEntry', foreground='grey',
font=(0, 0, 'bold'))
self.insert(0, self.text)  # insert placeholder
self.icursor(0)  # move insertion cursor to start of entrybox
self.__has_placeholder = True  # set flag to true
def _normal(self, *args):  # method to set the text to normal properties
self._add()  # if empty add placeholder
if self.get() == self.text and self.__has_placeholder:  # clear the placeholder if starts typing
self.bind('<Key>', self._clear)
self.icursor(-1)  # keep insertion cursor to the end
else:
self.s.configure('my.TEntry', foreground='black',
font=(0, 0, 'normal'))  # set normal font
def acquire(self):  # custom method to get the text
if self.get() == self.text and self.__has_placeholder:
return 'None'
else:
return self.get()
def shove(self, index, string):  # custom method to insert into entry
self._clear()
self.insert(index, string)
def remove(self, first, last):  # custom method to remove from entry
if self.get() != self.text:
self.delete(first, last)
self._add()
elif self.acquire() == self.text and not self.__has_placeholder:
self.delete(first, last)
self._add()
def length(self):
if self.get() == self.text and self.__has_placeholder:
return 0
else:
return len(self.get())
def _cursor(self, *args):  # method to not allow user to move cursor when placeholder exists
if self.get() == self.text and self.__has_placeholder:
self.icursor(0)
# MRE
if __name__ == '__main__':
root = tk.Tk()

e1 = PlaceholderEntry(root,'First Entry')
e1.pack()
e2 = PlaceholderEntry(root,'Second Entry')
e2.pack()

root.mainloop()

在这里,点击其中一个条目并将焦点更改为下一个条目时,您可以注意到";行为;这两个实例在某种程度上有联系?尽管一个实例可以很好地工作。这是我的第一个OOP项目,所以我不确定出了什么问题。

提前感谢:D

您有两个问题,都与您的类依赖于全局数据的事实有关:您使用的是bind_all,它有效地创建了全局绑定,并且您对普通和占位符都使用相同的样式,但样式是全局的。

第一个问题是使用bind_all。每次创建实例时,该实例的bind_all都会替换前一个实例的绑定。两个实例都将继承一个绑定,该绑定只调用上一个创建的实例的_normal_cursor方法。

根据一般经验,像这样的类应该始终只在自身上创建绑定。您需要将self.bind_all更改为仅self.bind

self.bind('<Key>', self._normal)
self.bind('<Button-1>', self._cursor)

第二个问题是你对风格的使用。在两种状态下,两个小部件都使用一个样式,但由于样式是全局的,当您更改一个小部件中的样式时,它会影响另一个。

您不应该重新配置一个样式,而应该配置两个样式并在它们之间切换。例如,可以为正常情况创建my.TEntry样式,为要显示占位符时创建placeholder.TEntry样式。

首先在__init__方法中配置样式:

def __init__(self, master, placeholder, **kwargs):
# style for ttk widget
self.s = ttk.Style()
self.s.configure('my.TEntry', foreground='black', font=(0, 0, 'normal'))
self.s.configure('placeholder.TEntry', foreground='grey', font=(0, 0, 'bold'))

接下来,与其重新配置样式定义,不如交换小部件上的样式。例如,_clear看起来像这样:

def _clear(self, *args):  # method to remove the placeholder
if self.get() == self.text and self.__has_placeholder:  
self.configure(style="my.TEntry")
...

类似地,_add应该看起来像这样:

def _add(self, *args):  # method to add placeholder
if self.get() == '' and not self.__has_placeholder:  # if no text add placeholder
self.configure(style="placeholder.TEntry")
...

最后,您的逻辑中有一个错误,可能是由于误解了绑定的工作方式。当您绑定到<Key>时,该绑定发生在默认绑定之前。因此,您在self._add中检查条目是否为空的操作发生在插入刚才键入的密钥之前。因此,代码认为条目小部件是空的,并将样式切换为具有占位符。因此,它添加占位符文本,然后进行键的默认处理并插入键。

这个问题至少有三种解决办法。

  1. 您可以在<KeyRelease>而不是<Key>上绑定,因为这将在默认绑定插入字符后激发
  2. 您可以在默认绑定之后添加一个自定义bindtag,以便先进行默认绑定,然后再进行绑定
  3. 你可以坚持使用bind_all。在这种情况下,您只需要执行一次绑定,而不是每个小部件执行一次,并且您需要调整函数以使用event.widget而不是self

此答案和此答案对绑定标记的工作方式进行了更详细的描述。

相关内容

  • 没有找到相关文章

最新更新