import tkinter as tk from tkinter import ttk from tkinter import font from tkinter.ttk import Button, Checkbutton, Combobox from tkinter.ttk import Entry, Frame, Label from tkinter.ttk import Labelframe, LabelFrame, Menubutton from tkinter.ttk import Notebook, OptionMenu, PanedWindow from tkinter.ttk import Panedwindow, Progressbar, Radiobutton from tkinter.ttk import Scale, Scrollbar, Separator from tkinter.ttk import Sizegrip, Spinbox, Treeview from ttkbootstrap.constants import * # date entry imports from ttkbootstrap.dialogs import Querybox from datetime import datetime # floodgauge imports import math # meter imports from PIL import Image, ImageTk, ImageDraw from ttkbootstrap.style import Colors from ttkbootstrap import utility from ttkbootstrap.style import Bootstyle M = 3 # meter image scale, higher number increases resolution TTK_WIDGETS = ( ttk.Button, ttk.Checkbutton, ttk.Combobox, ttk.Entry, ttk.Frame, ttk.Labelframe, ttk.Label, ttk.Menubutton, ttk.Notebook, ttk.Panedwindow, ttk.Progressbar, ttk.Radiobutton, ttk.Scale, ttk.Scrollbar, ttk.Separator, ttk.Sizegrip, ttk.Spinbox, ttk.Treeview, ttk.OptionMenu, ) TK_WIDGETS = ( tk.Tk, tk.Toplevel, tk.Button, tk.Label, tk.Text, tk.Frame, tk.Checkbutton, tk.Radiobutton, tk.Entry, tk.Scale, tk.Listbox, tk.Menu, tk.Menubutton, tk.LabelFrame, tk.Canvas, tk.OptionMenu, tk.Spinbox, ) class DateEntry(ttk.Frame): """A date entry widget combines the `Combobox` and a `Button` with a callback attached to the `get_date` function. When pressed, a date chooser popup is displayed. The returned value is inserted into the combobox. The date chooser popup will use the date in the combobox as the date of focus if it is in the format specified by the `dateformat` parameter. By default, this format is "%Y-%m-%d". The bootstyle api may be used to change the style of the widget. The available colors include -> primary, secondary, success, info, warning, danger, light, dark. The starting weekday on the date chooser popup can be changed with the `firstweekday` parameter. By default this value is `6`, which represents "Sunday". The `Entry` and `Button` widgets are accessible from the `DateEntry.Entry` and `DateEntry.Button` properties. ![](../../assets/widgets/date-entry.png) """ def __init__( self, master=None, dateformat=r"%x", firstweekday=6, startdate=None, bootstyle="", **kwargs, ): """ Parameters: master (Widget, optional): The parent widget. dateformat (str, optional): The format string used to render the text in the entry widget. For more information on acceptable formats, see https://strftime.org/ firstweekday (int, optional): Specifies the first day of the week. 0=Monday, 1=Tuesday, etc... startdate (datetime, optional): The date that is in focus when the widget is displayed. Default is current date. bootstyle (str, optional): A style keyword used to set the focus color of the entry and the background color of the date button. Available options include -> primary, secondary, success, info, warning, danger, dark, light. **kwargs (Dict[str, Any], optional): Other keyword arguments passed to the frame containing the entry and date button. """ self._dateformat = dateformat self._firstweekday = firstweekday self._startdate = startdate or datetime.today() self._bootstyle = bootstyle super().__init__(master, **kwargs) # add visual components entry_kwargs = {"bootstyle": self._bootstyle} if "width" in kwargs: entry_kwargs["width"] = kwargs.pop("width") self.entry = ttk.Entry(self, **entry_kwargs) self.entry.pack(side=tk.LEFT, fill=tk.X, expand=tk.YES) self.button = ttk.Button( master=self, command=self._on_date_ask, bootstyle=f"{self._bootstyle}-date", ) self.button.pack(side=tk.LEFT) # starting value self.entry.insert(tk.END, self._startdate.strftime(self._dateformat)) def __getitem__(self, key: str): return self.configure(cnf=key) def __setitem__(self, key: str, value): self.configure(cnf=None, **{key: value}) def _configure_set(self, **kwargs): """Override configure method to allow for setting custom DateEntry parameters""" if "state" in kwargs: state = kwargs.pop("state") if state in ["readonly", "invalid"]: self.entry.configure(state=state) elif state in ("disabled", "normal"): self.entry.configure(state=state) self.button.configure(state=state) else: kwargs[state] = state if "dateformat" in kwargs: self._dateformat = kwargs.pop("dateformat") if "firstweekday" in kwargs: self._firstweekday = kwargs.pop("firstweekday") if "startdate" in kwargs: self._startdate = kwargs.pop("startdate") if "bootstyle" in kwargs: self._bootstyle = kwargs.pop("bootstyle") self.entry.configure(bootstyle=self._bootstyle) self.button.configure(bootstyle=[self._bootstyle, "date"]) if "width" in kwargs: width = kwargs.pop("width") self.entry.configure(width=width) super(ttk.Frame, self).configure(**kwargs) def _configure_get(self, cnf): """Override the configure get method""" if cnf == "state": entrystate = self.entry.cget("state") buttonstate = self.button.cget("state") return {"Entry": entrystate, "Button": buttonstate} if cnf == "dateformat": return self._dateformat if cnf == "firstweekday": return self._firstweekday if cnf == "startdate": return self._startdate if cnf == "bootstyle": return self._bootstyle else: return super(ttk.Frame, self).configure(cnf=cnf) def configure(self, cnf=None, **kwargs): """Configure the options for this widget. Parameters: cnf (Dict[str, Any], optional): A dictionary of configuration options. **kwargs: Optional keyword arguments. """ if cnf is not None: return self._configure_get(cnf) else: return self._configure_set(**kwargs) def _on_date_ask(self): """Callback for pushing the date button""" _val = self.entry.get() or datetime.today().strftime(self._dateformat) try: self._startdate = datetime.strptime(_val, self._dateformat) except Exception as e: print("Date entry text does not match", self._dateformat) self._startdate = datetime.today() self.entry.delete(first=0, last=tk.END) self.entry.insert( tk.END, self._startdate.strftime(self._dateformat) ) old_date = datetime.strptime(_val, self._dateformat) # get the new date and insert into the entry new_date = Querybox.get_date( parent=self.entry, startdate=old_date, firstweekday=self._firstweekday, bootstyle=self._bootstyle, ) self.entry.delete(first=0, last=tk.END) self.entry.insert(tk.END, new_date.strftime(self._dateformat)) self.entry.focus_force() class Floodgauge(Progressbar): """A widget that shows the status of a long-running operation with an optional text indicator. Similar to the `ttk.Progressbar`, this widget can operate in two modes. *determinate* mode shows the amount completed relative to the total amount of work to be done, and *indeterminate* mode provides an animated display to let the user know that something is happening. Variable are generated automatically for this widget and can be linked to other widgets by referencing them via the `textvariable` and `variable` attributes. ![](../../assets/widgets/floodgauge.gif) Examples: ```python import ttkbootstrap as ttk from ttkbootstrap.constants import * app = ttk.Window(size=(500, 500)) gauge = ttk.Floodgauge( bootstyle=INFO, font=(None, 24, 'bold'), mask='Memory Used {}%', ) gauge.pack(fill=BOTH, expand=YES, padx=10, pady=10) # autoincrement the gauge gauge.start() # stop the autoincrement gauge.stop() # manually update the gauge value gauge.configure(value=25) # increment the value by 10 steps gauge.step(10) app.mainloop() ``` """ def __init__( self, master=None, cursor=None, font=None, length=None, maximum=100, mode=DETERMINATE, orient=HORIZONTAL, bootstyle=PRIMARY, takefocus=False, text=None, value=0, mask=None, **kwargs, ): """ Parameters: master (Widget, optional): Parent widget. Defaults to None. cursor (str, optional): The cursor that will appear when the mouse is over the progress bar. Defaults to None. font (Union[Font, str], optional): The font to use for the progress bar label. length (int, optional): Specifies the length of the long axis of the progress bar (width if orient = horizontal, height if if vertical); maximum (float, optional): A floating point number specifying the maximum `value`. Defaults to 100. mode ('determinate', 'indeterminate'): Use `indeterminate` if you cannot accurately measure the relative progress of the underlying process. In this mode, a rectangle bounces back and forth between the ends of the widget once you use the `Floodgauge.start()` method. Otherwise, use `determinate` if the relative progress can be calculated in advance. orient ('horizontal', 'vertical'): Specifies the orientation of the widget. bootstyle (str, optional): The style used to render the widget. Options include primary, secondary, success, info, warning, danger, light, dark. takefocus (bool, optional): This widget is not included in focus traversal by default. To add the widget to focus traversal, use `takefocus=True`. text (str, optional): A string of text to be displayed in the Floodgauge label. This is assigned to the attribute `Floodgauge.textvariable` value (float, optional): The current value of the progressbar. In `determinate` mode, this represents the amount of work completed. In `indeterminate` mode, it is interpreted modulo `maximum`; that is, the progress bar completes one "cycle" when the `value` increases by `maximum`. mask (str, optional): A string format that can be used to update the Floodgauge label every time the value is updated. For example, the string "{}% Storage Used" with a widget value of 45 would show "45% Storage Used" on the Floodgauge label. If a mask is set, then the `text` option is ignored. **kwargs: Other configuration options from the option database. """ # progress bar value variables if 'variable' in kwargs: self._variable = kwargs.pop('variable') else: self._variable = tk.IntVar(value=value) if 'textvariable' in kwargs: self._textvariable = kwargs.pop('textvariable') else: self._textvariable = tk.StringVar(value=text) self._bootstyle = bootstyle self._font = font or "helvetica 10" self._mask = mask self._traceid = None super().__init__( master=master, class_="Floodgauge", cursor=cursor, length=length, maximum=maximum, mode=mode, orient=orient, bootstyle=bootstyle, takefocus=takefocus, variable=self._variable, **kwargs, ) self._set_widget_text(self._textvariable.get()) self.bind("<>", self._on_theme_change) self.bind("<>", self._on_theme_change) if self._mask is not None: self._set_mask() def _set_widget_text(self, *_): ttkstyle = self.cget("style") if self._mask is None: text = self._textvariable.get() else: value = self._variable.get() text = self._mask.format(value) self.tk.call("ttk::style", "configure", ttkstyle, "-text", text) self.tk.call("ttk::style", "configure", ttkstyle, "-font", self._font) def _set_mask(self): if self._traceid is None: self._traceid = self._variable.trace_add( "write", self._set_widget_text ) def _unset_mask(self): if self._traceid is not None: self._variable.trace_remove("write", self._traceid) self._traceid = None def _on_theme_change(self, *_): text = self._textvariable.get() self._set_widget_text(text) def _configure_get(self, cnf): if cnf == "value": return self._variable.get() if cnf == "text": return self._textvariable.get() if cnf == "bootstyle": return self._bootstyle if cnf == "mask": return self._mask if cnf == "font": return self._font else: return super(Progressbar, self).configure(cnf=cnf) def _configure_set(self, **kwargs): if "value" in kwargs: self._variable.set(kwargs.pop("value")) if "text" in kwargs: self._textvariable.set(kwargs.pop("text")) if "bootstyle" in kwargs: self._bootstyle = kwargs.get("bootstyle") if "mask" in kwargs: self._mask = kwargs.pop("mask") if "font" in kwargs: self._font = kwargs.pop("font") if "variable" in kwargs: self._variable = kwargs.get("variable") Progressbar.configure(self, cnf=None, **kwargs) if "textvariable" in kwargs: self.textvariable = kwargs.pop("textvariable") else: Progressbar.configure(self, cnf=None, **kwargs) def __getitem__(self, key: str): return self._configure_get(cnf=key) def __setitem__(self, key: str, value): self._configure_set(**{key: value}) def configure(self, cnf=None, **kwargs): """Configure the options for this widget. Parameters: cnf (Dict[str, Any], optional): A dictionary of configuration options. **kwargs: Optional keyword arguments. """ if cnf is not None: return self._configure_get(cnf) else: self._configure_set(**kwargs) @property def textvariable(self): """Returns the textvariable object""" return self._textvariable @textvariable.setter def textvariable(self, value): """Set the new textvariable property""" self._textvariable = value self._set_widget_text(self._textvariable.get()) @property def variable(self): """Returns the variable object""" return self._variable @variable.setter def variable(self, value): """Set the new variable object""" self._variable = value if self.cget('variable') != value: self.configure(variable=self._variable) class Meter(ttk.Frame): """A radial meter that can be used to show progress of long running operations or the amount of work completed; can also be used as a dial when set to `interactive=True`. This widget is very flexible. There are two primary meter types which can be set with the `metertype` parameter: 'full' and 'semi', which shows the arc of the meter in a full or semi-circle. You can also customize the arc of the circle with the `arcrange` and `arcoffset` parameters. The meter indicator can be displayed as a solid color or with stripes using the `stripethickness` parameter. By default, the `stripethickness` is 0, which results in a solid meter indicator. A higher `stripethickness` results in larger wedges around the arc of the meter. Various text and label options exist. The center text and meter indicator is formatted with the `meterstyle` parameter. You can set text on the left and right of this center label using the `textleft` and `textright` parameters. This is most commonly used for '$', '%', or other such symbols. If you need access to the variables that update the meter, you you can access these via the `amountusedvar`, `amounttotalvar`, and the `labelvar`. The value of these properties can also be retrieved via the `configure` method. ![](../../assets/widgets/meter.gif) Examples: ```python import ttkbootstrap as ttk from ttkbootstrap.constants import * app = ttk.Window() meter = ttk.Meter( metersize=180, padding=5, amountused=25, metertype="semi", subtext="miles per hour", interactive=True, ) meter.pack() # update the amount used directly meter.configure(amountused = 50) # update the amount used with another widget entry = ttk.Entry(textvariable=meter.amountusedvar) entry.pack(fill=X) # increment the amount by 10 steps meter.step(10) # decrement the amount by 15 steps meter.step(-15) # update the subtext meter.configure(subtext="loading...") app.mainloop() ``` """ def __init__( self, master=None, bootstyle=DEFAULT, arcrange=None, arcoffset=None, amounttotal=100, amountused=0, wedgesize=0, metersize=200, metertype=FULL, meterthickness=10, showtext=True, interactive=False, stripethickness=0, textleft=None, textright=None, textfont="-size 20 -weight bold", subtext=None, subtextstyle=DEFAULT, subtextfont="-size 10", stepsize=1, **kwargs, ): """ Parameters: master (Widget): The parent widget. arcrange (int): The range of the arc if degrees from start to end. arcoffset (int): The amount to offset the arc's starting position in degrees. 0 is at 3 o'clock. amounttotal (int): The maximum value of the meter. amountused (int): The current value of the meter; displayed in a center label if the `showtext` property is set to True. wedgesize (int): Sets the length of the indicator wedge around the arc. If greater than 0, this wedge is set as an indicator centered on the current meter value. metersize (int): The meter is square. This represents the size of one side if the square as measured in screen units. bootstyle (str): Sets the indicator and center text color. One of primary, secondary, success, info, warning, danger, light, dark. metertype ('full', 'semi'): Displays the meter as a full circle or semi-circle. meterthickness (int): The thickness of the indicator. showtext (bool): Indicates whether to show the left, center, and right text labels on the meter. interactive (bool): Indicates that the user may adjust the meter value with mouse interaction. stripethickness (int): The indicator can be displayed as a solid band or as striped wedges around the arc. If the value is greater than 0, the indicator changes from a solid to striped, where the value is the thickness of the stripes (or wedges). textleft (str): A short string inserted to the left of the center text. textright (str): A short string inserted to the right of the center text. textfont (Union[str, Font]): The font used to render the center text. subtext (str): Supplemental text that appears below the center text. subtextstyle (str): The bootstyle color of the subtext. One of primary, secondary, success, info, warning, danger, light, dark. The default color is Theme specific and is a lighter shade based on whether it is a 'light' or 'dark' theme. subtextfont (Union[str, Font]): The font used to render the subtext. stepsize (int): Sets the amount by which to change the meter indicator when incremented by mouse interaction. **kwargs: Other keyword arguments that are passed directly to the `Frame` widget that contains the meter components. """ super().__init__(master=master, **kwargs) # widget variables self.amountusedvar = tk.IntVar(value=amountused) self.amountusedvar.trace_add("write", self._draw_meter) self.amounttotalvar = tk.IntVar(value=amounttotal) self.labelvar = tk.StringVar(value=subtext) # misc settings self._set_arc_offset_range(metertype, arcoffset, arcrange) self._towardsmaximum = True self._metersize = utility.scale_size(self, metersize) self._meterthickness = utility.scale_size(self, meterthickness) self._stripethickness = stripethickness self._showtext = showtext self._wedgesize = wedgesize self._stepsize = stepsize self._textleft = textleft self._textright = textright self._textfont = textfont self._subtext = subtext self._subtextfont = subtextfont self._subtextstyle = subtextstyle self._bootstyle = bootstyle self._interactive = interactive self._bindids = {} self._setup_widget() def _setup_widget(self): self.meterframe = ttk.Frame( master=self, width=self._metersize, height=self._metersize ) self.indicator = ttk.Label(self.meterframe) self.textframe = ttk.Frame(self.meterframe) self.textleft = ttk.Label( master=self.textframe, text=self._textleft, font=self._subtextfont, bootstyle=(self._subtextstyle, "metersubtxt"), anchor=tk.S, padding=(0, 5), ) self.textcenter = ttk.Label( master=self.textframe, textvariable=self.amountusedvar, bootstyle=(self._bootstyle, "meter"), font=self._textfont, ) self.textright = ttk.Label( master=self.textframe, text=self._textright, font=self._subtextfont, bootstyle=(self._subtextstyle, "metersubtxt"), anchor=tk.S, padding=(0, 5), ) self.subtext = ttk.Label( master=self.meterframe, text=self._subtext, bootstyle=(self._subtextstyle, "metersubtxt"), font=self._subtextfont, ) self.bind("<>", self._on_theme_change) self.bind("<>", self._on_theme_change) self._set_interactive_bind() self._draw_base_image() self._draw_meter() # set widget geometery self.indicator.place(x=0, y=0) self.meterframe.pack() self._set_show_text() def _set_widget_colors(self): bootstyle = (self._bootstyle, "meter", "label") ttkstyle = Bootstyle.ttkstyle_name(string="-".join(bootstyle)) textcolor = self._lookup_style_option(ttkstyle, "foreground") background = self._lookup_style_option(ttkstyle, "background") troughcolor = self._lookup_style_option(ttkstyle, "space") self._meterforeground = textcolor self._meterbackground = Colors.update_hsv(background, vd=-0.1) self._metertrough = troughcolor def _set_meter_text(self): """Setup and pack the widget labels in the appropriate order""" self._set_show_text() self._set_subtext() def _set_subtext(self): if self._subtextfont: if self._showtext: self.subtext.place(relx=0.5, rely=0.6, anchor=tk.CENTER) else: self.subtext.place(relx=0.5, rely=0.5, anchor=tk.CENTER) def _set_show_text(self): self.textframe.pack_forget() self.textcenter.pack_forget() self.textleft.pack_forget() self.textright.pack_forget() self.subtext.pack_forget() if self._showtext: if self._subtext: self.textframe.place(relx=0.5, rely=0.45, anchor=tk.CENTER) else: self.textframe.place(relx=0.5, rely=0.5, anchor=tk.CENTER) self._set_text_left() self._set_text_center() self._set_text_right() self._set_subtext() def _set_text_left(self): if self._showtext and self._textleft: self.textleft.pack(side=tk.LEFT, fill=tk.Y) def _set_text_center(self): if self._showtext: self.textcenter.pack(side=tk.LEFT, fill=tk.Y) def _set_text_right(self): self.textright.configure(text=self._textright) if self._showtext and self._textright: self.textright.pack(side=tk.RIGHT, fill=tk.Y) def _set_interactive_bind(self): seq1 = "" seq2 = "" if self._interactive: self._bindids[seq1] = self.indicator.bind( seq1, self._on_dial_interact ) self._bindids[seq2] = self.indicator.bind( seq2, self._on_dial_interact ) return if seq1 in self._bindids: self.indicator.unbind(seq1, self._bindids.get(seq1)) self.indicator.unbind(seq2, self._bindids.get(seq2)) self._bindids.clear() def _set_arc_offset_range(self, metertype, arcoffset, arcrange): if metertype == SEMI: self._arcoffset = 135 if arcoffset is None else arcoffset self._arcrange = 270 if arcrange is None else arcrange else: self._arcoffset = -90 if arcoffset is None else arcoffset self._arcrange = 360 if arcrange is None else arcrange self._metertype = metertype def _draw_meter(self, *_): """Draw a meter""" img = self._base_image.copy() draw = ImageDraw.Draw(img) if self._stripethickness > 0: self._draw_striped_meter(draw) else: self._draw_solid_meter(draw) self._meterimage = ImageTk.PhotoImage( img.resize((self._metersize, self._metersize), Image.CUBIC) ) self.indicator.configure(image=self._meterimage) def _draw_base_image(self): """Draw base image to be used for subsequent updates""" self._set_widget_colors() self._base_image = Image.new( mode="RGBA", size=(self._metersize * M, self._metersize * M) ) draw = ImageDraw.Draw(self._base_image) x1 = y1 = self._metersize * M - 20 width = self._meterthickness * M # striped meter if self._stripethickness > 0: _from = self._arcoffset _to = self._arcrange + self._arcoffset _step = 2 if self._stripethickness == 1 else self._stripethickness for x in range(_from, _to, _step): draw.arc( xy=(0, 0, x1, y1), start=x, end=x + self._stripethickness - 1, fill=self._metertrough, width=width, ) # solid meter else: draw.arc( xy=(0, 0, x1, y1), start=self._arcoffset, end=self._arcrange + self._arcoffset, fill=self._metertrough, width=width, ) def _draw_solid_meter(self, draw: ImageDraw.Draw): """Draw a solid meter""" x1 = y1 = self._metersize * M - 20 width = self._meterthickness * M if self._wedgesize > 0: meter_value = self._meter_value() draw.arc( xy=(0, 0, x1, y1), start=meter_value - self._wedgesize, end=meter_value + self._wedgesize, fill=self._meterforeground, width=width, ) else: draw.arc( xy=(0, 0, x1, y1), start=self._arcoffset, end=self._meter_value(), fill=self._meterforeground, width=width, ) def _draw_striped_meter(self, draw: ImageDraw.Draw): """Draw a striped meter""" meter_value = self._meter_value() x1 = y1 = self._metersize * M - 20 width = self._meterthickness * M if self._wedgesize > 0: draw.arc( xy=(0, 0, x1, y1), start=meter_value - self._wedgesize, end=meter_value + self._wedgesize, fill=self._meterforeground, width=width, ) else: _from = self._arcoffset _to = meter_value - 1 _step = self._stripethickness for x in range(_from, _to, _step): draw.arc( xy=(0, 0, x1, y1), start=x, end=x + self._stripethickness - 1, fill=self._meterforeground, width=width, ) def _meter_value(self) -> int: """Calculate the value to be used to draw the arc length of the progress meter.""" value = int( (self["amountused"] / self["amounttotal"]) * self._arcrange + self._arcoffset ) return value def _on_theme_change(self, *_): self._draw_base_image() self._draw_meter() def _on_dial_interact(self, e: tk.Event): """Callback for mouse drag motion on meter indicator""" dx = e.x - self._metersize // 2 dy = e.y - self._metersize // 2 rads = math.atan2(dy, dx) degs = math.degrees(rads) if degs > self._arcoffset: factor = degs - self._arcoffset else: factor = 360 + degs - self._arcoffset # clamp the value between 0 and `amounttotal` amounttotal = self.amounttotalvar.get() lastused = self.amountusedvar.get() amountused = (amounttotal / self._arcrange * factor) # calculate amount used given stepsize if amountused > self._stepsize//2: amountused = amountused // self._stepsize * self._stepsize + self._stepsize else: amountused = 0 # if the number is the name, then do not redraw if lastused == amountused: return # set the amount used variable if amountused < 0: self.amountusedvar.set(0) elif amountused > amounttotal: self.amountusedvar.set(amounttotal) else: self.amountusedvar.set(amountused) def _lookup_style_option(self, style: str, option: str): """Wrapper around the tcl style lookup command""" value = self.tk.call( "ttk::style", "lookup", style, "-%s" % option, None, None ) return value def _configure_get(self, cnf): """Override the configuration get method""" if cnf == "arcrange": return self._arcrange elif cnf == "arcoffset": return self._arcoffset elif cnf == "amounttotal": return self.amounttotalvar.get() elif cnf == "amountused": return self.amountusedvar.get() elif cnf == "interactive": return self._interactive elif cnf == "subtextfont": return self._subtextfont elif cnf == "subtextstyle": return self._subtextstyle elif cnf == "subtext": return self._subtext elif cnf == "metersize": return self._metersize elif cnf == "bootstyle": return self._bootstyle elif cnf == "metertype": return self._metertype elif cnf == "meterthickness": return self._meterthickness elif cnf == "showtext": return self._showtext elif cnf == "stripethickness": return self._stripethickness elif cnf == "textleft": return self._textleft elif cnf == "textright": return self._textright elif cnf == "textfont": return self._textfont elif cnf == "wedgesize": return self._wedgesize elif cnf == "stepsize": return self._stepsize else: return super(ttk.Frame, self).configure(cnf) def _configure_set(self, **kwargs): """Override the configuration set method""" meter_text_changed = False if "arcrange" in kwargs: self._arcrange = kwargs.pop("arcrange") if "arcoffset" in kwargs: self._arcoffset = kwargs.pop("arcoffset") if "amounttotal" in kwargs: amounttotal = kwargs.pop("amounttotal") self.amounttotalvar.set(amounttotal) if "amountused" in kwargs: amountused = kwargs.pop("amountused") self.amountusedvar.set(amountused) if "interactive" in kwargs: self._interactive = kwargs.pop("interactive") self._set_interactive_bind() if "subtextfont" in kwargs: self._subtextfont = kwargs.pop("subtextfont") self.subtext.configure(font=self._subtextfont) self.textleft.configure(font=self._subtextfont) self.textright.configure(font=self._subtextfont) if "subtextstyle" in kwargs: self._subtextstyle = kwargs.pop("subtextstyle") self.subtext.configure(bootstyle=[self._subtextstyle, "meter"]) if "metersize" in kwargs: self._metersize = utility.scale_size(kwargs.pop("metersize")) self.meterframe.configure( height=self._metersize, width=self._metersize ) if "bootstyle" in kwargs: self._bootstyle = kwargs.pop("bootstyle") self.textcenter.configure(bootstyle=[self._bootstyle, "meter"]) if "metertype" in kwargs: self._metertype = kwargs.pop("metertype") if "meterthickness" in kwargs: self._meterthickness = self.scale_size( kwargs.pop("meterthickness") ) if "stripethickness" in kwargs: self._stripethickness = kwargs.pop("stripethickness") if "subtext" in kwargs: self._subtext = kwargs.pop("subtext") self.subtext.configure(text=self._subtext) meter_text_changed = True if "textleft" in kwargs: self._textleft = kwargs.pop("textleft") self.textleft.configure(text=self._textleft) meter_text_changed = True if "textright" in kwargs: self._textright = kwargs.pop("textright") meter_text_changed = True if "showtext" in kwargs: self._showtext = kwargs.pop("showtext") meter_text_changed = True if "textfont" in kwargs: self._textfont = kwargs.pop("textfont") self.textcenter.configure(font=self._textfont) if "wedgesize" in kwargs: self._wedgesize = kwargs.pop("wedgesize") if "stepsize" in kwargs: self._stepsize = kwargs.pop("stepsize") if meter_text_changed: self._set_meter_text() try: if self._metertype: self._set_arc_offset_range( metertype=self._metertype, arcoffset=self._arcoffset, arcrange=self._arcrange, ) except AttributeError: return self._draw_base_image() self._draw_meter() # pass remaining configurations to `ttk.Frame.configure` super(ttk.Frame, self).configure(**kwargs) def __getitem__(self, key: str): return self._configure_get(key) def __setitem__(self, key: str, value) -> None: self._configure_set(**{key: value}) def configure(self, cnf=None, **kwargs): """Configure the options for this widget. Parameters: cnf (Dict[str, Any], optional): A dictionary of configuration options. **kwargs: Optional keyword arguments. """ if cnf is not None: return self._configure_get(cnf) else: self._configure_set(**kwargs) def step(self, delta=1): """Increase the indicator value by `delta` The indicator will reverse direction and count down once it reaches the maximum value. Parameters: delta (int): The amount to change the indicator. """ amountused = self.amountusedvar.get() amounttotal = self.amounttotalvar.get() if amountused >= amounttotal: self._towardsmaximum = True self.amountusedvar.set(amountused - delta) elif amountused <= 0: self._towardsmaximum = False self.amountusedvar.set(amountused + delta) elif self._towardsmaximum: self.amountusedvar.set(amountused - delta) else: self.amountusedvar.set(amountused + delta)