TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
11.51k stars 1.08k forks source link

Scrollbar is stuttering #215

Open IshanJ25 opened 2 years ago

IshanJ25 commented 2 years ago

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.

TomSchimansky commented 2 years ago

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?

IshanJ25 commented 2 years ago

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

felipetesc commented 2 years ago

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

TomSchimansky commented 2 years ago

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

IshanJ25 commented 2 years ago

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)
TomSchimansky commented 2 years ago

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

IshanJ25 commented 2 years ago

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 commented 2 years ago

any update?

Abdulsamipy commented 2 years ago

how can we implement scroll on the frame?