Reputation: 1975
In Python 3.x using a horizontal scrollbar and ttk treeview the x scroll by clicking on the arrows is very very slow, several pixels per second, I don't know how to fix this, here is a minimal example I made:
import tkinter as tk
from tkinter import ttk
app = tk.Tk()
t = ttk.Treeview(app)
t.pack(side="top",fill="both",expand=1)
xscroll = tk.Scrollbar(app,command=t.xview,orient="horizontal")
t.configure(xscrollcommand=xscroll.set)
xscroll.pack(side="top",fill="x")
tcols = ["header " + str(i)
for i in range(50)]
t.config(columns=tcols)
for h in tcols:
t.heading(h,text=h)
for i in range(5):
t.insert("","end",
text = "item" + str(i),
values = ["value" + str(x) for x in range(49)])
app.geometry("{}x{}".format(800, 600))
it's the arrow clicking scrolling that is slow, the dragging using the bar is fine
Upvotes: 2
Views: 1578
Reputation: 4482
All you need to know that xview
method of XView
"mix-in"-class is just another one callback, that acts differently depending on a user interactions:
If the user drags the scrollbar slider, xview
is called as
xview(self, *('moveto', some_offset))
If the user clicks in the trough, xview
is called as xview(self, *('scroll', some_step, 'pages'))
.
If the user clicks the arrows, xview
is called as xview(self, *('scroll', some_step, 'units'))
.
As you can see - not so complicated! Your problem lies in fact, that some_step
is always either 1 or -1 string value in the last case, so all you need just multiply that value by some multiplier. Easy as pie!
As a solution, the most lightweight approach that I see here creates a derived class, that inherits from ttk.Treeview
with overrided xview
method.
Try this Treeview
in your programm:
class CustomTreeview(ttk.Treeview):
def __init__(self, parent, *args, **kwargs):
ttk.Treeview.__init__(self, parent, *args, **kwargs)
self.vanilla_xview = tk.XView.xview
def xview(self, *args):
# here's our multiplier
multiplier = 100
if 'units' in args:
# units in args - user clicked the arrows
# time to build a new args with desired increment
mock_args = args[:1] + (str(multiplier * int(args[1])),) + args[2:]
return self.vanilla_xview(self, *mock_args)
else:
# just do default things
return self.vanilla_xview(self, *args)
Upvotes: 5
Reputation: 15226
One way we can speed up the scrolling is to create a few functions to help us along and use some bindings.
first we need a variable we can update to monitor if the mouse button is clicked down or released.
scrolling = False
next we need to create a few functions that we can use to start and stop the scrolling as well as creating a looping function that increases the speed of scrolling.
# uses that status of `scrolling` to keep scrolling or to stop for either arrow button
def scrolling_active(arrow, *args):
global scrolling
if scrolling == True:
if arrow == "arrow1":
t.tk.call(t._w,'xview', 'scroll', -10, 'units')
if arrow == "arrow2":
t.tk.call(t._w,'xview', 'scroll', 10, 'units')
app.after(10, lambda a = arrow: scrolling_active(a))
# sets scrolling to True and activates the looping function to start scrolling
def start_scrolling(event):
global scrolling
scrolling = True
scrolling_active(xscroll.identify(event.x, event.y))
# on mouse release this sets scrolling to false. This stops the loop above
def stop_scrolling(event):
global scrolling
scrolling = False
Now we need to bind the start and stop functions to our scroll widget for mouse click and release.
xscroll.bind("<Button-1>", start_scrolling)
xscroll.bind('<ButtonRelease-1>', stop_scrolling)
With those changes we get arrows that scroll faster when clicked!
You can change the speed up to your liking by editing the int value in:
t.tk.call(t._w,'xview', 'scroll', -10, 'units')
t.tk.call(t._w,'xview', 'scroll', 10, 'units')
Take a look at the below code:
import Tkinter as tk
import ttk
app = tk.Tk()
t = ttk.Treeview(app)
t.pack(side="top",fill="both",expand=1)
scrolling = False
xscroll = tk.Scrollbar(app,command=t.xview,orient="horizontal")
t.configure(xscrollcommand=xscroll.set)
xscroll.pack(side="top",fill="x")
def scrolling_active(arrow, *args):
global scrolling
if scrolling == True:
if arrow == "arrow1":
t.tk.call(t._w,'xview', 'scroll', -10, 'units')
if arrow == "arrow2":
t.tk.call(t._w,'xview', 'scroll', 10, 'units')
app.after(10, lambda a = arrow: scrolling_active(a))
def start_scrolling(event):
global scrolling
scrolling = True
scrolling_active(xscroll.identify(event.x, event.y))
def stop_scrolling(event):
global scrolling
scrolling = False
xscroll.bind("<Button-1>", start_scrolling)
xscroll.bind('<ButtonRelease-1>', stop_scrolling)
tcols = ["header " + str(i)
for i in range(50)]
t.config(columns=tcols)
for h in tcols:
t.heading(h,text=h)
for i in range(5):
t.insert("","end",
text = "item" + str(i),
values = ["value" + str(x) for x in range(49)])
app.geometry("{}x{}".format(800, 600))
app.mainloop()
Upvotes: 1