如何通过从tkinter.Button派生tkinter切换按钮类?

时间:2019-06-20 13:27:54

标签: python-3.x button tkinter

我试图通过从tkinter.Button对象派生一个 toggle 按钮类。为此,我使用this StackOverflow答案和these代码示例。

问题是,只有在我两次单击按钮之后,我才能从按钮获得所需的切换行为;前两次单击,不会执行self.config(relief="sunken")。我尝试使用this答案中的command关键字参数示例,并且从一开始就有效。

import tkinter as tk

class ToggleButton(tk.Button):
    def __init__(self, parent=None, toggle_text="Toggled", toggle_bg_color="green", **kwargs):
        tk.Button.__init__(self, parent, **kwargs)

        self.toggled = False

        self.default_bg_color = self['bg']
        self.default_text = self["text"]

        self.toggle_bg_color = toggle_bg_color
        self.toggle_text = toggle_text

        self.bind("<Button-1>", self.toggle, add="+")

    def toggle(self, *args):
        if self["relief"] == "sunken":
            self["bg"] = self.default_bg_color
            self["text"] = self.default_text
            self.config(relief="raised")
            # self["relief"] = "raised"
            self.toggled = False
        else:
            self["bg"] = self.toggle_bg_color
            self["text"] = self.toggle_text
            # self["relief"] = "sunken"
            self.config(relief="sunken")
            self.toggled = True

def button_placeholder():
    print("TO BE IMPLEMENTED")


root = tk.Tk()

button = ToggleButton(parent=root,
                toggle_text="ON", toggle_bg_color="green",
                text="OFF", command=button_placeholder)
button.pack()
root.mainloop()

以下是多次点击后按钮行为的屏幕截图 Screenshots of a button with clicks

在前两次单击按钮后,发生预期的行为。但是,如果用户将注意力集中在另一个窗口上(例如,通过最小化tkinter窗口),然后又返回,则前两次单击仍不会导致所需的行为。

能不能解释一下?如果没有,有人可以提供一种解决方案,使我在切换按钮时具有一致的行为吗?

有关我的系统的信息

  • Windows 10; 64位
  • Python 3.7.3(64位)
  • Tkinter 8.6

1 个答案:

答案 0 :(得分:1)

您似乎遇到的问题是,首次创建按钮时未定义bg参数;只会在第一次按下按钮时分配一个值。

然后,很难遵循切换逻辑:您有一个self.toggled布尔值,但您正在测试按钮是否为sunken以便区分状态...

我重新组织了逻辑,使其更易于遵循;毕竟,切换是从一种状态到另一种状态的二进制变化。因此,我将ONOFF状态的定义放在了类的主体中(分为两个类字典),并且代码在切换时交换了两个配置。

在Windows上:

import tkinter as tk


class ToggleButton(tk.Button):

    ON_config = {'bg': 'green',
                 'text': 'button is ON',
                 'relief': 'sunken',
                 }
    OFF_config =  {'bg': 'white',
                 'text': 'button is OFF',
                 'relief': 'raised',
                 }

    def __init__(self, parent, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.toggled = False
        self.config = self.OFF_config
        self.config_button()

        self.bind("<Button-1>", self.toggle)

    def toggle(self, *args):
        if self.toggled:   # True = ON --> toggle to OFF
            self.config = self.OFF_config
        else:
            self.config = self.ON_config
        self.toggled = not self.toggled
        return self.config_button()

    def config_button(self):
        self['bg'] = self.config['bg']
        self['text'] = self.config['text']
        self['relief'] = self.config['relief']
        return "break"

    def __str__(self):
        return f"{self['text']}, {self['bg']}, {self['relief']}"


def button_placeholder():
    print('toggling now!')


if __name__ == '__main__':

    root = tk.Tk()

    button = ToggleButton(root)
    button.pack()

    root.mainloop()

在OSX上:

修复了按钮方面的问题,使用tk.Label可以模仿所需的行为:

import tkinter as tk


class ToggleButtonLBL(tk.Label):

    ON_config = {'bg': 'green',
                 'text': 'button is ON',
                 'relief': 'sunken',
                 }
    OFF_config =  {'bg': 'white',
                 'text': 'button is OFF',
                 'relief': 'raised',
                 }

    def __init__(self, parent, *args, command=None, **kwargs):
        super().__init__(parent, *args, **kwargs)

        self.toggled = False
        self.config = self.OFF_config
        self.config_button()

        self.bind("<Button-1>", self._toggle_helper)
        self.bind("<ButtonRelease-1>", self._toggle)
        self.command = command

    def _toggle_helper(self, *args):
        return 'break'

    def _toggle(self, dummy_event):
        self.toggle()
        self.cmd()

    def toggle(self, *args):
        if self.toggled:   # True = ON --> toggle to OFF
            self.config = self.OFF_config
        else:
            self.config = self.ON_config
        self.toggled = not self.toggled
        self.config_button()
        return 'break'

    def config_button(self):
        self['bg'] = self.config['bg']
        self['text'] = self.config['text']
        self['relief'] = self.config['relief']
        return "break"

    def __str__(self):
        return f"{self['text']}, {self['bg']}, {self['relief']}"

    def cmd(self):
        self.command()


def button_placeholder():
    print('toggling now!')


if __name__ == '__main__':

    root = tk.Tk()

    button = ToggleButtonLBL(root, command=button_placeholder)
    button.pack()

    root.mainloop()