简体   繁体   中英

Implementing Dark-mode with on/off function in Simple Python Tkinter program?

I followed this tutorial in creating a very simple text-editor app using Python's Tkinter . What I wanted to do was add the option of using a checkbutton , so when checked , the theme of the text-editor would change to dark-mode theme and when unchecked , would return to the default white theme. How can I do this?

I tried binding a function the checkbutton where it would check the state and depending on the state, change the variables of the frames in the window. For example, if it was:

frame = tk.Frame(colour=white)

as the default, in the function I would put:

frame = tk.Frame(colour=white)

Even to me, this didn't look right. (I know the format is incorrect.)

Here the code (without my attempt at doing the dark-mode):

import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename

def open_file():
    """Open a file for editing."""
    filepath = askopenfilename(
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
    )
    if not filepath:
        return
    txt_edit.delete(1.0, tk.END)
    with open(filepath, "r") as input_file:
        text = input_file.read()
        txt_edit.insert(tk.END, text)
    window.title(f"Simple Text Editor - {filepath}")

def save_file():
    """Save the current file as a new file."""
    filepath = asksaveasfilename(
        defaultextension="txt",
        filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
    )
    if not filepath:
        return
    with open(filepath, "w") as output_file:
        text = txt_edit.get(1.0, tk.END)
        output_file.write(text)
    window.title(f"Simple Text Editor - {filepath}")

window = tk.Tk()
window.title("Simple Text Editor")
window.rowconfigure(0, minsize=800, weight=1)
window.columnconfigure(1, minsize=800, weight=1)

txt_edit = tk.Text(window)
fr_buttons = tk.Frame(window, relief=tk.RAISED, bd=2)
btn_open = tk.Button(fr_buttons, text="Open", command=open_file)
btn_save = tk.Button(fr_buttons, text="Save As...", command=save_file)

btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
btn_save.grid(row=1, column=0, sticky="ew", padx=5)

fr_buttons.grid(row=0, column=0, sticky="ns")
txt_edit.grid(row=0, column=1, sticky="nsew")

window.mainloop()

We can create our GUI inside a method of a class. We can then define another method that kills the GUI, changes the variables that set our parameters for the theme and then restarts the GUI.

This might seem like alot of code, but once it is set up, it is very scalable and works not only for colour, but also other attributes. You can just loop through all the appropriate widgets you want.

Check out the following code:

import tkinter as tk


class App():
    # Here, set everything up you want to override later, when restarting the GUI
    def __init__(self):
        self.color_scheme('dark')  # This determines the mode the GUI starts up in.
        self.font = ('Serif', 12)
        # This will be shown on the button (when dark mode is active it needs to say "light theme")
        self.theme_btn_text = 'Light Theme'

        self.mainGUI()  # Call the actual GUI and Inistialize with the standard Settings. This has to be AFTER all the setup

    def mainGUI(self):
        # Set up Root window
        self.root = tk.Tk()
        self.root.title('A title')
        # whenever we style items, we use the attributes as colors. These attributes are defined in the function below.
        self.root.configure(bg=self.bg)

        # Set up buttons (only the unique elements)
        self.btn1 = tk.Button(self.root,
                              text='Random Text')

        self.theme_btn = tk.Button(self.root,
                                   text=self.theme_btn_text,
                                   command=lambda: self.theme_switch())

        # Set up the attributes that all buttons have in common and pack them (or better use grid)
        # Style them however you want
        for btn in [self.btn1, self.theme_btn]:
            btn.configure(bg=self.bg,
                          fg=self.fg,
                          activebackground=self.bg,
                          borderwidth=0)
            btn.pack(padx=20, pady=20)
        # Don't forget that everything needs to be between the TK() and the mainloop()
        self.root.mainloop()

    # Create the method that sets the colours of the class, depending on the active theme.
    # This is used in the __init__ method to create the first theme, but also by the theme_switch method below.
    def color_scheme(self, mode):
        if mode == 'dark':
            self.bg, self.fg, self.ac1, self.ac2 = ('#282828', 'white', '#404040y', '#B3B3B3')
        if mode == 'light':
            self.bg, self.fg, self.ac1, self.ac2 = ('#FBF8F1', 'black', '#F7ECDE', '#E9DAC1')

    # This could of course also be placed inside the theme_switch method directly. But I like to use it for other things too.
    def restart_GUI(self):
        self.root.destroy()
        self.mainGUI()

    # Here we decide on whether we need to switch from light to dark or the other way around by looking at the color used at the moment the method is called via button press
    def theme_switch(self):
        if self.bg == '#282828':
            self.color_scheme('light')
            # We change the text to match the opposite theme
            self.theme_btn_text = 'Dark Theme'
        elif self.bg == '#FBF8F1':
            self.color_scheme('dark')
            self.theme_btn_text = 'Light Theme'
        self.restart_GUI()


# Finally, we initiate an object of our class to start the GUI
x = App()

Hope this helps someone, Of course the two approaches can easily be combined. extended for other attributes and other widgets etc.

All the best!

You Can Do By Installing The Module ttkthemes

pip install ttkthemes

import tkinter as tk
import tkinter.ttk as ttk 
from ttkthemes import ThemedStyle

app = tk.Tk()
app.geometry("200x400")
app.title("Changing Themes")
# Setting Theme
style = ThemedStyle(app)
style.set_theme("scidgrey")

# Button Widgets
Def_Btn = tk.Button(app,text='Default Button')
Def_Btn.pack()
Themed_Btn = ttk.Button(app,text='Themed button')
Themed_Btn.pack()

# Scrollbar Widgets
Def_Scrollbar = tk.Scrollbar(app)
Def_Scrollbar.pack(side='right',fill='y')
Themed_Scrollbar = ttk.Scrollbar(app,orient='horizontal')
Themed_Scrollbar.pack(side='top',fill='x')

# Entry Widgets
Def_Entry = tk.Entry(app)
Def_Entry.pack()
Themed_Entry = ttk.Entry(app)
Themed_Entry.pack()

app.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM