Как сделать, чтобы tkinter переключал класс, производный от tkinter.Button? - PullRequest
1 голос
/ 20 июня 2019

Я пытаюсь создать класс кнопок с переключателем , основываясь на объекте tkinter.Button. Для этого я использую этот ответ StackOverflow и эти примеры кода.

Проблема в том, что я получаю желаемое поведение переключения с кнопки только после того, как дважды щелкну по ней ; первые два клика, он не принимает self.config(relief="sunken"). Я попытался использовать 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 бит)
  • Ткинтер 8,6

1 Ответ

1 голос
/ 20 июня 2019

Похоже, проблема в том, что параметр bg не определен при первом создании кнопки; он получает значение, назначенное только после первого нажатия кнопки.

Тогда трудно следовать логике переключения: у вас есть логическое значение self.toggled, но вы проверяете, является ли кнопка sunken или нет, чтобы различать состояния ...

Я реорганизовал логику, чтобы ей было легче следовать; в конце концов, переключение - это двоичное изменение из одного состояния в другое. Поэтому я поместил определение состояний ON и OFF в теле класса (в два словаря классов), и код переключает две конфигурации при переключении.

В 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...