Scroll bars not working correctly with canvas - python

The scroll bars move the canvas but they always snap back to the top or the left. What am I doing wrong?
import tkinter as Tk
root = Tk.Tk()
root.rowconfigure(0,weight=1)
root.columnconfigure(0,weight=1)
frame = Tk.Frame(root)
frame.grid(row=0, column=0, sticky='NSEW')
frame.rowconfigure(0,weight=1)
frame.columnconfigure(0,weight=1)
canvas = Tk.Canvas(frame)
canvas.grid(row=0, column=0, sticky='NSEW')
scroll_x = Tk.Scrollbar(frame, orient="horizontal", command=canvas.xview)
scroll_x.grid(row=1, column=0, sticky="ew")
scroll_y = Tk.Scrollbar(frame, orient="vertical", command=canvas.yview)
scroll_y.grid(row=0, column=1, sticky="ns")
canvas.create_oval(0,0,1333,1000)
canvas.configure(scrollregion=canvas.bbox("all"))
root.mainloop()

Scrollbars require two-way communication with the widgets they are controlling. You've properly configured the scrollbars but haven't configured the canvas to update the scrollbars.
To do that, add the following code after you've defined the canvas and scrollbars:
canvas.configure(yscrollcommand=scroll_y.set, xscrollcommand=scroll_x.set)

Related

Sub Frame Is Not Expanded When Its Parent Is

import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
left_frame_1 = tk.Frame(root, background="#ff0000")
left_frame_1.grid(row=0, column=0)
left_frame_2 = tk.Frame(left_frame_1)
left_frame_2.grid(row=0, column=0)
left_label_1 = tk.Label(left_frame_2, text="HELLO")
left_label_2 = tk.Label(left_frame_2, text="WORLD")
left_label_3 = tk.Label(left_frame_2, text="=D")
left_label_1.grid(row=0, column=0)
left_label_2.grid(row=1, column=0)
left_label_3.grid(row=2, column=0)
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0)
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0)
root.mainloop()
When my parent frame expands to all its free space, the child frame doesn't, instead it just stays on top.
I've been testing if .grid() has something to do with it, but haven't found anything.
Even if I add sticky="nsew" to both the frame and the label, there is still no change.
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0, sticky="nsew")
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0, sticky="nsew")
My goal is for the parent frame (the one with the green color) to expand to all available space (which I've achieved), and for the child frame containing the label to expand.
right_frame_2 looks because it expands.
right_frame_1 is not visible because it is completely covered by right_frame_2.
I hope your help, thank you.
To get the result of the last image in the question, you need to:
change sticky options of .grid() for right_frame_2 and right_label_1
set weight options of .rowconfigure() and .columnconfigure() on root, right_frame1 and right_frame_2
import tkinter as tk
import tkinter.ttk as ttk
root = tk.Tk()
left_frame_1 = tk.Frame(root, background="#ff0000")
left_frame_1.grid(row=0, column=0)
left_frame_2 = tk.Frame(left_frame_1)
left_frame_2.grid(row=0, column=0)
left_label_1 = tk.Label(left_frame_2, text="HELLO")
left_label_2 = tk.Label(left_frame_2, text="WORLD")
left_label_3 = tk.Label(left_frame_2, text="=D")
left_label_1.grid(row=0, column=0)
left_label_2.grid(row=1, column=0)
left_label_3.grid(row=2, column=0)
right_frame1 = tk.Frame(root, background="#00ff00")
right_frame1.grid(row=0, column=1, sticky="nsew")
right_frame_2 = tk.Frame(right_frame1, background="#0000ff")
right_frame_2.grid(row=0, column=0, sticky="nsew") # expand to fill available space
right_label_1 = tk.Label(right_frame_2, text="CENTER ME!")
right_label_1.grid(row=0, column=0, sticky="ew") # expand horizontally
root.rowconfigure(0, weight=1) # make left and right frame expand vertically
root.columnconfigure(1, weight=1) # make right frame expand horizontally
# allocate all space to right_frame_2
right_frame1.rowconfigure(0, weight=1)
right_frame1.columnconfigure(0, weight=1)
# allocate all space of right_frame_2 to right_label_1
right_frame_2.rowconfigure(0, weight=1)
right_frame_2.columnconfigure(0, weight=1)
root.mainloop()
Result:
When the window is resized:

how to expand canvas window to fill the full size window in tkinter

I am new to Tkinter, I am trying to create a full-screen scrollable frame using Tkinter and canvas. here is my code:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title('Learn To Code at Codemy.com')
root.geometry("500x400")
def FrameWidth(event):
canvas_width = event.width
canvas_height = event.height
my_canvas.itemconfig(canvas_frame, width = canvas_width)
# my_canvas.itemconfig(canvas_frame, height = canvas_height)
# my_canvas.itemconfig(canvas_frame, width = canvas_width, height = canvas_height)
def OnFrameConfigure(event):
my_canvas.configure(scrollregion=my_canvas.bbox("all"))
# Create A Main Frame
main_frame = Frame(root)
main_frame.grid(row=0, column=0, sticky='news')
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
main_frame.grid_columnconfigure(0, weight=1)
main_frame.grid_rowconfigure(0, weight=1)
# Create A Canvas
my_canvas = Canvas(main_frame, bg='red')
my_canvas.grid(row=0, column=0, sticky='news')
my_canvas.grid_columnconfigure(0, weight=1)
my_canvas.grid_rowconfigure(0, weight=1)
# Create ANOTHER Frame INSIDE the Canvas
second_frame = Frame(my_canvas, bg='blue')
# Add that New frame To a Window In The Canvas
canvas_frame = my_canvas.create_window((0,0), window=second_frame, anchor="nw")
# Add A Scrollbar To The Canvas
my_scrollbar = ttk.Scrollbar(main_frame, orient=VERTICAL, command=my_canvas.yview)
my_scrollbar.grid(row=0, column=1, sticky='ns')
my_canvas.configure(yscrollcommand=my_scrollbar.set)
main_frame.grid_rowconfigure(0, weight=1)
main_frame.grid_columnconfigure(0, weight=5)
main_frame.grid_columnconfigure(1, weight=0)
# Configure The Canvas
my_canvas.bind('<Configure>', FrameWidth)
second_frame.bind('<Configure>', OnFrameConfigure)
for thing in range(5):
Button(second_frame, text=f'Button {thing} Yo!').grid(row=thing, column=0, pady=10, padx=10, sticky = 'news')
my_label = Label(second_frame, text="It's Friday Yo!").grid(row=3, column=1, sticky='news')
second_frame.grid_columnconfigure(0,weight=4)
second_frame.grid_columnconfigure(1,weight=1)
root.mainloop()
my problem is that the frame with a blue background does not expand to full size and fill the canvas window, here is a screenshot of my app, my question is how to expand the second frame to fill entire window:
enter image description here

Tkinter scrollbars not filling or aligning correctly

I'm trying to create a frame with both a vertical and horizontal scrollbar, but the horizontal one seems to pack next to the canvas, and not below it. I have the scrollbar packed with side=tk.BOTTOM and fill=tk.X, so I'm not sure what else I need to add. What should I do to get the horizontal scrollbar to stretch across the entire canvas?
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
frame=tk.Frame(root)
frame.pack()
canvas=tk.Canvas(frame, height=200, width=200, background="blue")
canvas.pack(side=tk.LEFT)
yscrollbar=tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
yscrollbar.pack(side=tk.RIGHT, fill=tk.Y)
xscrollbar=tk.Scrollbar(frame, orient=tk.HORIZONTAL, command=canvas.xview)
xscrollbar.pack(side=tk.BOTTOM, fill=tk.X)
canvas.configure(yscrollcommand=yscrollbar.set)
canvas.configure(xscrollcommand=xscrollbar.set)
root.mainloop()
Right now the canvas is set to side=tk.LEFT. If I set it to side=tk.TOP, the reverse problem happens.
The issue comes from the order in which you pack the widgets. The idea is that pack uses the remaining space to put what is left, so you need to pack first the scrollbars, then at last the canvas:
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
frame=tk.Frame(root)
frame.pack()
canvas=tk.Canvas(frame, height=200, width=200, background="blue")
yscrollbar=tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
xscrollbar=tk.Scrollbar(frame, orient=tk.HORIZONTAL, command=canvas.xview)
xscrollbar.pack(side=tk.BOTTOM, fill=tk.X)
yscrollbar.pack(side=tk.RIGHT, fill=tk.Y)
canvas.pack(side=tk.LEFT)
canvas.configure(yscrollcommand=yscrollbar.set)
canvas.configure(xscrollcommand=xscrollbar.set)
root.mainloop()
However, because pack is not so intuitive when it comes to more complex GUI, I prefer to use grid:
xscrollbar.grid(row=1, column=0, sticky='ew')
yscrollbar.grid(row=0, column=1, sticky='ns')
canvas.grid(row=0, column=0, sticky='ewns')
But in this case, if you want your GUI to resize properly, you will need to add
frame.columnconfigure(0, weight=1)
frame.rowconfigure(0, weight=1)
so that the row and column 0 of the grid will fill all the available space.
In my env, calling canvas.pack and frame.pack after defining y and xscrollbar worked fine.
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
frame=tk.Frame(root)
yscrollbar=tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
yscrollbar.pack(side=tk.RIGHT, fill=tk.Y)
xscrollbar=tk.Scrollbar(frame, orient=tk.HORIZONTAL, command=canvas.xview)
xscrollbar.pack(side=tk.BOTTOM, fill=tk.X)
canvas=tk.Canvas(frame, height=200, width=200, background="blue")
canvas.pack(side=tk.LEFT)
frame.pack()
canvas.configure(yscrollcommand=yscrollbar.set)
canvas.configure(xscrollcommand=xscrollbar.set)
root.mainloop()

Tkinter canvas & scrollbar with grid

I have a canvas in a frame
photoFrame = Frame(centerFrame, width=250, height=190, bg="#EBEBEB")
photoFrame.grid(row=0, column=1, sticky="nsew")
photoCanvas = Canvas(photoFrame, bg="#EBEBEB")
photoCanvas.grid(row=0, column=0, sticky="nsew")
and I try to put a scrollbar to my canvas with this
photoScroll = Scrollbar(photoFrame, orient=VERTICAL)
photoScroll.config(command=photoCanvas.yview)
photoCanvas.config(yscrollcommand=photoScroll.set)
photoScroll.grid(row=0, column=1, sticky="ns")
The scrollbar appears but it's disabled. Can you help me please ?
Sorry for my bad english.
In a for loop I add lots of Image button with this code
element = Button(photoCanvas, image = listPhotos[i], borderwidth=0, height = 200, width = 200, bg="#EBEBEB")
element.grid(row=rowPhoto, column=columnPhoto, padx=5, pady=5, sticky="nsew")
Finnally I have this
root = Tk()
photoFrame = Frame(root, width=250, height=190, bg="#EBEBEB")
photoCanvas = Canvas(photoFrame, bg="#EBEBEB")
photoCanvas.grid(row=0, column=0, sticky="nsew")
for i in range(0, len(listPhotos), 1):
element = Button(photoCanvas, image = listPhotos[i], borderwidth=0, height = 200, width = 200, bg="#EBEBEB")
element.grid(row=rowPhoto, column=columnPhoto, padx=5, pady=5, sticky="nsew")
photoScroll=Scrollbar(photoFrame,orient=VERTICAL)
photoScroll.config(command=photoCanvas.yview)
photoCanvas.config(yscrollcommand=photoScroll.set)
photoScroll.grid(row=0, column=1, sticky="ns")
in my app, the purple rectangle is the next frame and I need a vertical scrollbar
Say if you have some questions
One way to scroll a group of widgets is to put them (with grid of pack) inside a frame and put this frame inside a canvas.
The two key elements (besides connecting the scrollbar to the canvas) for the scrolling to work are:
Use canvas.create_window(x, y, window=frame) to put the frame inside the canvas so that it is treated like a canvas item.
Update the canvas scrollregion each time the size of the frame changes (for instance after adding a new widget) with canvas.configure(scrollregion=canvas.bbox('all')).
Here is an adaptation of the code of the question Python Tkinter scrollbar for frame, but using the widgets name from the OP's question and grid instead of pack:
import tkinter as tk
def update_scrollregion(event):
photoCanvas.configure(scrollregion=photoCanvas.bbox("all"))
root = tk.Tk()
photoFrame = tk.Frame(root, width=250, height=190, bg="#EBEBEB")
photoFrame.grid()
photoFrame.rowconfigure(0, weight=1)
photoFrame.columnconfigure(0, weight=1)
photoCanvas = tk.Canvas(photoFrame, bg="#EBEBEB")
photoCanvas.grid(row=0, column=0, sticky="nsew")
canvasFrame = tk.Frame(photoCanvas, bg="#EBEBEB")
photoCanvas.create_window(0, 0, window=canvasFrame, anchor='nw')
for i in range(10):
element = tk.Button(canvasFrame, text='Button %i' % i, borderwidth=0, bg="#EBEBEB")
element.grid(padx=5, pady=5, sticky="nsew")
photoScroll = tk.Scrollbar(photoFrame, orient=tk.VERTICAL)
photoScroll.config(command=photoCanvas.yview)
photoCanvas.config(yscrollcommand=photoScroll.set)
photoScroll.grid(row=0, column=1, sticky="ns")
canvasFrame.bind("<Configure>", update_scrollregion)
root.mainloop()

Is it possible to make 'dynamically' adjustable widgets in Tkinter/ttk

I'm developing very simple GUI for my DB. It shows record's list/tree in DB on left panel and (if user clicks on some record) shows the record on the right panel.
Here some bit of code which creates GUI
from Tkinter import *
import ttk
master = Tk()
reclist = ttk.Treeview(columns=["TIME STAMP","HASH","MESSAGE"])
ysb = ttk.Scrollbar(orient=VERTICAL, command= reclist.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= reclist.xview)
reclist['yscroll'] = ysb.set
reclist['xscroll'] = xsb.set
reclist.grid(in_=master, row=0, column=0, sticky=NSEW)
ysb.grid(in_=master, row=0, column=1, sticky=NS)
xsb.grid(in_=master, row=1, column=0, sticky=EW)
Comment = Text(master)
Comment.tag_configure("center", justify='center')
ysc = ttk.Scrollbar(orient=VERTICAL, command= Comment.yview)
xsc = ttk.Scrollbar(orient=HORIZONTAL, command= Comment.xview)
Comment.grid(in_=master,row=0,column=2,sticky=W+E+N+S)#, columnspan=5)
ysc.grid(in_=master, row=0, column=3, sticky=NS)
xsc.grid(in_=master, row=1, column=2, sticky=EW)
master.rowconfigure(0, weight=3)
master.columnconfigure(0, weight=3)
master.columnconfigure(2, weight=3)
master.mainloop()
Everything works pretty well, except that two panels are not adjustable. I cannot move border between them to make list of records or record panel bigger or smaller. I'm pretty sure in is possible (for example in gitk you can move the border between the list of commits and a displaied commit). I've search quite a lot with no luck.
What you are looking for is called a "PanedWindow". Both the tkinter and ttk modules have one, and they work almost identically. The general idea is that you create a PanedWindow instance, and then you add two or more widgets to it. The PanedWindow will add a movable slider between each widget. Typically you would use frames, which you can then fill up with other widgets.
Here is an example using the one in Tkinter:
import Tkinter as tk
root = tk.Tk()
pw = tk.PanedWindow()
pw.pack(fill="both", expand=True)
f1 = tk.Frame(width=200, height=200, background="bisque")
f2 = tk.Frame(width=200, height=200, background="pink")
pw.add(f1)
pw.add(f2)
# adding some widgets to the left...
text = tk.Text(f1, height=20, width=20, wrap="none")
ysb = tk.Scrollbar(f1, orient="vertical", command=text.yview)
xsb = tk.Scrollbar(f1, orient="horizontal", command=text.xview)
text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
f1.grid_rowconfigure(0, weight=1)
f1.grid_columnconfigure(0, weight=1)
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
text.grid(row=0, column=0, sticky="nsew")
# and to the right...
b1 = tk.Button(f2, text="Click me!")
s1 = tk.Scale(f2, from_=1, to=20, orient="horizontal")
b1.pack(side="top", fill="x")
s1.pack(side="top", fill="x")
root.mainloop()

Categories