-1

It all started from the fact that a normal tk.Scale was not feared correctly (I'm using a custom theme).
I then switched to ttk.Scale but it was not showing the value above the slider when I changed it.
I then discovered ttkwidget, which seems to work but with graphical glitches. Anyone have any ideas to fix?
Code: https://pastebin.com/bxgvsjSF
Screenshot: https://ibb.co/tLdjLrx
P.S. Furthermore, the the widgets that have problems are very slow to load widgets

EDIT: solution is in the comment of the best answer

Gino_P
  • 74
  • 7
  • Are we supposed to assume what `ttkwidgets` is? Is it from the `tkinter` library? – Delrius Euphoria Feb 18 '21 at 08:52
  • sorry for the misunderstanding. [This is `ttkwidgets`](https://github.com/TkinterEP/ttkwidgets) – Gino_P Feb 18 '21 at 09:52
  • The problem was caused due to widget from `ttkwidgets` right? – Delrius Euphoria Feb 18 '21 at 10:08
  • 1
    This question is actually less effective here as the error was caused not due to standard `tkinter` widget. You should probably ask the same question over at [issues](https://github.com/TkinterEP/ttkwidgets/issues) so the developers cant directly get in touch with you. – Delrius Euphoria Feb 18 '21 at 10:13
  • You can try ttk.LabeledScale (it has a small bug fixed in the last python) – hussic Feb 18 '21 at 14:50
  • 1
    Please don't link to code on other sites. Take the time to embed a [mcve] directly in your question. – Bryan Oakley Feb 18 '21 at 15:43
  • @hussic Both `ttk.LabeledScale` and `ttkthemes.TickScale` have graphical glitches, though the ones of `TickScale` are slighlty worse (I am using python 3.9.1). – j_4321 Feb 18 '21 at 19:45
  • I guess that the glitches with `TickScale` and `LabeledScale` are performances issues when moving the `Label`. If you don't want glitches, the easiest would be to use a static label next to the slider. – j_4321 Feb 18 '21 at 19:55
  • Here you can see the very small ufficial fix: https://github.com/python/cpython/commit/6ad5fd14825fc6039a9684dfdc14f5d12b86e25f – hussic Feb 18 '21 at 20:33

1 Answers1

0

I have found a way to get rid of the glitches in ttk.LabeledScale. I think the issue is the redrawing of the whole Label widget so I used a Canvas instead. With a Canvas, when the text is moved, there is no need to redraw the background so the animation is smoother.

The code below is based on the source code of the ttk.LabeledScale (from Python 3.9.1, you can find it in tkinter/ttk.py). But the widget is now based on a Canvas in which the scale and text are added with create_window() and ćreate_text(). I modified the __init__() and _adjust() methods and I added a _on_theme_change() method which is called when the theme changes to update the styling of the canvas and adjust the positions of the elements.

import tkinter as tk
from tkinter import ttk


class LabeledScale(tk.Canvas):

    def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
        self._label_top = kw.pop('compound', 'top') == 'top'

        tk.Canvas.__init__(self, master, **kw)
        self._variable = variable or tk.IntVar(master)
        self._variable.set(from_)
        self._last_valid = from_

        # use style to set the Canvas background color
        self._style = ttk.Style(self)
        self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
        # create the scale
        self.scale = ttk.Scale(self, variable=self._variable, from_=from_, to=to)
        self.scale.bind('<<RangeChanged>>', self._adjust)

        # put scale in canvas
        self._scale = self.create_window(0, 0, window=self.scale, anchor='nw')
        # put label in canvas (the position will be updated later)
        self._label = self.create_text(0, 0, text=self._variable.get(),
                                       fill=self._style.lookup('TLabel', 'foreground'),
                                       anchor='s' if self._label_top else 'n')
        # adjust canvas height to fit the whole content
        bbox = self.bbox(self._label)
        self.configure(width=self.scale.winfo_reqwidth(),
                       height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])
        # bindings and trace to update the label
        self.__tracecb = self._variable.trace_variable('w', self._adjust)
        self.bind('<Configure>', self._adjust)
        self.bind('<Map>', self._adjust)
        # update sizes, positions and appearances on theme change
        self.bind('<<ThemeChanged>>', self._on_theme_change)

    def destroy(self):
        """Destroy this widget and possibly its associated variable."""
        try:
            self._variable.trace_vdelete('w', self.__tracecb)
        except AttributeError:
            pass
        else:
            del self._variable
        super().destroy()
        self.label = None
        self.scale = None

    def _on_theme_change(self, *args):
        """Update position and appearance on theme change."""
        def adjust_height():
            bbox = self.bbox(self._label)
            self.configure(height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])

        self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
        self.itemconfigure(self._label, fill=self._style.lookup('TLabel', 'foreground'))
        self._adjust()
        self.after_idle(adjust_height)

    def _adjust(self, *args):
        """Adjust the label position according to the scale."""
        def adjust_label():
            self.update_idletasks() # "force" scale redraw
            x, y = self.scale.coords()
            if self._label_top:
                y = 0
            else:
                y = self.scale.winfo_reqheight()
            # avoid that the label goes off the canvas
            bbox = self.bbox(self._label)
            x = min(max(x, 0), self.winfo_width() - (bbox[2] - bbox[0])/2)
            self.coords(self._label, x, y)  # move label
            self.configure(scrollregion=self.bbox('all'))
            self.yview_moveto(0)  # make sure everything is visible

        self.itemconfigure(self._scale, width=self.winfo_width())
        from_ = ttk._to_number(self.scale['from'])
        to = ttk._to_number(self.scale['to'])
        if to < from_:
            from_, to = to, from_
        newval = self._variable.get()
        if not from_ <= newval <= to:
            # value outside range, set value back to the last valid one
            self.value = self._last_valid
            return

        self._last_valid = newval
        self.itemconfigure(self._label, text=newval)
        self.after_idle(adjust_label)

    @property
    def value(self):
        """Return current scale value."""
        return self._variable.get()

    @value.setter
    def value(self, val):
        """Set new scale value."""
        self._variable.set(val)

root = tk.Tk()
style = ttk.Style(root)
style.theme_use('alt')

scale = LabeledScale(root, from_=0, to=100, compound='bottom')
scale.pack(expand=True, fill='x')

root.mainloop()
j_4321
  • 15,431
  • 3
  • 34
  • 61
  • Thank you but i had finally figured out how to create what i want. in `ttkwidgets.Tickscale` i set the `digits=0` and now the label below is disappeared and the value that slides is an integer – Gino_P Feb 19 '21 at 07:34