i have made a scrollbar and it works perfect but the size of the scrollbar doesn't match the size of the canvas. i tryed removing the scrollbar.place but it puts the scrollbar in the upper left corner and reallt short. my question is how do i change the length of the scrollbar. thank you very much.
the code:
canvas = tkinter.Canvas(self.main_window, width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
canvas.grid()
canvas.place(relx=0.5, rely=0.2, anchor=tkinter.N)
scrollbar = tkinter.Scrollbar(self.main_window, orient=tkinter.VERTICAL, command=canvas.yview)
scrollbar.grid(row=3, column=1, sticky=tkinter.N + tkinter.S + tkinter.E)
scrollbar.place(relx=1, rely=0.5, anchor=tkinter.E)
canvas.configure(yscrollcommand=scrollbar.set, scrollregion=canvas.bbox("all"))
frame = tkinter.Frame(canvas)
canvas.create_window((0,0), window=frame, anchor=tkinter.NW)
frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all")))
After making the following changes:
remove canvas.place(...) and scrollbar.place(...)
put canvas at the same row (3) of scrollbar
set the width and height of frame
Then the height of scrollbar will be the same of canvas.
canvas = tkinter.Canvas(self.main_window, width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
canvas.grid(row=3, column=0, sticky='nsew') # put in same row of scrollbar
#canvas.place(relx=0.5, rely=0.2, anchor=tkinter.N)
scrollbar = tkinter.Scrollbar(self.main_window, orient=tkinter.VERTICAL, command=canvas.yview)
scrollbar.grid(row=3, column=1, sticky=tkinter.N + tkinter.S + tkinter.E)
#scrollbar.place(relx=1, rely=0.5, anchor=tkinter.E)
canvas.configure(yscrollcommand=scrollbar.set)
# give frame a size longer then the canvas to enable scrolling
frame = tkinter.Frame(canvas, width=CANVAS_WIDTH, height=CANVAS_HEIGHT*2)
canvas.create_window((0,0), window=frame, anchor=tkinter.NW)
frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all")))
Related
As you can see in the picture I want the canvas scrollbar to go all the way right... In a few words I want the canvas to cover all the red bg that the frame 2 has... Any ideas? Here is the code: (reference to the code bellow about the scrollbar code you will find here)
upFrame = Frame(window, bg='lightgray')
upFrame.grid(row=1, column=0, sticky='nesw')
midFrame = Frame(window, bg='red')
midFrame.grid(row=2, column=0, sticky='nesw')
bottomFrame = Frame(window, bg='lightgray')
bottomFrame.grid(row=3, column=0, sticky='nesw')
window.grid_rowconfigure(1, weight = 0)
window.grid_columnconfigure(0, weight = 1)
window.grid_rowconfigure(1, weight = 0)
container = Frame(midFrame)
canvas = Canvas(container)
scrollbar = Scrollbar(container, orient="vertical", command=canvas.yview)
scrollable_frame = Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
for i in range(50):
Label(scrollable_frame, text="Sample scrolling label").pack()
container.grid(row=2, column=0, sticky='nesw')
canvas.pack(side='left', fill='both', expand=True)
scrollbar.pack(side="right", fill="y")
Also It would be helpfull if you guys could tell me how to make the scrollbar move with the mouse wheel
Next time please add a complete reproducible example including the imports and mainloop so it is easier to follow.
Make your canvas scrollable
(source: tkinter: binding mousewheel to scrollbar):
# bind scrolling to mousewheel
def _scroll_canvas(event):
canvas.yview_scroll(-1*(int(event.delta/100)), "units")
canvas.bind_all("<MouseWheel>", _scroll_canvas)
Stick your scrollbar on the right side
You dont need another frame just for the Canvas, i removed your container Frame and put the Canvas directly into your midFrame. The red background can be removed also.
Same goes for your scrollbar - put directly into your midFrame
For the red portion i created a new sub-frame called red_frame (at the bottom)
If you want the scrollbar to appear on the right side of the screen, use pack with the "right" option, and for all other sub-frames use the "left" option. Everything else is managed by the order in which you pack the widgets.
Full code:
from tkinter import Frame, Canvas, Scrollbar, Tk, Label
window = Tk()
upFrame = Frame(window, bg='lightgray')
upFrame.grid(row=1, column=0, sticky='nesw')
midFrame = Frame(window, bg='red')
midFrame.grid(row=2, column=0, sticky='nesw')
bottomFrame = Frame(window, bg='lightgray')
bottomFrame.grid(row=3, column=0, sticky='nesw')
window.grid_rowconfigure(1, weight = 0)
window.grid_columnconfigure(0, weight = 1)
window.grid_rowconfigure(1, weight = 0)
canvas = Canvas(midFrame)
scrollbar = Scrollbar(midFrame, orient="vertical", command=canvas.yview)
scrollable_frame = Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
# bind scrolling to mousewheel
def _scroll_canvas(event):
canvas.yview_scroll(-1*(int(event.delta/100)), "units")
canvas.bind_all("<MouseWheel>", _scroll_canvas)
for i in range(50):
Label(scrollable_frame, text="Sample scrolling label").pack()
# here you decide in which order your widgets appear: canvas, red frame and on the right - your scrollbar
canvas.pack(side='left', fill='both')
red_frame = Frame(midFrame, bg="red")
red_frame.pack(side="left")
scrollbar.pack(side="right", fill="y")
window.mainloop()
I am using the following code to populate Entry widget on tkinter Frame:
import tkinter as tk
def populate(frame):
'''Put in some fake data'''
for row in range(100):
tk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0)
tk.Entry(frame, width = 50).grid(row=row, column=1)
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
root = tk.Tk()
canvas = tk.Canvas(root, borderwidth=0)
frame = tk.Frame(canvas)
vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((0,0), window=frame, anchor="nw")
frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
populate(frame)
root.mainloop()
Issue which I am facing in the above code is that when I am resizing the tkinter main window the Entry widget is not resizing itself automatically according to change in window size.
Can someone please help me out in solving this.
If you want to resize the entries when the tkinter window is resized, you need to:
resize frame when canvas is resized
def on_canvas_resized(event):
canvas.itemconfig('frame', width=event.width)
...
canvas.bind('<Configure>', on_canvas_resized)
...
canvas.create_window((0,0), window=frame, anchor="nw", tag='frame') # added tag
make Entry to fill the available space horizontally
def populate(frame):
'''Put in some fake data'''
for row in range(100):
tk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0)
tk.Entry(frame, width = 50).grid(row=row, column=1, sticky='ew') # added sticky
...
frame.columnconfigure(1, weight=1) # make column 1 to fill available space horizontally
I've coded a scrollbar into my app which works like a treat. The only issue is that there is an annoying black frame which apperas around the edges (see below):
The frame I'm talking about is to the right of the scroll bar and above the "Ply builder" title.
My scrollbar code is:
"""Scroll bar"""
canvas = Canvas(self, borderwidth=0)
scrollable_frame = Frame(canvas)
vsb = Scrollbar(self, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, height=725)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
Not sure if the border is controlled by the Canvas, Frame or window element. I also don't know how to get rid of it.
As #acw1668 said in the comments,
Add highlightthickness in Canvas(...)
That is the most reasonable solution, and it can be achieved like so:
canvas = Canvas(self, borderwidth=0, highlightthickness = 0)
scrollable_frame = Frame(canvas)
vsb = Scrollbar(self, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set, width=450, height=725)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
Read more about the highlightthickness parameter.
The width of the highlight border. The default is system specific (usually one or two pixels). (highlightThickness/HighlightThickness)
Hope this helps!
I am trying to get the ScrollBar to apply only to the LeftFrame.
When I use the Canvas setup the ScrollBar applies to the whole window.
Is there better way to setup a selectable long-list table?
Should I be using a listbox instead?
import tkinter as tk
root = tk.Tk()
root.geometry("1000x600")
# root.resizable(width=False, height=False)
def printname(event, i):
print ("my name is", i)
namelabel.configure(text="test" + str(i))
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
leftFrame = tk.Frame(canvas, background="#ffffff", width=700, height=500)
vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="left", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((1,1), window=leftFrame, anchor="ne")
rightFrame = tk.Frame(root, background="#ffffff", width=300)
# vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
# canvas.configure(yscrollcommand=vsb.set)
# vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((1,2), window=rightFrame, anchor="nw")
# rightFrame = tk.Frame(root, width=400)
# rightFrame.pack()
leftFrame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
textlabel = tk.Label(leftFrame, text="Player List")
textlabel.grid(row=0)
for i in range(50):
if (i % 2):
w = tk.Text(leftFrame, height=1, fg="white", bg="black")
else:
w = tk.Text(leftFrame, height=1, fg="black", bg="white")
w.insert(1.0, "test" + str(i))
w.configure(relief="flat")
w.configure(state="disabled")
w.bind("<Button-1>", lambda event, a=i: printname(event, a))
w.grid(row=7+i)
namelabel = tk.Label(rightFrame, text="Test", fg="white", bg="black")
namelabel.grid(row=1)
root.mainloop()
The short answer is that if you don't want something to scroll, don't put it in the canvas. Scrolling a canvas means every item added to the canvas will scroll. There's no way to avoid that. You can't scroll half of a widget.
Is there better way to setup a selectable long-list table?
Probably, though it depends on the data you're wanting to show. If it's a list of short strings, a Listbox is probably a better choice. If it's blocks of text, a single text widget with some tags and custom bindings might be better than multiple widgets.
I have a canvas with an image I'd like to use for a background. My problem is that when I position the button to the correct place, and I try to scroll down, the button moves with the screen instead of staying where I want it to on the canvas.
frame = Frame(self)
frame.pack()
mapImg = PhotoImage(file='Fo4-pip-map.png')
canvas = Canvas(frame, width=2048, height=2048, scrollregion=(0,0,2048,2048))
canvas.create_image(0,0, image=mapImg, anchor='nw')
canvas.image = mapImg
xscrollbar = Scrollbar(frame, orient=HORIZONTAL)
xscrollbar.pack(side=BOTTOM, fill=X, anchor='s')
yscrollbar = Scrollbar(frame, orient=VERTICAL)
yscrollbar.pack(side=RIGHT, fill=Y, anchor='e')
xscrollbar.config(command=canvas.xview)
yscrollbar.config(command=canvas.yview)
canvas.config(xscrollcommand=xscrollbar.set, yscrollcommand=yscrollbar.set)
canvas.pack(side=LEFT, expand=True, fill=BOTH)
vaultImg = PhotoImage(file='Vault.png')
vaultImg = vaultImg.zoom(5)
vaultImg = vaultImg.subsample(32)
vault111Button = Button(canvas, width=30, height=30, borderwidth=0, image=vaultImg,
command=lambda: controller.show_frame('Vault111'))
vault111Button.image = vaultImg
vault111Button.place(x=150, y=100)
The place geometry manager places children at a fixed offset from their parent - in particular, the scrolling of a canvas doesn't apply to them. Get rid of that last line, and instead use:
canvas.create_window(150, 100, window=vault111Button)