Unwanted border appearing when scrollable canvas added - python

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!

Related

How do I change this code to add a horizontal scrollbar to a Frame? (tkinter)

I am trying to implement a scrollbar to a Frame object I have, so if there are too many widgets inside it, I can scroll down to see more of them. I found this code and copied it into my program to use:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
container = ttk.Frame(root)
canvas = tk.Canvas(container)
scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.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):
ttk.Label(scrollable_frame, text="Sample scrolling label").pack()
container.pack()
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
root.mainloop()
It works, but I changed the code to add a horizontal scrollbar as well. I tried adding the same code with some changes that I thought would make it work, like this:
super().__init__(container, *args, **kwargs)
canvas = Canvas(self, width=1000, height=700)
yscrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
xscrollbar = ttk.Scrollbar(self, orient="horizontal", command=canvas.xview)
self.scrollable_frame = ttk.Frame(canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=yscrollbar.set)
canvas.configure(xscrollcommand=xscrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
yscrollbar.pack(side="right", fill="y")
xscrollbar.pack(side="left", fill="x")
However, it didn't work like I expected, but instead added a tiny scrollbar to the side that doesn't do anything:
[![enter image description here][1]][1]
How should've I changed the code to make a working horizontal scrollbar?
[1]: https://i.stack.imgur.com/xp1ju.png
The order of packing those widgets matters and also side="bottom" should be used instead for xscrollbar:
xscrollbar.pack(side="bottom", fill="x")
canvas.pack(side="left", fill="both", expand=True)
yscrollbar.pack(side="right", fill="y")

Positioning the scrolbar in tk using Python

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

make scrollbar longer in tkinter

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

How get a ScrollBar to work on Only Left Frame Using Canvas?

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.

Tkinter, make scrollbar stick to canvas and not root?

I've got a working scrollbar and a canvas. Problem is that my canvas height and maybe even width are not the same dimensions as the root, but the scrollbar is attached to the root. Which looks odd. How can I make it stick to the canvas? So that I also may add an additional canvas with a scrollbar in the future.
self.canvas = Canvas(root, borderwidth=0, background="#c0c0c0",height= 150, width=500)
self.frameTwo = Frame(self.canvas, background="#ffffff")
self.vsb = Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left")
self.canvas.place(y=195)
self.canvas.create_window((4,4), window=self.frameTwo, anchor="w",
tags="self.frame")
self.frameTwo.bind("<Configure>", self.onFrameConfigure)
The best solution is to put your scrollbars and canvas in a frame specifically for that purpose.
Here's a complete working example:
import tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# create a frame specifically for the canvas,
# and scrollbars, and let it control the border
self.canvas_frame = tk.Frame(self, borderwidth = 1, relief="sunken")
self.canvas = tk.Canvas(self.canvas_frame, borderwidth=0, highlightthickness=0,
background="white")
self.vsb = tk.Scrollbar(self.canvas_frame, orient="vertical",
command=self.canvas.yview)
self.hsb = tk.Scrollbar(self.canvas_frame, orient="horizontal",
command=self.canvas.xview)
self.canvas.configure(yscrollcommand=self.vsb.set,
xscrollcommand=self.hsb.set)
self.canvas.grid(row=0, column=0, sticky="nsew")
self.vsb.grid(row=0, column=1, sticky="ns")
self.hsb.grid(row=1, column=0, sticky="ew")
self.canvas_frame.rowconfigure(0, weight=1)
self.canvas_frame.columnconfigure(0, weight=1)
# add the canvas+scrollbars to the window
self.canvas_frame.pack(fill="both", expand=True)
if __name__== "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
By the way, in your code you're calling both pack and place on the canvas. It's pointless to call both -- only one or the other can affect the placement of the widget. When you call place after calling pack for the same widget, the effects of calling place will be completely forgotten.

Categories

Resources