Tkinter canvas & scrollbar with grid - python

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()

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

Orient and Organize Tkinter frames

I would like to know how to create frames that align to grid and fill space I dictate. I am having a hard time grasping frame orientation within a window. I can make all the frames, but if I pack, they are all on left or in a line/on right. I played with that and decided on grid(). I set the window geometry and then use grid for frames. However, my frames all collapse down to the top left or my initial frame fills the whole space and the other two squeeze in around the edges. I tried using window geometry of 1024 and 768, then allotting width and height for my first frame to be 512 and 384. Then setting the next two to have width 512 and height of 192. I also saw in other articles that weight is important and tried iterations of code to alter that, but was unsuccessful. Rowspan and columnspan helped me get the background colors to appear so that I could visualize the space being occupied by each.
Anyway, code is below:
class twinsplay:
def __init__(self):
#Window
self.window = Tk()
self.window.geometry('1024x768')
self.window.grid_rowconfigure(1, weight=1)
self.window.grid_columnconfigure(1, weight=1)
#window title
self.window.title('Live Feed')
#frames and labels
self.frame_left = Frame(self.window, background='magenta')
self.frame_left.grid(row=0, column=0, padx=5, pady=5, rowspan=3, columnspan=3, sticky=NSEW)
self.label_left = Label(self.frame_left, text = 'Weather Data', justify=LEFT)
self.label_left.grid(row=0,column=0, sticky=NSEW)
self.frame_tr = Frame(self.window, background='green')
self.frame_tr.grid(row=0, column=3, padx=5, pady=5, rowspan=3, columnspan=2, sticky=NSEW)
self.label_tr = Label(self.frame_tr, text = 'Solar Data', justify = RIGHT)
self.label_tr.grid(row=0, column=512, sticky=NSEW)
self.frame_br = Frame(self.window, background='yellow')
self.frame_br.grid(padx=5, pady=5, rowspan=2, columnspan=2, sticky=NSEW)
self.label_br = Label(self.frame_br, text = 'News', justify = RIGHT)
self.label_br.grid(row=0, column=512, sticky=NSEW)
#main
self.window.mainloop()
```
I don't know if I understand your problem.
Your frames use full size inside window but elements inside frames are collapsed. You may need to use grid_rowconfigure/grid_columnconfigure also with frame_left, frame_tr, frame_br to resize elements in grid inside frames.
I move elements to rows an columns 0 because empty rows/columns have no size and there is no sense to put in column 512
from tkinter import *
class twinsplay:
def __init__(self):
self.window = Tk()
self.window.geometry('1024x768')
self.window.grid_rowconfigure(0, weight=1)
self.window.grid_columnconfigure(0, weight=1)
#window title
self.window.title('Live Feed')
#frames and labels
self.frame_left = Frame(self.window, background='magenta')
self.frame_left.grid(row=0, column=0, padx=5, pady=5, sticky=NSEW)
self.label_left = Label(self.frame_left, text = 'Weather Data', justify=LEFT, bg='red')
self.label_left.grid(row=0,column=0, sticky=NSEW)
self.frame_left.grid_rowconfigure(0, weight=1)
self.frame_left.grid_columnconfigure(0, weight=1)
# ---
self.frame_tr = Frame(self.window, background='green')
self.frame_tr.grid(row=0, column=1, padx=5, pady=5, sticky=NSEW)
self.label_tr = Label(self.frame_tr, text = 'Solar Data', justify = RIGHT, bg='green')
self.label_tr.grid(row=0, column=0, sticky=NSEW)
self.frame_tr.grid_rowconfigure(0, weight=1)
self.frame_tr.grid_columnconfigure(0, weight=1)
# ---
self.frame_br = Frame(self.window, background='yellow')
self.frame_br.grid(row=1, column=0, padx=5, pady=5, columnspan=2, sticky=NSEW)
self.label_br = Label(self.frame_br, text = 'News', justify = RIGHT, bg='yellow')
self.label_br.grid(row=0, column=0, sticky=NSEW)
self.frame_br.grid_rowconfigure(0, weight=1)
self.frame_br.grid_columnconfigure(0, weight=1)
#main
self.window.mainloop()
twinsplay()
But if you try to do something different then maybe you have to use grid_rowconfigure/grid_columnconfigure with other rows/columns - you can use it many times with different rows/columns.
EDIT:
from tkinter import *
class twinsplay:
def __init__(self):
self.window = Tk()
self.window.geometry('1024x768')
# left and right column will use the same size
self.window.grid_columnconfigure(0, weight=1)
self.window.grid_columnconfigure(1, weight=1)
# top and bottom rows will use the same size
self.window.grid_rowconfigure(0, weight=1)
self.window.grid_rowconfigure(1, weight=1)
#window title
self.window.title('Live Feed')
#frames and labels
# frame will use 2 rows
self.frame_left = Frame(self.window, background='magenta')
self.frame_left.grid(row=0, column=0, padx=5, pady=5, sticky=NSEW, rowspan=2)
self.label_left = Label(self.frame_left, text = 'Weather Data', justify=LEFT, bg='red')
self.label_left.grid(row=0,column=0, sticky=NSEW)
self.frame_left.grid_rowconfigure(0, weight=1)
self.frame_left.grid_columnconfigure(0, weight=1)
# ---
self.frame_tr = Frame(self.window, background='green')
self.frame_tr.grid(row=0, column=1, padx=5, pady=5, sticky=NSEW)
self.label_tr = Label(self.frame_tr, text = 'Solar Data', justify = RIGHT, bg='green')
self.label_tr.grid(row=0, column=0, sticky=NSEW)
self.frame_tr.grid_rowconfigure(0, weight=1)
self.frame_tr.grid_columnconfigure(0, weight=1)
# ---
self.frame_br = Frame(self.window, background='yellow')
self.frame_br.grid(row=1, column=1, padx=5, pady=5, columnspan=2, sticky=NSEW)
self.label_br = Label(self.frame_br, text = 'News', justify = RIGHT, bg='yellow')
self.label_br.grid(row=0, column=0, sticky=NSEW)
self.frame_br.grid_rowconfigure(0, weight=1)
self.frame_br.grid_columnconfigure(0, weight=1)
#main
self.window.mainloop()
twinsplay()

trying to load image in TKinter but get white space where it should be any ideas?

i only get one picture at the bottom but theres supposed to be 10 all in a vertical tower any idea? also was wondering if the tkinter scrollbar command could have images inside it if not is there any other way to have a scrollbar for lables?
def show_data(self):
print('------------------------------------------------------------------------')
print('data OK')
for i in range(10):
self.image = Image.open(self.data[i][7] + '.jpg')
self.photo = ImageTk.PhotoImage(self.image)
#result0 = Label(self.frame, text=self.data[i][0])
#result0.grid(row=i+3, column=1, sticky=W)
#result1 = Label(self.frame, text=self.data[i][1])
#result1.grid(row=i+3, column=2, sticky=W)
#result2 = Label(self.frame, text=self.data[i][2])
#result2.grid(row=i+3, column=3, sticky=W)
#result3 = Label(self.frame, text=self.data[i][3])
#result3.grid(row=i+3, column=4, sticky=W)
#result4 = Label(self.frame, text=self.data[i][4])
#result4.grid(row=i+3, column=5, sticky=W)
#result5 = Label(self.frame, text=self.data[i][5])
#result5.grid(row=i+3, column=6, sticky=W)
#result6 = Label(self.frame, text=self.data[i][6])
#result6.grid(row=i+3, column=7, sticky=W)
result7 = Label(self.frame, image=self.photo)
result7.grid(row=i + 3, column=8, sticky=W)
In order to keep the image used in label from being destroyed, you need to keep a reference to the image:
def show_data(self):
print('------------------------------------------------------------------------')
print('data OK')
for i in range(10):
photo = ImageTk.PhotoImage(file=self.data[i][7]+'.jpg')
result7 = Label(self.frame, image=photo)
result7.image = photo # keep a reference of the image
result7.grid(row=i + 3, column=8, sticky=W)
To have a scrollbar for the labels inside a frame, the most common way is to put the frame inside a canvas and then create a scrollbar to scroll the view region of the canvas:
# create a canvas and put it as the left side of window
canvas = Canvas(self.master, width=200, height=600) # assume self.master is Tk()
canvas.pack(side='left')
# create an vertical scrollbar and put it at the right side of window
scrollbar = Scrollbar(self.master, orient='vertical', command=canvas.yview)
scrollbar.pack(side='right', fill='y')
# configure the scrollbar to scroll the canvas in vertical direction
canvas.configure(yscrollcommand=scrollbar.set)
# create the frame as child of canvas to hold the labels
self.frame = Frame(canvas)
canvas.create_window((0,0), window=self.frame, anchor='nw')
# make sure to update the scrolling region if the frame is resized
self.frame.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox('all')))

Tkinter Scrolling Issues

I am trying to put a scrollbar on my listbox but because the items I am inserting are so long, but the scrollbar does not scroll all the way. If you run the code below, you will see what I am talking about and notice that the horizontal scroll bar can not scroll all the way to the right. Does anyone know how I can fix this? Same problem occurs for the vertical scroll bar when you add a few more entries.
import tkinter
lista=[11111111111111111111111111111111111111111111111,22222222222222222222222222222222222222222222222222,
33333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444467,4444444444444444,
5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556]
master = tkinter.Tk()
master.geometry("400x250")
frame = tkinter.Frame(master, borderwidth=1, highlightthickness=1,
highlightbackground="black", highlightcolor="black")
frame.place(bordermode=tkinter.INSIDE, height=240, width=300, y=0, x=30)
LIST = tkinter.Listbox(frame)
LIST.place(bordermode=tkinter.INSIDE, height=237, width=296)
Scroll_Bar_x = tkinter.Scrollbar(frame, orient=tkinter.HORIZONTAL)
Scroll_Bar_x.config(command=LIST.xview)
Scroll_Bar_x.pack(fill=tkinter.X, side=tkinter.BOTTOM)
LIST.config(xscrollcommand=Scroll_Bar_x.set)
Scroll_Bar_y = tkinter.Scrollbar(frame, orient=tkinter.VERTICAL)
Scroll_Bar_y.config(command=LIST.yview)
Scroll_Bar_y.pack(fill=tkinter.Y, side=tkinter.RIGHT)
LIST.config(yscrollcommand=Scroll_Bar_y.set)
for x in lista:
LIST.insert(0, x)
master.mainloop()
You are having issues with overlapping your scroll bars on the placed listbox. Most of the time place() is not what you want to use.
Instead try grid() this will help keep everything in order and sized correctly.
import tkinter
lista=[11111111111111111111111111111111111111111111111,22222222222222222222222222222222222222222222222222,
33333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444467,4444444444444444,
5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556]
master = tkinter.Tk()
master.geometry("400x250")
frame = tkinter.Frame(master, height=240, width=300, borderwidth=1, highlightthickness=1, highlightbackground="black", highlightcolor="black")
frame.grid(row=0, column=0)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
LIST = tkinter.Listbox(frame)
LIST.grid(row=0, column=0, sticky="nsew")
frame.grid_propagate(False)
Scroll_Bar_x = tkinter.Scrollbar(frame, orient=tkinter.HORIZONTAL)
Scroll_Bar_x.config(command=LIST.xview)
Scroll_Bar_x.grid(row=1, column=0, sticky="ew")
LIST.config(xscrollcommand=Scroll_Bar_x.set)
Scroll_Bar_y = tkinter.Scrollbar(frame, orient=tkinter.VERTICAL)
Scroll_Bar_y.config(command=LIST.yview)
Scroll_Bar_y.grid(row=0, column=1, sticky="ns")
LIST.config(yscrollcommand=Scroll_Bar_y.set)
for x in lista:
LIST.insert(0, x)
master.mainloop()
Results:

Categories