CustomTkinter icon indicating copy to clipboard operation
CustomTkinter copied to clipboard

Scrollbar is stuttering

Open IshanJ25 opened this issue 3 years ago • 9 comments
trafficstars

I was previously using ttk.Scrollbar widget, but thought of migrating to the ctk.CTkScrollbar because of easy customization. It looks good, but there is a problem ruins the scroll effect. When i hold down the scroll bar to move (not using the scroll wheel) through the page quickly, it creates this stuttering effect (shown in attached screen recording), while this does not occur in the native scroll bar.

Native:

        self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)

CTk:

        self.scrollbar = ctk.CTkScrollbar(self, orientation="vertical",
                                          command=self.canvas.yview,
                                          fg_color=ColorPalette.black_3,
                                          scrollbar_color=ColorPalette.black_4,
                                          scrollbar_hover_color=ColorPalette.black_5,
                                          width=30, corner_radius=10)

https://user-images.githubusercontent.com/86649457/175858559-fa04e43c-c9ae-4d3b-804a-bf1b373ad548.mp4

I hope the 2 parts of this video are differentiable. Sorry for low quality.

IshanJ25 avatar Jun 27 '22 04:06 IshanJ25

I don't know exactly what you mean. Is the rendering lagging? In the video it looks like the rendering lags a bit but also on the normal scrollbar. Or do you mean the scrolling is not smooth, and has bigger steps than the normal scrollbar?

TomSchimansky avatar Jun 27 '22 17:06 TomSchimansky

Here is a clearer version of the recording:

Native Scrollbar CTk Scrollbar

Yes, i believe the rendering is not smooth, as you can see in the video. This happens with both of the scrollbars, but is more noticeable in the CTkScrollbar

IshanJ25 avatar Jun 28 '22 04:06 IshanJ25

I was previously using ttk.Scrollbar widget, but thought of migrating to the ctk.CTkScrollbar because of easy customization. It looks good, but there is a problem ruins the scroll effect. When i hold down the scroll bar to move (not using the scroll wheel) through the page quickly, it creates this stuttering effect (shown in attached screen recording), while this does not occur in the native scroll bar.

Native:

        self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)

CTk:

        self.scrollbar = ctk.CTkScrollbar(self, orientation="vertical",
                                          command=self.canvas.yview,
                                          fg_color=ColorPalette.black_3,
                                          scrollbar_color=ColorPalette.black_4,
                                          scrollbar_hover_color=ColorPalette.black_5,
                                          width=30, corner_radius=10)

2022-06-27.09-23-32_2.mp4 I hope the 2 parts of this video are differentiable. Sorry for low quality.

https://github.com/TomSchimansky/CustomTkinter/issues/215#issue-1285209510

Nothing to do with the issue itself, but your gui seems nice

felipetesc avatar Jun 28 '22 21:06 felipetesc

Can you post your code? Or part of it with the scrollable frame?

TomSchimansky avatar Jun 29 '22 14:06 TomSchimansky

I just changes this line:

        self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)

to this:

        self.scrollbar = ctk.CTkScrollbar(self, orientation="vertical",
                                          command=self.canvas.yview,
                                          fg_color=ColorPalette.black_3,
                                          scrollbar_color=ColorPalette.black_4,
                                          scrollbar_hover_color=ColorPalette.black_5,
                                          width=30, corner_radius=10)

Code:

it is used in a class called ScrollableFrame (extends ttk.Frame object)

class ScrollableFrame(ttk.Frame):
    def __init__(self, container, *args, **kwargs):
        super().__init__(container, *args, **kwargs)
        self.canvas = tk.Canvas(self)

        # self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)

        self.scrollbar = ctk.CTkScrollbar(self, orientation="vertical",
                                          command=self.canvas.yview,
                                          fg_color=ColorPalette.black_3,
                                          scrollbar_color=ColorPalette.black_4,
                                          scrollbar_hover_color=ColorPalette.black_5,
                                          width=30, corner_radius=10)

        self.scrollable_frame = ttk.Frame(self.canvas)

        self.scrollable_frame.bind("<Configure>", lambda *args, **kwargs: self.canvas.configure(
            scrollregion=self.canvas.bbox("all")))

        self.bind_all("<MouseWheel>", self._on_mousewheel)
        self.bind("<Destroy>", lambda *args, **kwargs: self.unbind_all("<MouseWheel>"))

        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.pack(side="left", fill="both", expand=True)

        self.scrollbar.pack(side="right", fill="y")

    def _on_mousewheel(self, event):
        self.canvas.yview_scroll(-1 * round(event.delta / 120), "units")

Usage (unchanged):

        s = ttk.Style()
        s.configure('My.scrollable.TFrame', background=color.black_3)

        scrollable_frame = my_classes.ScrollableFrame(rec_frame)
        scrollable_frame.place(x=border, y=border, anchor='nw', width=stats_w, height=stats_h + border + rec_table_h)

        scrollable_frame.scrollable_frame.configure(style='My.scrollable.TFrame')
        scrollable_frame.canvas.configure(background=color.black_3, highlightthickness=0)

IshanJ25 avatar Jun 29 '22 15:06 IshanJ25

Can you post an executable one-file example where the effect is visible for you?

TomSchimansky avatar Jul 01 '22 19:07 TomSchimansky

you mean packing an example app in exe file?

This code works in windows 11:

from ctypes import windll
from tkinter import ttk
import tkinter as tk

from win32api import GetMonitorInfo, MonitorFromPoint
import customtkinter as ctk

###############################################
################# set scaling #################
###############################################

screensize_old = windll.user32.GetSystemMetrics(0)
windll.shcore.SetProcessDpiAwareness(1)
screensize_new = windll.user32.GetSystemMetrics(0)

scale = round(screensize_old / screensize_new, 2)

ctk.set_window_scaling(scale)
ctk.set_spacing_scaling(scale)
ctk.set_widget_scaling(scale)

###############################################
###############################################

work_area = GetMonitorInfo(MonitorFromPoint((0, 0))).get("Work")
screen_w, screen_h = work_area[2], work_area[3]

root_w = 800
root_h = 500
border = 20

lines = 100


###############################################
###############################################

class ScrollableFrame(ttk.Frame):
    def __init__(self, container, *args, **kwargs):
        super().__init__(container, *args, **kwargs)
        self.canvas = tk.Canvas(self)

        # self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)

        self.scrollbar = ctk.CTkScrollbar(self, orientation="vertical",
                                          command=self.canvas.yview,
                                          fg_color='#202020',
                                          scrollbar_color='#303030',
                                          scrollbar_hover_color='#404040',
                                          width=30, corner_radius=10)

        self.scrollable_frame = ttk.Frame(self.canvas)

        self.scrollable_frame.bind("<Configure>", lambda *args, **kwargs: self.canvas.configure(
            scrollregion=self.canvas.bbox("all")))

        self.bind_all("<MouseWheel>", self._on_mousewheel)
        self.bind("<Destroy>", lambda *args, **kwargs: self.unbind_all("<MouseWheel>"))

        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.canvas.configure(yscrollcommand=self.scrollbar.set)
        self.canvas.pack(side="left", fill="both", expand=True)

        self.scrollbar.place(relx=1, rely=0, relheight=1, anchor="ne")
        # self.scrollbar.pack(side="right", fill="y")

    def _on_mousewheel(self, event):
        self.canvas.yview_scroll(-1 * round(event.delta / 120), "units")


# ininitialize the root window
root = ctk.CTk(fg_color='#202020')
root.title("Scrollbar")
# spawn window in center of the screen
root.geometry(f"{root_w}x{root_h}+{int(screen_w / 2 - root_w / 2)}+{int(screen_h / 2 - root_h / 2)}")
root.resizable(False, False)

scrollable_frame = ScrollableFrame(root)
scrollable_frame.place(x=border, y=border, anchor='nw', width=root_w - 2 * border, height=root_h - 2 * border)

# configuring looks of the scrollbar frame
ttk_style = ttk.Style()
ttk_style.configure('My.scrollable.TFrame', background='#202020')
scrollable_frame.scrollable_frame.configure(style='My.scrollable.TFrame')
scrollable_frame.canvas.configure(background='#202020', highlightthickness=0)

# creating text labels in the scrollable frame
for i in range(lines):
    ctk.CTkLabel(
        scrollable_frame.scrollable_frame,
        fg_color='#404040',
        bg_color='#202020',
        width=root_w - 4 * border,
        corner_radius=10,
        text=f"Line {i + 1}",
        text_font="Consolas 24 normal",
    ).pack(pady=(10, 0))

root.mainloop()

IshanJ25 avatar Jul 02 '22 12:07 IshanJ25

any update?

IshanJ25 avatar Jul 06 '22 12:07 IshanJ25

how can we implement scroll on the frame?

Abdulsamipy avatar Jul 12 '22 11:07 Abdulsamipy