israel-dryer / ttkbootstrap

A supercharged theme extension for tkinter that enables on-demand modern flat style themes inspired by Bootstrap.
MIT License
1.9k stars 381 forks source link

Checkbutton (Roundtoggle & Squaretoggle) disappearing with custom style #482

Open Fredrik2002 opened 1 year ago

Fredrik2002 commented 1 year ago

Desktop (please complete the following information):

ttkbootstrap Version 1.10.1 OS : Windows 11 Python : 3.10.6

Describe the bug

The roundtoggle and squaretoggle checkbuttons disappear when we give them a custom style.

I tried custom styling with TCheckbutton, Toolbutton and Outline.Toolbutton and it works fine.

To Reproduce

from tkinter import Tk from ttkbootstrap import Checkbutton, Style

window = Tk() window.geometry("400x400")

my_style = Style()

my_style.configure('Roundtoggle.Toolbutton', font=("Helvetica 26"), foreground="#FF0000")

button = Checkbutton(window, text = "Hello", style="Roundtoggle.Toolbutton") button.pack()

window.mainloop()

image

Expected behavior

A bigger checkbutton (or at least bigger font)

Screenshots

No response

Additional context

No response

vinzjaystone commented 1 year ago

Try this if it work on your side

Hello there, I've come across your bug report regarding the checkbutton and have taken the opportunity to evaluate it on my own system. Based on my understanding and the information available in the documentation, it appears that when customizing styles, it's essential to define valid ttk class names. If your goal is to enlarge the text, as it appears in the image you've shared, you might want to consider using the following code snippet.

from tkinter import Tk
from ttkbootstrap import Checkbutton, Style
window = Tk()
window.geometry("400x50")
my_style = Style()

# or success.TRadiobutton , danger.TRadiobutton, etc...
checkbtn_style = "danger.TRadiobutton"

my_style.configure(checkbtn_style, foreground="#FF0000",
                   font=("Helvetica", 26))
button = Checkbutton(window, text="Hello", style=checkbtn_style)
button.pack()
window.mainloop()

ttkbootstrap

Fredrik2002 commented 1 year ago

Thanks for you answer, it works indeed

But what if I want to use a checkbutton (and particularly a Roundtoggle button) instead of a radiobutton ?

from ttkbootstrap import Checkbutton, Style

window = Tk()
window.geometry("400x50")

my_style = Style()

#my_style.configure('danger.Roundtoggle.Toolbutton',font=("Helvetica 26"), foreground="#FF0000")

button = Checkbutton(window, text = "Hello", style="danger.Roundtoggle.Toolbutton")
button.pack()

window.mainloop()

You can see that if I comment out the my_style.configure() line, the checkbutton reappear, with default style

You talk about class names, I tried to follow the class names given in the documentation here : https://ttkbootstrap.readthedocs.io/en/version-0.5/widgets/checkbutton.html

image

vinzjaystone commented 1 year ago

Oh, I understand now. Please correct me if I'm mistaken, what you are looking is to increase the font size of the checkbutton? As far as my knowledge goes, I haven't worked on configuring a custom style for a checkbutton to specifically customize the font sizes. Typically, I've focused on altering the styles related to appearance.

As a potential workaround for achieving the desired larger font size for the checkbutton, I might consider adjusting the Windows scaling to a desired value. For instance, if I set the scaling to 2, it would proportionally scale up the sizes of the widgets, resulting in a larger font size. If further adjustments to the text sizes of labels are needed, I could create another custom style specifically for TLabels.

import ttkbootstrap as tb

class App(tb.Window):
    def __init__(self) -> None:
        super().__init__(self, themename="darkly") #, scaling=2)
        self.title("GB #482")
        self.geometry("300x100")
        self.position_center()

        cframe = tb.Frame(self)
        cframe.pack()
        chk_btn = tb.Checkbutton(cframe, text="", bootstyle="danger-square-toggle")
        chk_btn.pack(side="left")

        chk_lbl_style = tb.Style()
        chk_label_style = "vinz.TLabel"
        chk_lbl_style.configure(chk_label_style, foreground="#FF0000", font=("Consolas", 7, "bold", "italic"))

        chk_label = tb.Label(cframe, text="ADD ME", style=chk_label_style)
        chk_label.pack(side="left")

        lframe = tb.Frame(self)
        lframe.pack()
        chk_btn2 = tb.Checkbutton(lframe, text="", bootstyle="danger-round-toggle")
        chk_btn2.pack(side="left")
        chk_lbl_style2 = tb.Style()
        chk_label_style2 = "vinz2.TLabel"
        chk_lbl_style2.configure(chk_label_style2, foreground="#FF0000", font=("Consolas", 30, "bold", "italic"))
        chk_label2 = tb.Label(lframe, text="ADD ME", style=chk_label_style2)
        chk_label2.pack(side="left")

app = App().mainloop()

DEFAULT Scaling

default

Scaling = 2

scaling-2

Custom Label Font Size

scaling-2-custom-font-size

I hope this helps with your issue.

Fredrik2002 commented 1 year ago

Thanks a lot, that's exactly what I wanted, I had no idea about that scaling argument. I'll use that to solve my problem, but I guess it changes the size of every widget ? So there's no way to only scale up the checkbuttons ?

And I still don't understand why I can't style a checkbutton the same way you style a label. In the code below, the rounded checkbutton disappear once again when I try to give it a custom style

import ttkbootstrap as tb

window = tb.Window(scaling=2)
window.geometry("500x400")

chk_lbl_style = tb.Style()
chk_lbl_style.configure("vinz.TLabel", foreground="#00FF00", font=("Consolas", 15, "bold", "italic"))
chk_label2 = tb.Label(window, text="Normal Label", style="vinz.TLabel")
chk_label2.pack()

chk_lbl_style2 = tb.Style()
chk_lbl_style2.configure("vinz2.danger.Roundtoggle.Toolbutton", foreground="#FF0000", font=("Consolas", 15, "bold", "italic"))
chk_btn2 = tb.Checkbutton(window, text="Roundtoggle.Toolbutton",style="vinz2.danger.Roundtoggle.Toolbutton")
chk_btn2.pack()

chk_lbl_style3 = tb.Style()
chk_lbl_style3.configure("vinz3.success.TCheckbutton", foreground="#00FF00", font=("Consolas", 15, "bold", "italic"))
chk_btn3 = tb.Checkbutton(window, text="TCheckbutton",style="vinz3.success.TCheckbutton")
chk_btn3.pack()

window.mainloop()

image

I don't really care now that I have your solution, but still, it looks weird

ismaelink commented 1 year ago

Hello! Building upon @vinzjaystone response, you could create a class that generates this specific widget using the provided concepts, which would help avoid code duplication. As for the root cause of the problem, it could potentially be a bug. The example below is just a rough outline:

from tkinter import Tk, IntVar
from tkinter.font import Font
from ttkbootstrap import Checkbutton, Style, Frame, Label
from typing import Any

class Roundtoggle(Frame):
    def __init__(self, 
                 parent: Any=None, 
                 font: Font=("Arial", 12),
                 text: str="",
                 onvalue: int=1,
                 offvalue: int=0,
                 foreground: str="#000000", 
                 **kwargs):
        super().__init__(parent, **kwargs)

        self.__style = Style()
        self.__value = IntVar()
        self.__checkbutton = None
        self.__label = None
        self.__font = font
        self.__text = text
        self.__onvalue = onvalue
        self.__offvalue = offvalue
        self.__foreground = foreground

        self.__create_checkbutton()
        self.__create_label()
        self.__set_event()

    def get(self) -> Any:
        return self.__value.get()

    def set(self, new_value: Any) -> None:
        self.__value.set(new_value)

    def __create_checkbutton(self) -> None:
        self.__checkbutton = Checkbutton(self, 
                style="Roundtoggle.Toolbutton",
                onvalue=self.__onvalue,
                offvalue=self.__offvalue,
                variable=self.__value)

        self.__checkbutton.grid(row=0, column=0)

    def __create_label(self) -> None:
        self.__style.configure(style="roundtoggle.TLabel",
                font=self.__font,
                foreground=self.__foreground)

        self.__label = Label(self, 
                text=self.__text,
                style="roundtoggle.TLabel")

        self.__label.grid(row=0, column=1)

    def __click(self, event) -> None:
        value = self.get()

        if value == self.__offvalue:
            self.set(self.__onvalue)
        else:
            self.set(self.__offvalue)

    def __set_event(self) -> None:
        self.bind_all("<1>", self.__click)

window = Tk()
window.geometry("400x400")

c = Roundtoggle(parent=window,
                text="Hello",
                font=("Heveltica", 26),
                onvalue=1,
                offvalue=0,
                foreground="#FF0000",
                cursor="hand2")
c.pack()

window.mainloop()

Result: Captura de tela de 2023-09-09 22-10-55