0
\$\begingroup\$

I used to use the Tkinter scale widget for this, but it is limited in several ways. So I made a custom Timeline with the Canvas widget, which makes a more dynamically changeable Timeline.

from tkinter import Canvas, Frame

class CustomScale(Canvas):
    def __init__(self, master=None, *, command=None, to=100,from_=0,**kwargs):
        super().__init__(master, **kwargs)
        self.min_value = from_
        self.max_value = to
        self.value = 0
        self.master = master
        self.slider_width = 40
        self.command_flag = 0
        self.buffer_value = 0
        self.command = command
        self.height = 20  
        self.configure(height=self.height)
        self.set_real_value()   
        self.set_resolution()
        self.slider = self.create_rectangle(self.value, 0, self.value + self.slider_width, self.height, fill='gray', tags='slider')
        self.left_side = self.create_rectangle(0, 0, self.value,self.height, fill='black', tags='left_side')        
        self.right_side = self.create_rectangle(self.value + self.slider_width, 0, self.winfo_width(), self.height, fill='red', tags='right_side')
        self.buffer = self.create_rectangle(self.value + self.slider_width, 2,self.value + self.slider_width + self.buffer_value , self.height-2, fill='blue', tags='buffer')
        self.bind('<Configure>', self.set_resolution)
        self.bind('<Button-1>', self.on_event)
        self.bind('<B1-Motion>', self.on_event)

    def set_real_value(self):
        relation = self.value / (self.winfo_width()-self.slider_width)
        self.real_value = self.min_value + relation * (self.max_value-self.min_value) 

    def set_resolution(self, event = None):
        self.value = (self.real_value / (self.max_value-self.min_value)) * (self.winfo_width()-self.slider_width)
        self.value= max(0, min(self.winfo_width()-self.slider_width, self.value))
        self.set_coords()

    def set_slider(self):
        self.set_coords()
        self.set_real_value()   

    def set_coords(self):
        self.coords('slider', self.value, 0, self.value + self.slider_width, self.height)
        self.coords('left_side', 0,0,self.value,self.height)
        self.coords('right_side', self.value + self.slider_width,0,self.winfo_width(),self.height)
        self.coords('buffer', self.value + self.slider_width, 2,self.value + self.slider_width + self.buffer_value , self.height-2)

    def on_event(self, event):
        self.value = max(0, min(self.winfo_width()-self.slider_width, event.x))
        self.set_slider()
        if self.command and self.command_flag == 0:
            self.command_flag = 1
            self.after(200, self.execute_command)

    def execute_command(self):
        self.command()
        self.set_real_value()
        print(self.real_value)
        self.command_flag = 0




    def get(self):
        relation = self.value / (self.winfo_width()-self.slider_width)
        return relation * (self.max_value-self.min_value)

    def set(self, value = False, buffer = 0):
        if buffer:
            self.buffer_value = buffer
            relation = buffer / (self.max_value-self.min_value)
            buffer = relation * (self.winfo_width()-self.slider_width)
            self.buffer_value= max(0, min(self.winfo_width()-self.slider_width, self.value))
        if value:
            relation = value / (self.max_value-self.min_value)
            self.value = relation * (self.winfo_width()-self.slider_width)
            self.value= max(0, min(self.winfo_width()-self.slider_width, self.value))
        if value or buffer:
            self.set_slider()

    def config(self, **kwargs):
        self.set_real_value()   
        for key, value in kwargs.items():
            if key == 'from_':
                self.min_value = value
            elif key == 'to':
                self.max_value = value
            elif key == 'value':
                self.real_value = value
        self.value = (self.real_value / (self.max_value-self.min_value)) * (self.winfo_width()-self.slider_width)
        self.value= max(0, min(self.winfo_width()-self.slider_width, self.value))
        self.set_coords()

enter image description here

\$\endgroup\$
1
  • \$\begingroup\$ I know why there's no unit tests in this submission -- it's hard to even begin to create an object without a graphic context. We see some Business Logic in the set method that could benefit from automated testing. Consider using a technique like humble object to separate the display & business logic from each other. \$\endgroup\$
    – J_H
    Mar 26 at 16:39

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.