I put together many part of code found to make a fully functional scrolled Frame in tkinter (class ScrolledWindow). It's working good.
Still remaining a little problem when scrolling with mousewheel (when no scrollbar present). I supposed a problem of border or something like that but can't find the problem or the solution (quite new to tkinter). Some help will be appreciate to perfect this tkinter scrolledFrame.
The code sample:
# -*- coding: utf8 -*-
'''
Created on 16 juin 2016
@author: Chevalier Thierry
'''
import tkinter, tkinter.scrolledtext
import tkinter.ttk
class ScrolledWindow(tkinter.ttk.Frame):
"""
Parent = master of scrolled window
"""
def __init__(self, parent, *args, **kwargs):
"""Parent = master of scrolled window
"""
super().__init__(parent, *args, **kwargs)
self.parent = parent
# creating a scrollbars and canvas
self.xScrollbar = tkinter.ttk.Scrollbar(self, orient = 'horizontal')
self.yScrollbar = tkinter.ttk.Scrollbar(self)
self.scrollCanvas = tkinter.Canvas(self)
##self.scrollCanvas.config(relief = 'flat', width = 100, heigh = 100, bd = 0)
self.scrollCanvas.config(relief = 'flat', bd = 0)
# placing scrollbar and canvas into frame
self.xScrollbar.grid(column = 0, row = 1, sticky="NESW", columnspan = 2)
self.yScrollbar.grid(column = 1, row = 0, sticky="NESW")
self.scrollCanvas.grid(column = 0, row = 0, columnspan=1,rowspan=1,sticky="NESW",padx=0,pady=0,ipadx=0,ipady=0)
self.grid_columnconfigure(0,weight=1)
self.grid_columnconfigure(1,weight=0)
self.grid_rowconfigure(0,weight=1)
self.grid_rowconfigure(1,weight=0)
# accociating scrollbar comands to canvas scroling
self.xScrollbar.config(command = self.scrollCanvas.xview)
self.yScrollbar.config(command = self.scrollCanvas.yview)
self.scrollCanvas.config(xscrollcommand = self.xScrollbar.set, yscrollcommand = self.yScrollbar.set)
# creating a frame to inserto to canvas
self.scrollWindow = tkinter.ttk.Frame(self.scrollCanvas)
self.scrollWindowItemId = self.scrollCanvas.create_window(0, 0, window = self.scrollWindow, anchor = 'nw')
self.scrollWindow.bind('<Configure>', self._configure_scrollWindow)
self.scrollCanvas.bind('<Configure>', self._configure_scrollCanvas)
self.scrollWindow.bind('<Enter>', self._bound_to_mousewheel)
self.scrollWindow.bind('<Leave>', self._unbound_to_mousewheel)
def _bound_to_mousewheel(self, event):
self.scrollCanvas.bind_all("<MouseWheel>", self._on_mousewheel)
def _unbound_to_mousewheel(self, event):
self.scrollCanvas.unbind_all("<MouseWheel>")
def _on_mousewheel(self, event):
self.scrollCanvas.yview_scroll(int(-1*(event.delta/120)), "units")
def _configure_scrollWindow(self, event):
print("_configure_scrollWindow:")
print(" scrollWindow", "w=", event.width, "h", event.height)
size = (self.scrollWindow.winfo_reqwidth(), self.scrollWindow.winfo_reqheight())
print(" scrollWindow reqwidth and reqheight", "w=", size[0], ", h=", size[1])
print(" scrollCanvas", "w=", self.scrollCanvas.winfo_width(), ", h=", self.scrollCanvas.winfo_height())
def _configure_scrollCanvas(self, event):
print("_configure_scrollCanvas:")
print(" scrollCanvas", "w=", event.width, self.scrollCanvas.winfo_width(), ", h=", event.height, self.scrollCanvas.winfo_height())
#=======================================================================
# if self.scrollWindow.winfo_reqwidth() != self.scrollCanvas.winfo_width():
# # update the inner frame's width to fill the canvas
# self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=self.scrollCanvas.winfo_width())
# if self.scrollWindow.winfo_reqheight() != self.scrollCanvas.winfo_height():
# # update the inner frame's width to fill the canvas
# self.scrollCanvas.itemconfig(self.scrollWindowItemId, height=self.scrollCanvas.winfo_height())
#=======================================================================
canvasWidth, canvasHeight = (self.scrollCanvas.winfo_width(), self.scrollCanvas.winfo_height())
canvasWidth, canvasHeight = (event.width, event.height)
windowReqWidth, windowReqHeight = (self.scrollWindow.winfo_reqwidth(), self.scrollWindow.winfo_reqheight())
if windowReqWidth < canvasWidth:
if windowReqHeight < canvasHeight:
# windowReqWidth < canvasWidth and windowReqHeight < canvasHeight
self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=canvasWidth, height=canvasHeight)
##self.scrollCanvas.config(scrollregion='0 0 %s %s' % (canvasWidth, canvasHeight))
self.scrollCanvas.config(scrollregion=self.scrollCanvas.bbox("all"))
self.tk.call("grid", "remove", self.xScrollbar)
self.tk.call("grid", "remove", self.yScrollbar)
else:
# windowReqWidth < canvasWidth and windowReqHeight > canvasHeight
self.scrollCanvas.config(width = canvasWidth, height = windowReqHeight)
self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=canvasWidth, height=windowReqHeight)
##self.scrollCanvas.config(scrollregion='0 0 %s %s' % (canvasWidth, windowReqHeight))
self.scrollCanvas.config(scrollregion=self.scrollCanvas.bbox("all"))
self.tk.call("grid", "remove", self.xScrollbar)
self.yScrollbar.grid()
else: # windowReqWidth > canvasWidth
if windowReqHeight < canvasHeight:
# windowReqWidth > canvasWidth and windowReqHeight < canvasHeight
self.scrollCanvas.config(width = windowReqWidth, height = canvasHeight)
self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=windowReqWidth, height=canvasHeight)
##self.scrollCanvas.config(scrollregion='0 0 %s %s' % (windowReqWidth, canvasHeight))
self.scrollCanvas.config(scrollregion=self.scrollCanvas.bbox("all"))
self.xScrollbar.grid()
self.tk.call("grid", "remove", self.yScrollbar)
else:
# windowReqWidth > canvasWidth and windowReqHeight > canvasHeight
self.scrollCanvas.config(width = windowReqWidth, height = windowReqHeight)
self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=windowReqWidth, height=windowReqHeight)
##self.scrollCanvas.config(scrollregion='0 0 %s %s' % (windowReqWidth, windowReqHeight))
self.scrollCanvas.config(scrollregion=self.scrollCanvas.bbox("all"))
self.xScrollbar.grid()
self.yScrollbar.grid()
def getScrollWindow(self):
return self.scrollWindow
class tkTestingGUI(tkinter.ttk.Frame):
def __init__(self, master):
super().__init__(master)
self.initialize()
self.pack(fill="both", expand=1)
self.master.resizable(True,True)
def initialize(self):
#
# main frame definition
#
leftFrame=tkinter.ttk.Frame(self, borderwidth=0)
rightFrame=tkinter.ttk.Frame(self, borderwidth=0)
leftFrame.grid(column=0,row=0,columnspan=1,rowspan=1,sticky=tkinter.N+tkinter.S+tkinter.E+tkinter.W,padx=1,pady=1,ipadx=0,ipady=0)
rightFrame.grid(column=1,row=0,columnspan=1,rowspan=1,sticky='NESW',padx=1,pady=1,ipadx=0,ipady=0)
self.grid_columnconfigure(0,weight=1)
self.grid_columnconfigure(1,weight=1)
self.grid_rowconfigure(0,weight=1)
scrolledLeftFrame = ScrolledWindow(self, borderwidth=0)
rightFrame=tkinter.ttk.Frame(self, borderwidth=0)
scrolledLeftFrame.grid(column=0,row=0,columnspan=1,rowspan=1,sticky=tkinter.N+tkinter.S+tkinter.E+tkinter.W,padx=1,pady=1,ipadx=0,ipady=0)
rightFrame.grid(column=1,row=0,columnspan=1,rowspan=1,sticky="NESW",padx=1,pady=1,ipadx=0,ipady=0)
self.grid_columnconfigure(0,weight=1)
self.grid_columnconfigure(1,weight=1)
self.grid_rowconfigure(0,weight=1)
# get real inside left frame
leftFrame = scrolledLeftFrame.getScrollWindow() # real inside window to put widgets
#
# left frame definition
#
self.idFrame = tkinter.ttk.Frame(leftFrame, borderwidth=0)
##self.idFrame.grid(column=0,row=0,columnspan=1,rowspan=1,sticky='NESW', padx=1,pady=1,ipadx=0,ipady=0)
self.idFrame.grid(column=0,row=0,sticky='NESW')
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=0, row=0, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=1, row=0, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=2, row=0, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=0, row=1, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=1, row=1, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=2, row=1, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test", anchor="e").grid(column=3, row=1, sticky=tkinter.E)
entry2 = tkinter.ttk.Entry(self.idFrame,text="A")
entry2.grid(column=4,row=0,columnspan=1,rowspan=1,sticky='EW',padx=1,pady=1,ipadx=0,ipady=0)
self.idFrame.grid_columnconfigure(0,weight=1)
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=0, row=2, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=1, row=2, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=2, row=2, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=0, row=3, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=1, row=3, sticky="NESW")
label = tkinter.ttk.Label(self.idFrame, text="Test").grid(column=2, row=3, sticky="NESW")
#
self.activeEventsFrame = tkinter.ttk.Frame(leftFrame, borderwidth=0)
self.activeEventsFrame.grid(column=0,row=1,columnspan=1,rowspan=1,sticky='NESW',padx=1,pady=1,ipadx=0,ipady=0)
label = tkinter.ttk.Label(self.activeEventsFrame, text="Test").grid(column=0, row=0, sticky="NESW")
label = tkinter.ttk.Label(self.activeEventsFrame, text="Test").grid(column=1, row=0, sticky="NESW")
label = tkinter.ttk.Label(self.activeEventsFrame, text="Test long").grid(column=2, row=0, sticky="NESW")
#
leftFrame.grid_columnconfigure(0,weight=1)
leftFrame.grid_rowconfigure(0,weight=1)
leftFrame.grid_rowconfigure(1,weight=1)
#
# right frame definition
#
self.inputText = tkinter.scrolledtext.ScrolledText(rightFrame)
self.inputText.insert(tkinter.END, "Paste your text here.....")
self.inputText.grid(column=0,row=0,columnspan=1,rowspan=1,sticky="NESW",padx=1,pady=1,ipadx=0,ipady=0)
#
rightFrame.grid_columnconfigure(0,weight=1)
rightFrame.grid_rowconfigure(0,weight=1)
#
# update main frame
#
self.update()
def quit(self):
pass
if __name__ == "__main__":
tkinterTk = tkinter.Tk()
app = tkTestingGUI(master=tkinterTk)
app.master.title('Testing TK')
app.mainloop()
I found the solution, finally, just need to add highlightthickness=0 option to canvas because default is not null.
Find below the fully functionnal scrolledFrame with autohiding scrollbars and mousewheel scrolling (little modifications added for better behaviour)