【问题标题】:Python: Changing ttk button color depending on current color?Python:根据当前颜色更改 ttk 按钮颜色?
【发布时间】:2018-03-16 22:35:38
【问题描述】:

我第一次尝试在 ttk 中使用样式做一些事情。我刚才的目标是在鼠标经过时突出显示一些样式按钮的背景颜色,但是按钮有一些状态,并且在不同的时刻会有不同的颜色,所以我尝试了这个:

按钮代码

from PIL.ImageTk import PhotoImage
import tkinter.ttk as ttk
from random import random

class ImgButton(ttk.Button):
    def __init__(self, master=None, **kw):
        super().__init__(master, **kw)
        self.img = kw.get('image')

class DiceFrame(ttk.Frame):
    def __init__(self, master, *args, **kwargs):
        super().__init__(master, *args, **kwargs)

        currentImg = PhotoImage(file='anyFileYouWant.jpg')

        style = ttk.Style()
        style.configure('Die.TButton',
                        background='red',
                        borderwidth=8,
                        )

        def active_color(self):
            # Test code. Final goal is get the current color and modify it
            return random.choice(['blue', 'yellow', 'black', 'purple', 'cyan', 'brown', 'orange'])

        style.map('Die.TButton',
                  background=[('active', active_color), ])

        # Don't worry. ImgButton extends the regular ttk Button. Almost equal
        button = ImgButton(master, image=currentImg, style="Die.TButton")
        button.pack(side=tk.LEFT)

if __name__ == "__main__":
    root = tk.Tk()
    DiceFrame(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

尝试在按钮上设置随机背景颜色。

我的最终目标是获取当前按钮的颜色并设置相同的颜色但更浅。例如,如果按钮为红色,则当鼠标移到按钮上时,将其设置为较浅的红色。如果它是黄色的,则为浅黄色,等等......

这种尝试只会在按钮上显示一些奇怪的东西,您可以使用代码进行试验。所以我不知道如何在那里动态设置一个返回有效颜色的函数。

【问题讨论】:

  • 对不起。我添加了一些导入和一个类,但我现在必须走了。我稍后会完成源代码。
  • @j_4321 刚刚完成

标签: python-3.x tkinter ttk


【解决方案1】:

您不能像以前那样为活动背景提供函数而不是颜色:

style.map('Die.TButton', background=[('active', active_color), ])

这就是按钮在激活时会有奇怪行为的原因。

无论如何,每次你想改变按钮背景时,你都必须配置'Die.TButton'样式,这样你就可以同时改变活动背景:

import tkinter as tk
import tkinter.ttk as ttk
import random


def change_style():
    color = random.choice(['red', 'blue', 'yellow', 'dark gray', 'purple', 'cyan', 'brown', 'orange'])
    style.configure('Die.TButton', background=color)
    style.map('Die.TButton', background=[('active', active_color(color))])


def active_color(color):
    c = root.winfo_rgb(color)
    r = c[0] / 65535 * 255
    g = c[1] / 65535 * 255
    b = c[2] / 65535 * 255
    r += (255 - r) / 2
    g += (255 - g) / 2
    b += (255 - b) / 2
    return ("#%2.2x%2.2x%2.2x" % (round(r), round(g), round(b))).upper()


root = tk.Tk()

style = ttk.Style(root)

button = ttk.Button(root, text='Test', style='Die.TButton')
change_style()
button.pack()

ttk.Button(root, command=change_style, text='Change style').pack(padx=4, pady=10)

root.mainloop()

active_color 使用winfo_rgb 为活动背景返回较浅版本的颜色,以获取颜色的 RGB 代码。

【讨论】:

  • 我刚刚读过它。我想我应该使用“bind()”创建一个新的事件处理程序,将 changeStyle 绑定到“活动”事件。我不应该吗?
  • @madtyn 我不明白你在说什么事件。我对您的问题的理解是,在您的程序中的某个时刻,您更改了按钮的背景颜色,并且您希望活动颜色适应这种新颜色。
  • 是的,只要鼠标移到按钮上。我几乎可以肯定有一个事件。如果您查看我的代码,我尝试使用状态的 map() 函数,但是这些状态会因事件而发生变化。我想它必须存在一个将状态更改为“活动”的事件
  • style.map('Die.TButton', background=[('active', active_color(color))]) 负责这一点,您无需手动设置绑定来更改按钮的颜色。
【解决方案2】:

ttk 按钮外观由主题(3D/Color-alt/classic/default、Color-clam)驱动。不设置/其他使按钮变平/灰色,设置不会改变。 要让一个ttk TButton 改变颜色可以使用map 来实现。 3D 外观需要边框宽度。只有 Classic 使用高光形成外圈。以下 s.map 的背景逻辑:Active & Pressed(黄色)、!Active(绿色)、Active & !Pressed(青色)。救济可以单独定义。修改这些按钮方面不需要任何功能。但是,highlightcolor 必须使用 s.configure 更新。框架只能调用一个主题。

import tkinter as tk
from tkinter import ttk
root=tk.Tk();
s = ttk.Style(); 
s.theme_use('classic');
s.configure('zc.TButton',borderwidth='20')
s.configure('zc.TButton',highlightthickness='10')
s.configure('zc.TButton',highlightcolor='pink')
s.map('zc.TButton',background=[('active', 'pressed', 'yellow'),('!active','green'), ('active','!pressed', 'cyan')])
s.map('zc.TButton',relief=[('pressed','sunken'),('!pressed','raised')]);
calc_button=ttk.Button(root, text="classic", style='zc.TButton');
calc_button.grid(column=0,row=0,sticky='nsew');
root.mainloop() 

【讨论】:

    【解决方案3】:

    我的最终解决方案是这样的:

    所有关于颜色的行为都封装在按钮小部件中。

    我使用处理程序控制事件,该处理程序将活动状态的背景颜色更改为较浅的颜色。

    每当颜色改变时,它都是通过我的一个函数来实现的,所以我用 .generate_event() 触发 '' 事件,改变颜色并取消绑定当前处理程序以进行突出显示,然后绑定一个新处理程序用于突出显示替换前者。

    我为样式相关的方法和函数制作了一个辅助的、可重用的模块:

    styleUtils

    import tkinter as tk
    import tkinter.ttk as ttk
    import random as rnd
    
    style = None
    
    
    def random_color():
        """
        Returns a random color as a string
        :return: a color
        """
        def r():
            return rnd.randint(0, 0xffff)
        return '#{:04x}{:04x}{:04x}'.format(r(), r(), r())
    
    def get_style(master=None):
        """
        Returns the style object instance for handling styles
        :param master: the parent component
        :return: the style
        """
        global style
        if not style:
            style = ttk.Style(master) if master else ttk.Style()
    
        return style
    
    
    def get_style_name(widget):
        """
        Returns the the name of the current style applied on this widget
        :param widget: the widget
        :return: the name of the style
        """
        # .config('style') call returns the tuple
        # ( option name, dbName, dbClass, default value, current value)
        return widget.config('style')[-1]
    
    
    def get_background_color(widget):
        """
        Returns a string representing the background color of the widget
        :param widget: a widget
        :return: the color of the widget
        """
        global style
        color = style.lookup(get_style_name(widget), 'background')
        return color
    
    
    def highlighted_rgb(color_value):
        """
        Returns a slightly modified rgb value
        :param color_value: one of three possible rgb values
        :return: one of three possible rgb values, but highlighted
        """
        result = (color_value / 65535) * 255
        result += (255 - result) / 2
        return result
    
    
    def highlighted_color(widget, color):
        """
        Returns a highlighted color from the original entered
        :param color: a color
        :return: a highlight color for the one entered
        """
        c = widget.winfo_rgb(color)
        r = highlighted_rgb(c[0])
        g = highlighted_rgb(c[1])
        b = highlighted_rgb(c[2])
        return ("#%2.2x%2.2x%2.2x" % (round(r), round(g), round(b))).upper()
    
    
    def change_highlight_style(event=None):
        """
        Applies the highlight style for a color
        :param event: the event of the styled widget
        """
        global style
        widget = event.widget
        current_color = get_background_color(widget)
        color = highlighted_color(event.widget, current_color)
        style.map(get_style_name(widget), background=[('active', color)])
    

    现在可能需要稍微更改调用代码以删除不必要的代码,但这将立即生效。

    widgets.py(按钮代码)

    import os
    import tkinter as tk
    import tkinter.ttk as ttk
    from PIL.ImageTk import PhotoImage
    
    from user.myProject.view import styleUtils
    
    class ImgButton(ttk.Button):
        """
        This has all the behaviour for a button which has an image
        """
        def __init__(self, master=None, **kw):
            super().__init__(master, **kw)
            self._img = kw.get('image')
            # TODO Replace this temporal test handler for testing highlight color
            self.bind('<Button-1>', self.change_color)
    
        def change_color(self, __=None):
            """
            Changes the color of this widget randomly
            :param __: the event, which is no needed
            """
            import random as rnd
            #Without this, nothing applies until the mouse leaves the widget
            self.event_generate('<Leave>')
            self.set_background_color(rnd.choice(['black', 'white', 'red', 'blue',
                                                  'cyan', 'purple', 'green', 'brown',
                                                  'gray', 'yellow', 'orange', 'cyan',
                                                  'pink', 'purple', 'violet']))
            self.event_generate('<Enter>')
    
        def get_style_name(self):
            """
            Returns the specific style name applied for this widget
            :return: the style name as a string
            """
            return styleUtils.get_style_name(self)
    
        def set_background_color(self, color):
            """
            Sets this widget's background color to that received as parameter
            :param color: the color to be set
            """
            styleUtils.get_style().configure(self.get_style_name(), background=color)
            # If the color changes we don't want the current handler for the old color anymore
            self.unbind('<Enter>')
            # We replace the handler for the new color
            self.bind('<Enter>', self.change_highlight_style)
    
        def get_background_color(self):
            """
            Returns a string representing the background color of the widget
            :return: the color of the widget
            """
            return styleUtils.get_style().lookup(self.get_style_name(), 'background')
    
        def change_highlight_style(self, __=None):
            """
            Applies the highlight style for a color
            :param __: the event, which is no needed
            """
            current_color = self.get_background_color()
            # We get the highlight lighter color for the current color and set it for the 'active' state
            color = styleUtils.highlighted_color(self, current_color)
            styleUtils.get_style().map(self.get_style_name(), background=[('active', color)])
    

    调用代码

    import tkinter as tk
    import tkinter.ttk as ttk
    
    from widgets import ImgButton
    
    
    class DiceFrame(ttk.Frame):
        def __init__(self, master, *args, **kwargs):
            super().__init__(master, *args, **kwargs)
            current_style = 'Die.TButton'
    
            style = ttk.Style()
            style.configure(current_style,
                            borderwidth=6,
                            )
    
            button = ImgButton(master, style=current_style)
            button.pack(side=tk.LEFT)
    
    
    if __name__ == "__main__":
        root = tk.Tk()
        DiceFrame(root).pack(side="top", fill="both", expand=True)
        root.mainloop()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-03
      相关资源
      最近更新 更多