Python 3.x 更改背景颜色时会打开新的Tk窗口

Python 3.x 更改背景颜色时会打开新的Tk窗口,python-3.x,tkinter,Python 3.x,Tkinter,我有一个包含多个框架的GUI,每个框架包含多个标签/输入字段。我正在尝试添加一个“设置”选项,允许用户更改所有框架和标签的背景色。到目前为止,我已经成功地完成了这项任务,但是需要注意的是,在选定的背景下会弹出一个新的Tk窗口,而不是在当前窗口上更新 import tkinter as tk from tkinter import ttk from tkinter import colorchooser bg_hex = '#f0f0f0f0f0f0' #default background

我有一个包含多个框架的GUI,每个框架包含多个标签/输入字段。我正在尝试添加一个“设置”选项,允许用户更改所有框架和标签的背景色。到目前为止,我已经成功地完成了这项任务,但是需要注意的是,在选定的背景下会弹出一个新的Tk窗口,而不是在当前窗口上更新

import tkinter as tk
from tkinter import ttk
from tkinter import colorchooser


bg_hex = '#f0f0f0f0f0f0'  #default background color

def pick_color():
    global bg_hex
    bg_color = colorchooser.askcolor()
    bg_hex = bg_color[1]
    Master().update()
    print(bg_hex)


class Master(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side='top', fill='both', expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)


        self.frames = {}

        for F in (HomePage, PageOne, PageTwo, Settings):        
            frame = F(container, self)
            self.frames[F] = frame
            frame.config(bg = bg_hex)
            frame.grid(row=0, column=0, sticky='nsew')
        self.show_frame(HomePage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()



class HomePage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text='Home Page', font=('Verdana', 12), bg=bg_hex)
        label.pack(pady=5)
        button1 = tk.Button(self, text='Page One', command=lambda: controller.show_frame(PageOne))
        button1.pack(pady=5, ipadx=2)
        button2 = tk.Button(self, text='Page Two', command=lambda: controller.show_frame(PageTwo))
        button2.pack(pady=5)
        button3 = tk.Button(self, text='Settings', command=lambda: controller.show_frame(Settings))
        button3.pack(side='bottom', pady=10)

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Page One', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2, pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Page Two', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2,pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid()

class Settings(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Settings', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2,pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid() 
        button2 = tk.Button(self, text='Choose Background', command= pick_color)
        button2.grid()

Master().mainloop()
在运行代码块时不会发生错误,但当您选择“选择背景”按钮并选择颜色时,将打开一个新的Tk窗口,其中包含选定的背景颜色,而不是更新当前Tk窗口

**更新代码以不反映全局变量,希望它能帮助其他人

我在每个帧类下添加了
self.controller=controller
,将
pick\u color
color\u update
组合成1个函数,并放在Tk类下

def pick_color_bg(self):
    bg_color = colorchooser.askcolor()
    bg_hex = bg_color[1]
    # Loop through pages and contained widgets and set color
    for cls, obj in self.frames.items():
        obj.config(bg=bg_hex)   # Set frame bg color
        for widget in obj.winfo_children():
            if '!label' in str(widget):
                widget.config(bg=bg_hex)    # Set label bg color
最后,将按钮命令更改为
command=self.controller.pick\u color\u bg

通过这些更改,我能够消除对全局变量的需求

在函数
pick_color()
中,创建一个
Tk()
的新实例,该实例将获得新颜色:

Master().update()   # Creates a new root window!
要更改现有根窗口的颜色,必须保存对它的引用。此外,您还必须在类
Master()
中编写一个更新bg颜色的函数。调用
update()
时,颜色不会自动更新,您必须为每个帧配置bg颜色

还有一些

我很难在不重写代码的情况下阅读你的代码。对实例化根窗口的类使用名称
Master
。我将其称为
应用程序
或类似的名称
主机
通常意味着“主机和服务器”中的主机,或者可能是“父级”。您还可以使用名称
frame
,它通常是一个框架的名称,作为不同页面类实例(主页等)的名称。这使得它很难阅读。就像蓝色这个词是用红色字母写的

我会用更具描述性的名字重写它,这样更容易理解。这样问题就更容易发现和纠正

甚至更多

我花了一段时间,但下面是一个示例,说明了它如何处理小的更改:

import tkinter as tk
from tkinter import ttk
from tkinter import colorchooser


bg_hex = '#f0f0f0f0f0f0'  #default background color

def pick_color():
    global bg_hex
    bg_color = colorchooser.askcolor()
    bg_hex = bg_color[1]
    root.update_color()     # Call function for updating color
    print(bg_hex)


class Master(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side='top', fill='both', expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)


        self.frames = {}

        for F in (HomePage, PageOne, PageTwo, Settings):        
            frame = F(container, self)
            self.frames[F] = frame
            frame.config(bg = bg_hex)
            frame.grid(row=0, column=0, sticky='nsew')
        self.show_frame(HomePage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def update_color(self):
        # Loop through pages and contained widgets and set color
        for cls, obj in self.frames.items():
            obj.config(bg=bg_hex)   # Set frame bg color
            for widget in obj.winfo_children():
                if '!label' in str(widget):
                    widget.config(bg=bg_hex)    # Set label bg color



class HomePage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text='Home Page', font=('Verdana', 12), bg=bg_hex)
        label.pack(pady=5)
        button1 = tk.Button(self, text='Page One', command=lambda: controller.show_frame(PageOne))
        button1.pack(pady=5, ipadx=2)
        button2 = tk.Button(self, text='Page Two', command=lambda: controller.show_frame(PageTwo))
        button2.pack(pady=5)
        button3 = tk.Button(self, text='Settings', command=lambda: controller.show_frame(Settings))
        button3.pack(side='bottom', pady=10)

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Page One', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2, pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid()


class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Page Two', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2,pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid()

class Settings(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        tk.Label(self, text='Settings', font='Verdana 14 bold underline', bg=bg_hex).grid(row=0, columnspan=2,pady=5)
        button1 = tk.Button(self, text='Back to Home', command=lambda: controller.show_frame(HomePage))
        button1.grid() 
        button2 = tk.Button(self, text='Choose Background', command= pick_color)
        button2.grid()

root = Master()     # Save a reference to the root window
root.mainloop()

我建议不要在全局范围内通过函数改变颜色。我认为它最好作为
Master()
类的函数。这样您就不必使用全局变量。

谢谢!这对我有用。谈到这个话题,我在网上找不到太多的信息。我使用了您提供的相同模板来扩展功能,允许用户更改标签的字体颜色。我接受了你的建议,重新命名了Master()类,因为我对OOP的了解充其量只是初步的,我还以为它是一个父类。我将不得不暂时在全局范围内使用这两个函数,直到我在
类App()
下构建它为止。再次感谢:)有一个讨论OOP结构的好帖子:再次感谢@figbeam为我指明了正确的方向。我阅读了您链接的线程和其他一些帮助我更新代码以不依赖全局变量的线程。