为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕婉清6462132 2024-01-24 15:30:49
我制作了一个继承自ttk.Entry. 问题是,它只适用于实例化一次,如果我为同一个条目创建两个实例,它将开始显示一些奇怪的效果。类及其用法:import tkinter as tkfrom tkinter import ttkclass PlaceholderEntry(ttk.Entry):    '''    Custom modern Placeholder Entry box, takes positional argument master and placeholder\n    Use acquire() for getting output from entry widget\n    Use shove() for inserting into entry widget\n    Use remove() for deleting from entry widget\n    Use length() for getting the length of text in the widget\n    BUG 1: Possible bugs with binding to this class\n    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单击其中一个条目并将焦点更改到下一个条目时,您可以注意到两个实例在某种程度上有关联的“行为”?尽管单个实例工作得很好。这是我的第一个 OOP 项目,所以我不确定出了什么问题。先谢谢了
查看完整描述

1 回答

?
BIG阳

TA贡献1859条经验 获得超6个赞

您有两个问题,都与您的类依赖于全局数据这一事实有关:您正在使用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. 您可以绑定 on<KeyRelease>而不是,<Key>因为它将在默认绑定插入字符后触发。

  2. 您可以在默认绑定之后添加自定义绑定标签,以便首先发生默认绑定,然后再发生您的绑定。

  3. 你可以坚持使用bind_all. 在这种情况下,您只需要进行一次绑定,而不是每个小部件一次,并且您需要调整您的函数以使用event.widget而不是self.

查看完整回答
反对 回复 2024-01-24
  • 1 回答
  • 0 关注
  • 87 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信