I'm trying to make a GUI where I want several text widgets in a scrollable area, and that the width of the text widget should vary when the window size is changed. But I'm having trouble getting it to work. I think it has something to do with the create_window(), as I can make it work without the scroll function.
Example code below
import tkinter as tk
class Window:
"""Main window"""
def __init__(self):
"""Initiate new gui window"""
self.root = tk.Tk()
self.root.minsize(800, 200)
self.root.grid_columnconfigure(0, weight=1)
background_grid = tk.Frame(master=self.root, bg="lightblue")
background_grid.grid(row=0, column=0, sticky="ew")
background_grid.grid_columnconfigure(0, weight=1)
# Scroll area
scroll = ScrollArea(background_grid, (0, 0))
scroll.grid.configure(bg="darkgrey")
scroll.canvas.grid_columnconfigure(0, weight=1)
scroll.grid.grid_columnconfigure(0, weight=1)
for i in range(0, 20):
tk.Label(master=scroll.grid, text="Text").grid(row=i * 2, column=0, sticky="w")
# Text box that I want to follow the size of the window
text_box = tk.Text(master=scroll.grid, height=10)
text_box.grid(row=i * 2 1, column=0, sticky="ew")
text_box.grid_columnconfigure(0, weight=1)
tk.Button(master=scroll.grid, text="Button").grid(row=i*2 1, column=1, sticky="e")
scroll.update(self.root)
class ScrollArea:
"""Generate a scrollable area"""
def __init__(self, master: tk.Frame or tk.Tk, coordinates: tuple, max_height=500, color="orange"):
column, row = coordinates
self.canvas = tk.Canvas(master=master, bg=color,
highlightcolor="grey", highlightbackground="grey", highlightthickness=1)
self.canvas.grid(row=row, column=column, sticky="ew")
self.grid = tk.Frame(master=self.canvas)
self.grid.grid(row=0, column=0, sticky="ew")
self.scrollbar = tk.Scrollbar(master=master, orient=tk.VERTICAL, command=self.canvas.yview)
self.scrollbar.grid(row=row, column=column 1, sticky="ns")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.create_window((0, 0), window=self.grid)
self.max_height = max_height
def update(self, root_window: tk.Tk or tk.Toplevel):
"""Update the gui"""
root_window.update_idletasks()
# Set size of the grid to use for scroll region
bbox = self.canvas.bbox(tk.ALL)
display_width, display_height = self.grid.winfo_width(), min(self.max_height, self.grid.winfo_height())
self.canvas.configure(scrollregion=bbox, width=display_width, height=display_height)
self.grid.bind("<Enter>", self._bound_to_mousewheel)
self.grid.bind("<Leave>", self._unbound_to_mousewheel)
# Scroll to the top
self.canvas.yview_moveto(0)
def _bound_to_mousewheel(self, event):
"""Binds mouse wheel to scroll when the mouse is over the report area"""
self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
_ = event # dummy
def _unbound_to_mousewheel(self, event):
"""Removes the mouse wheel bind when the mouse leaves the report area"""
self.canvas.unbind_all("<MouseWheel>")
_ = event # dummy
def _on_mousewheel(self, event):
"""On scroll, move the canvas"""
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
def main():
window = Window()
window.root.mainloop()
if __name__ == '__main__':
main()
CodePudding user response:
You need to do two things: you need to set the width of the embedded window when the canvas changes size, and use the anchor
option when creating the window.
class ScrollArea:
def __init__(self, master: tk.Frame or tk.Tk, coordinates: tuple,
...
self.grid_id = self.canvas.create_window((0, 0), window=self.grid, anchor="nw")
self.canvas.bind("<Configure>", self._resize_grid)
def _resize_grid(self, event):
self.canvas.itemconfigure(self.grid_id, width=event.width)