Tkinter: Полностью функциональный scrolledFrame с автоматически скрытой полосой прокрутки, за исключением границы
Я собрал много частей кода, чтобы сделать полнофункциональную прокрутку Frame
в ткинтер (class ScrolledWindow
). Работает хорошо.
Все еще остается небольшая проблема при прокрутке колесиком мыши (когда полоса прокрутки отсутствует). Я предположил проблему границы или что-то подобное, но не могу найти проблему или решение (совершенно новое для tkinter). Некоторая помощь будет полезна для совершенствования этого прокручиваемого фрейма tkinter.
Пример кода:
# -*- 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()
1 ответ
Я нашел решение, наконец, просто нужно добавить опцию highlightthickness=0 на холст, потому что по умолчанию не ноль.
Найдите ниже полностью функциональную scrolledFrame с автоматически скрывающимися полосами прокрутки и прокруткой колесика мыши (для лучшего поведения добавлены небольшие модификации)
# -*- coding: utf8 -*-
'''
Created on 16 juin 2016
@author: Chevalier Thierry
'''
import tkinter
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 scrollbars and canvas and window to contain future widgets of the scrolling area
self.xScrollbar = tkinter.ttk.Scrollbar(self, orient = 'horizontal')
self.yScrollbar = tkinter.ttk.Scrollbar(self)
self.scrollCanvas = tkinter.Canvas(self)
self.scrollCanvas.config(relief = 'flat', borderwidth=0, highlightthickness=0)
self.scrollWindow = tkinter.ttk.Frame(self.scrollCanvas, borderwidth=0)
# placing scrollbar and canvas into frame and the scrollFrame into canvas
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.scrollWindowItemId = self.scrollCanvas.create_window(0, 0, window = self.scrollWindow, anchor = 'nw')
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)
##self.yScrollbar.lift(self.scrollWindow) # put to first plan
##self.xScrollbar.lift(self.scrollWindow)
self.scrollCanvas.bind('<Configure>', self._configure_scrollCanvas)
##self.scrollWindow.bind('<Configure>', self._configure_scrollWindow)
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(" event", "w=", event.width, "h", event.height)
print(" scrollWindow", "w=", self.scrollWindow.winfo_width(), ", h=", self.scrollWindow.winfo_height())
print(" scrollWindow reqwidth and reqheight", "w=", self.scrollWindow.winfo_reqwidth(), ", h=", self.scrollWindow.winfo_reqheight())
print(" scrollCanvas", "w=", self.scrollCanvas.winfo_width(), ", h=", self.scrollCanvas.winfo_height())
def _configure_scrollCanvas(self, event):
#=======================================================================
# print("_configure_scrollCanvas:")
# print(" event", "w=", event.width, "h", event.height)
# print(" scrollCanvas", "w=", self.scrollCanvas.winfo_width(), ", h=", self.scrollCanvas.winfo_height())
# print(" scrollWindow", "w=", self.scrollWindow.winfo_width(), ", h=", self.scrollWindow.winfo_height())
# print(" scrollWindow reqwidth and reqheight", "w=", self.scrollWindow.winfo_reqwidth(), ", h=", self.scrollWindow.winfo_reqheight())
#=======================================================================
##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 canvasWidth < windowReqWidth:
if canvasHeight < windowReqHeight:
# canvasWidth < windowReqWidth and canvasHeight < windowReqHeight
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()
else:
# canvasWidth < windowReqWidth and windowReqHeight <= canvasHeight
self.scrollCanvas.config(width = windowReqWidth)
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
if canvasHeight < windowReqHeight:
# windowReqWidth <= canvasWidth and canvasHeight < windowReqHeigh
self.scrollCanvas.config(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 and windowReqHeight <= canvasHeight
self.scrollCanvas.itemconfig(self.scrollWindowItemId, width=canvasWidth, height=canvasHeight)
self.scrollCanvas.config(scrollregion=self.scrollCanvas.bbox("all"))
self.tk.call("grid", "remove", self.xScrollbar)
self.tk.call("grid", "remove", self.yScrollbar)
def getScrollWindow(self):
return self.scrollWindow