Python: цвет фона изображения на кнопке, пребывающей в режиме disabled
Есть в форме кнопка tkinter.Button. Ничего необычного в конфигурации нет:
mybutton.configure(background = "blue", disabledforeground = "white", foreground = "white", image = "image.png")
С кнопкой связан длительный процесс. Для того, чтоб не дать пользователю опять нажать на кнопку в процессе, я блокирую её:
mybutton.configure(state = "disabled")
Когда процесс завершается, я её деблокирую:
mybutton.configure(state = "normal")
Мне кажется, что такой путь предотвращения проблем с повторным запуском, выглядит более-менее. Но суть не в нём. Цвет фона кнопки остаётся неизменным в блокированном состоянии, цвет текста тоже. Но фон изображения меняется на серый. Фон изображения сам по себе - прозрачный. Это позволяет мне легко менять цвет фона кнопок, использовать более интересную цветовую стилизацию всего приложения, чем просто скучно-стандартная. Можно ли как-то предотвратить "дружескую" помощь tkinter по изменению цвета заблокированных кнопок? Я достаточно долго рылся в документации, ничего не нашёл. Варианта только два:
- Отказаться от tkinter вообще. Это не так быстро и отсутствие "эффектов" тоже не гарантировано.
- Делать серой всю кнопку. Во-первых, почему только в пользу серого цвета? А если завтра по умолчанию "засеривание" превратится в "засинивание"? Да и "зесеривание" в контексте стилизации цветов в моих приложениях выглядит, мягко говоря, уродливо.
Я не рассчитываю, что решение есть, но на всякий случай... Вдруг я что-то упустил. ttk, стили ttk - уже пробовал, ноль.
Ответы (2 шт):
Тогда предлагаю унаследоваться от tk.Button и просто закостомить свою кнопку:
Через
kwargs.pop('command', None)
- вырезать заданную функцию для дальнейшего её вызова.Через
self.bind("<Button-1>", self.on_click)
- перебиндить на внутренний метод, тем самым перехватывать событие нажатия.В методе
on_click
в зависимости от флага уже решать блокировать событие или вызывать его.
import tkinter as tk
class CustomButton(tk.Button):
def __init__(self, master=None, normal_image=None, disabled_image=None, **kwargs):
self.normal_image = normal_image
self.disabled_image = disabled_image
self.is_disabled = False
self.command = kwargs.pop('command', None)
super().__init__(master, image=self.normal_image, **kwargs)
self.bind("<Button-1>", self.on_click)
def on_click(self, event):
if self.is_disabled:
return "break" # Блок события при заблокированной кнопке
# Вызов привязанной функции
if self.command:
self.command()
def disable(self):
self.is_disabled = True
self.config(image=self.disabled_image)
def enable(self):
self.is_disabled = False
self.config(image=self.normal_image)
root = tk.Tk()
normal_image = tk.PhotoImage(file=r"C:\Users\Amgarak\Desktop\K_Enter.png")
disabled_image = tk.PhotoImage(file=r"C:\Users\Amgarak\Desktop\K_Enter2.png")
def on_button_click():
print("Button clicked!")
custom_button = CustomButton(root, normal_image=normal_image, disabled_image=disabled_image, command=on_button_click)
custom_button.pack()
def disable_button():
custom_button.disable()
def enable_button():
custom_button.enable()
button_disable = tk.Button(root, text="Disable Button", command=disable_button)
button_disable.pack()
button_enable = tk.Button(root, text="Enable Button", command=enable_button)
button_enable.pack()
root.mainloop()
Дополнительный пример для наглядности:
import tkinter as tk
class CustomButton(tk.Button):
def __init__(self, master=None, normal_image=None, disabled_image=None, **kwargs):
self.normal_image = normal_image
self.disabled_image = disabled_image
self.is_disabled = False
self.command = kwargs.pop('command', None)
super().__init__(master, image=self.normal_image, **kwargs)
self.bind("<Button-1>", self.on_click)
def on_click(self, event):
if self.is_disabled:
return "break" # Блок события при заблокированной кнопке
# Вызов привязанной функции
if self.command:
self.command()
def disable(self):
self.is_disabled = True
self.config(image=self.disabled_image)
def enable(self):
self.is_disabled = False
self.config(image=self.normal_image)
root = tk.Tk()
normal_image = tk.PhotoImage(file=r"C:\Users\Amgarak\Desktop\K_Enter.png")
disabled_image = tk.PhotoImage(file=r"C:\Users\Amgarak\Desktop\K_Enter2.png")
def click_button1():
button1.disable()
print("Button1 clicked!")
root.after(4000, button1.enable)
def click_button2():
button2.disable()
print("Button2 clicked!")
root.after(4000, button2.enable)
def click_button3():
button3.disable()
print("Button3 clicked!")
root.after(4000, button3.enable)
button1 = CustomButton(root, normal_image=normal_image, disabled_image=disabled_image, command=click_button1)
button1.pack()
button2 = CustomButton(root, normal_image=normal_image, disabled_image=disabled_image, command=click_button2)
button2.pack()
button3 = CustomButton(root, normal_image=normal_image, disabled_image=disabled_image, command=click_button3)
button3.pack()
root.mainloop()
Решение, компактное и простое, хотя и обходное, я нашёл сам. Tkinter и ttk не написаны как следует и уже не будут дописаны. Значит, надо обходить проблему. Итак, в форме есть кнопка, связанная с длительной процедурой:
mybutton.configure(command = lambda: mylongprocedure())
Раньше проблема решалась так. Для того, чтоб пользователь не нажал ещё раз на эту же кнопку во время выполнения, кнопку приходилось блокировать:
def mylongprocedure:
# Блокирую кнопку
mybutton.configure(state = "disabled")
.......................
Код процедуры. Кнопка заблокирована, изображение на ней "засерено". Именно это и не нравится.
.......................
# Деблокирую кнопку
mybutton.configure(state = "normal")
Теперь проблема будет решаться так:
def mylongprocedure:
# Запоминаю связь кнопки с этой процедурой
v_command = mybutton.cget("command")
# Обнуляю связь кнопки с этой процедурой
mybutton.configure(command = "")
.......................
Код процедуры. Кнопка не заблокирована, но нажатие на неё не вызывает параллельное выполнение этой процедуры. Необходимости в блокировке кнопки нет, а значит и фон изображения не меняется.
.......................
# Восстанавливаю связь кнопки с этой процедурой
mybutton.configure(command = v_command)