没有显示Tkinter ttk背景样式的定制

时间:2018-02-22 17:42:17

标签: python tkinter ttk

在以下代码中,show_widget_validity()函数或者应用仅对窗口小部件的现有样式的背景颜色进行更改的自定义样式,或者恢复原始样式。这是一个库例程,因此不能完全控制样式。背景颜色似乎正确重新配置,如每次更改后在条目窗口小部件中报告的背景样式说明所示。但是,窗口小部件的实际背景颜色不会更改。

在使用Python 2.7和3.6的Linux上以及在使用Python 2.7的Windows上可以看到此行为;我还没有测试过其他环境。

有关此行为原因的任何线索,或者对其进行必要的代码更改,我们将不胜感激。

编辑:使用" fieldbackground"而不是"背景"在Linux上有效,但在Windows上无效,并且不允许在禁用状态下修改背景颜色。

try:
    import Tkinter as tk
except:
    import tkinter as tk
try:
    import ttk
except:
    from tkinter import ttk


def show_widget_validity(widget, is_valid):
    invalid_color = "#fff5ff"
    invalid_disabled_color = "#f6f0f6"
    sname = widget["style"] or widget.winfo_class()
    if sname.startswith("Invalid."):
        if is_valid:
            widget['style'] = sname[8:]
    else:
        if not is_valid:
            invname = "Invalid." + sname
            ttk.Style().configure(invname, background=[('disabled', invalid_disabled_color), ('active', invalid_color)])
            # Simpler, but also ineffective:
            #ttk.Style().configure(invname, background=invalid_color)
            widget['style'] = invname

def show_invalid():
    show_widget_validity(entry, False)
    entry_var.set(ttk.Style().lookup(entry["style"] or entry.winfo_class(), "background"))

def show_valid():
    show_widget_validity(entry, True)
    entry_var.set(ttk.Style().lookup(entry["style"] or entry.winfo_class(), "background"))

root = tk.Tk()
root.title("Testing of Style Customization")

appframe = tk.Frame(root, padx=12, pady=12)
appframe.pack(expand=True, fill=tk.BOTH)

entry_var = tk.StringVar()
entry = ttk.Entry(appframe, textvariable=entry_var, width=40, exportselection=False)
entry.grid(row=0, column=0, padx=3, pady=3, sticky=tk.EW)

btnframe = ttk.Frame(appframe)
btnframe.grid(row=1, column=0)
invalid_btn = ttk.Button(btnframe, text="Make invalid", command=show_invalid)
valid_btn = ttk.Button(btnframe, text="Make valid", command=show_valid)
invalid_btn.grid(row=0, column=0, padx=3, pady=3)
valid_btn.grid(row=0, column=1, padx=3, pady=3)

root.mainloop()

1 个答案:

答案 0 :(得分:1)

您似乎正在尝试通过更改背景使输入字段显示内容的有效性。但是,您每次都尝试重新配置样式,这不是正确的方法。相反,您应该根据窗口小部件状态配置动态属性。这就是处理禁用/!禁用按下/!按下外观更改的方式。 ttk小部件有一个状态方法,您可以在其中更改许多标志。对于按钮类型的小部件,已禁用是最常见的按下。另一个是活动当小部件在指针悬停在其上时使小部件改变其外观使其看起来“热”时。为了您的目的,定义了一个方便的无效状态。我们只需要将它添加到小部件样式的样式图中。由于我们不想影响所有Entry小部件,我们可以将当前样式复制到新的Custom.Entry样式:

style = ttk.Style()
style.layout("Custom.Entry", style.layout('TEntry'))
style.configure("Custom.Entry", **style.configure('TEntry'))
style.map("Custom.Entry", **style.map('TEntry'))
style.map("Custom.Entry",
    fieldbackground=[(['invalid','!disabled'], '#ff4040'),
                     (['invalid','disabled'], '#ffc0c0')])

entry = ttk.Entry(root, style='Custom.Entry')

在基于Tk的主题上,这将足以使窗口小部件的背景根据窗口小部件无效状态更改颜色。即:entry.state(['invalid'])将使其使用红色背景。在使用原生绘图元素(如Windows和MacOS主题)的主题上,这需要更多的工作。除非本机引擎已经支持无效状态,否则我们无法更改本机主题引擎绘制元素的外观。如果没有,那么我们可以通过从一个基于Tk的主题克隆新的元素来覆盖构成小部件演示的元素。为了说明这一点,请参阅下面的createCustomEntry函数,该函数从'default'主题复制fieldbackground,以便我们可以在Windows上更改外观。

在Linux上,它现在看起来像这样:

enter image description here

在Windows 7上: enter image description here

修改后的代码

try:
    import Tkinter as tk
except:
    import tkinter as tk
try:
    import ttk
except:
    from tkinter import ttk

def createCustomEntry(style):
    if 'Custom.Entry.field' not in style.element_names():
        style.element_create('Custom.Entry.field', 'from', 'default')
    if style.theme_use() in ['alt', 'clam', 'default', 'classic']:
        style.layout('Custom.Entry', style.layout('TEntry'))
    else:
        style.layout("Custom.Entry", [
            ("Custom.Entry.field", {'sticky': 'nswe', 'border': '1', 'children': [
                ("Custom.Entry.padding", {'sticky':'nswe', 'children': [
                    ("Custom.Entry.textarea", {'sticky':'nswe'})
                ]})
            ]})
        ])
    style.configure('Custom.Entry', **style.configure('TEntry'))
    style.map('Custom.Entry', **style.map('TEntry'))
    style.map('Custom.Entry',
        fieldbackground=[(['invalid','!disabled'], '#ff4040'),
                            (['invalid','disabled'], '#ffc0c0')])

def show_invalid():
    [w.state(['invalid']) for w in (entry, entry2)]

def show_valid():
    [w.state(['!invalid']) for w in (entry,entry2)]

root = tk.Tk()

# Simple version:
style = ttk.Style()
style.layout("Custom.Entry", style.layout('TEntry'))
style.configure("Custom.Entry", **style.configure('TEntry'))
style.map("Custom.Entry", **style.map('TEntry'))
style.map("Custom.Entry",
    fieldbackground=[(['invalid','!disabled'], '#ff4040'),
                     (['invalid','disabled'], '#ffc0c0')])

#createCustomEntry(style)

root.title("Testing of Style Customization")

appframe = tk.Frame(root, padx=12, pady=12)
appframe.pack(expand=True, fill=tk.BOTH)

entry_var = tk.StringVar()
entry = ttk.Entry(appframe, textvariable=entry_var, width=40,
                  exportselection=False, style="Custom.Entry")
entry.grid(row=0, column=0, padx=3, pady=3, sticky=tk.EW)

entry2 = ttk.Entry(appframe, textvariable=entry_var, width=40,
                   exportselection=False, style="Custom.Entry")
entry2.grid(row=1, column=0, padx=3, pady=3, sticky=tk.EW)
entry2.state(['disabled'])

btnframe = ttk.Frame(appframe)
btnframe.grid(row=2, column=0)
invalid_btn = ttk.Button(btnframe, text="Make invalid", command=show_invalid)
valid_btn = ttk.Button(btnframe, text="Make valid", command=show_valid)
invalid_btn.grid(row=0, column=0, padx=3, pady=3)
valid_btn.grid(row=0, column=1, padx=3, pady=3)

root.mainloop()