Scrollbar in canvas - python

I want to put a scrollbar in a canvas inside a Frame with tkinter. I can see my scrollbar but she don't work. I don't anderstant why, here is my code.
labelBlesser = tk.Label(self.fen, text='Rajouter', font=fontLabel)
labelBlesser.configure(background='white')
labelBlesser.place(relx=0.7, rely=0.40, anchor='center')
frameBlesser = tk.Frame(self.fen)
vbar = tk.Scrollbar(frameBlesser, orient='vertical')
vbar.pack(side='right', fill='y')
canvasBlesser = tk.Canvas(frameBlesser)
vbar.config(command=canvasBlesser.yview)
frameBlesser.place(relx=0.7, rely=0.65, anchor='center')
for _ in range(3):
self.listeBlesser.append(tk.Label(canvasBlesser, text='Blesse {}:'.format(self.nombreBlesserAjouter + 1)))
self.listeBlesser.append(tk.Entry(canvasBlesser, font=fontEntry))
self.refreshListeBlesser()
canvasBlesser.pack()
def refreshListeBlesser(self):
for i, lab in enumerate(self.listeBlesser):
lab.place(x=10, y=i*70+13)
thank you for your help :)

You need to bind the canvas and the scrollbar together. Also, you need to create a frame inside of the canvas using canvasBlesser.create_window so that you can place stuff inside the frame as usual.
frameBlesser = tk.Frame(self.fen)
vbar = tk.Scrollbar(frameBlesser, orient='vertical')
vbar.pack(side='right', fill='y')
canvasBlesser = tk.Canvas(frameBlesser)
# adding the side and fill for packing canvas in the frame
canvasBlesser.pack(side='left', fill='both')
# connecting the canvas and scrollbar together
vbar.config(command=canvasBlesser.yview)
canvasBlesser.configure(yscrollcommand=vbar.set)
# placing a frame inside the canvas as a parent to other widgets in it
inner_frame = tk.Frame(canvasBlesser)
canvasBlesser.create_window((0,0), window=inner_frame, anchor="nw")
Use this inner_frame as a parent to place the widgets just as usual to be seen in the canvas.
The line canvasBlesser.configure(yscrollcommand=vbar.set) makes it so that whenever the view of the canvas is changed, it updates the scrollbar's value.

Related

Python tkinter window resizes, but widgets never change or move

Python beginner. I placed a scrollbar widget in window and that works, but no matter what I do I can't get the scrollbox widget to change size. Could go with a larger scrollbox or for it to resize when the window resizes, but can't figure out how to force either to happen. Tried lots of different solutions, but feels like the grid and canvas are defaulting to a size and can't figure out how to change that. Help would be appreciated. Code is below:
import tkinter as tk
from tkinter import ttk
import os
import subprocess
class Scrollable(tk.Frame):
"""
Make a frame scrollable with scrollbar on the right.
After adding or removing widgets to the scrollable frame,
call the update() method to refresh the scrollable area.
"""
def __init__(self, frame, width=16):
scrollbar = tk.Scrollbar(frame, width=width)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=True)
self.canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.canvas.yview)
self.canvas.bind('<Configure>', self.__fill_canvas)
# base class initialization
tk.Frame.__init__(self, frame)
# assign this obj (the inner frame) to the windows item of the canvas
self.windows_item = self.canvas.create_window(0,0, window=self, anchor=tk.NW)
def __fill_canvas(self, event):
"Enlarge the windows item to the canvas width"
canvas_width = event.width
self.canvas.itemconfig(self.windows_item, width = canvas_width)
def update(self):
"Update the canvas and the scrollregion"
self.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))
root = tk.Tk()
root.title("application")
root.geometry('750x800')
dbEnvs = ['a','b']
x = 1
header = ttk.Frame(root)
body = ttk.Frame(root)
footer = ttk.Frame(root)
header.pack(side = "top")
body.pack()
footer.pack(side = "top")
#setup Environment selection
envLabel = tk.Label(header, text="Environment:")
envLabel.grid(row=0,column=0,sticky='nw')
dbselection = tk.StringVar()
scrollable_body = Scrollable(body, width=20)
x = 1
for row in range(50):
checkboxVar = tk.IntVar()
checkbox = ttk.Checkbutton(scrollable_body, text=row, variable=checkboxVar)
checkbox.var = checkboxVar # SAVE VARIABLE
checkbox.grid(row=x, column=1, sticky='w')
x += 1
scrollable_body.update()
#setup buttons on the bottom
pullBtn = tk.Button(footer, text='Pull')
pullBtn.grid(row=x, column=2, sticky='ew')
buildBtn = tk.Button(footer, text='Build')
buildBtn.grid(row=x, column=3, sticky='ew')
compBtn = tk.Button(footer, text='Compare')
compBtn.grid(row=x, column=4, sticky='ew')
root.mainloop()
have tried anchoring, changing the window base size and multiple other things (8 or 19 different items, plus reading lots of posts), but they normally involve packing and since I used grids that normally and ends with more frustration and nothing changed.
If you want the whole scrollbox to expand to fill the body frame, you must instruct pack to do that using the expand and fill options:
body.pack(side="top", fill="both", expand=True)
Another problem is that you're setting expand to True for the scrollbar. That's probably not something you want to do since it means the skinny scrollbar will be allocated more space than is needed. So, remove that attribute or set it to False.
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=False)
tip: when debugging layout problems, the problems are easier to visualize when you temporarily give each widget a unique color. For example, set the canvas to one color, body to another, the instance of Scrollable to another, etc. This will let you see which parts are visible, which are growing or shrinking, which are inside the borders of others, etc.

how to add scrollbar to a frame that is set to pack_propagate(False)

i want to make a chatroom GUI where the user's messages will show up on the right side, and other users' messages will show up on the left side.
in order to do so without having the frame on the canvas shrink to the size of the labels, i have to put frame.pack_propagate(False)
however, doing so disables my scrollbar. from my understanding it is happening because the frame is now 'locked' and any widgets packed into it will simply 'overflow' out of the frame's view.
the mainly important code is as follows:
# main window
root = tk.Tk()
root.title("Chatroom")
root.config(bg="red")
root.geometry("1200x800")
root.resizable(False, False)
chat_canvas = tk.Canvas(root, bg="gray30", height=600, width=1200)
scrollbar = tk.Scrollbar(root, orient='vertical', command=chat_canvas.yview)
chat_canvas.configure(yscrollcommand=scrollbar .set)
msg_frame = tk.Frame(chat_canvas, bg="blue", height=600, width=1200)
msg_frame.pack_propagate(False) #<-- this is a must for me as i want the messages to show up
# on the right side of the frame. without it the frame will
# shrink to the size of the label and stick it to the left
# however without it the scrollbar works rather fine(?)
chat_canvas.pack(fill='both', side='left', expand=True)
scrollbar .pack(side='right', fill='y')
chat_canvas.create_window((0, 0), window=msg_frame, anchor='nw')
msg_frame.bind('<Configure>', func=lambda ev: chat_canvas.configure(scrollregion=chat_canvas.bbox('all')))
when i want to print a message into the frame, the following function executes:
def print_message(msg):
msg_label = tk.Label(msg_frame, text=msg, bg="gray30",state='disabled')
msg_label.pack(anchor='ne', padx=10)
the GUI looks like this without any option to scroll (frame is blue, canvas has white borders):
what am i missing?

Tkinter treeview widgets not properly aligned/added space between widgets

I am working on this table in tkinter made from a bunch of treeveiw widgets. The idea is to get a table where I can add lines, select lines and edit them. In the code below you can add lines to the table by pushing the button. I now want to control the height of each row by configuring the style. However, when I use style the alignment of the treeview widgets is messed up, see attached picture. Any suggestions how to fix this?
EDIT: The problem is the added space between the widgets.
The code for the table is:
from tkinter import *
from tkinter import ttk
class MyApp(Tk):
def __init__(self):
super(MyApp, self).__init__()
self.geometry('950x500+100+100')
self.NewTree = []
label = Label(self,text='Table with some data', font=("Arial Bold", 25))
label.pack()
self.addLine()
master_frame = Frame(self, bd=3, relief=RIDGE)
master_frame.pack(side=BOTTOM)
# Create a frame for the canvas and scrollbar(s).
frame2 = Frame(master_frame)
frame2.pack(side = BOTTOM)
# Add a canvas in that frame.
self.canvas = Canvas(frame2)
self.canvas.grid(row=0, column=0)
# Create a vertical scrollbar linked to the canvas.
vsbar = Scrollbar(frame2, orient=VERTICAL, command=self.canvas.yview)
vsbar.grid(row=0, column=1, sticky=NS)
self.canvas.configure(yscrollcommand=vsbar.set)
# Create a frame on the canvas to contain the buttons.
self.table_frame = Frame(self.canvas)
# Create canvas window to hold the buttons_frame.
self.canvas.create_window((0,0), window=self.table_frame, anchor=NW)
def addLine(self):
#Make button for adding step
bt = Button(self,text='Add Line',command=lambda: self.addLineMethod())
bt.config(width=9, height=1)
bt.pack()
def addLineMethod(self):
lineNumber = int(len(self.NewTree)/5)
for index in range(5):
s = ttk.Style()
s.configure('MyStyle.Treeview', rowheight=25)
self.NewTree.append(ttk.Treeview(self.table_frame, height=1,show="tree",columns=("0"),style='MyStyle.Treeview'))
#Works fine when using this line instead of those above
#self.NewTree.append(ttk.Treeview(self.table_frame, height=1,show="tree",columns=("0")))
self.NewTree[index+5*lineNumber].grid(row=lineNumber, column=index+1)
self.NewTree[index+5*lineNumber]['show'] = ''
item = str(index+5*lineNumber)
self.NewTree[index+5*lineNumber].column("0", width=180, anchor="w")
self.NewTree[index+5*lineNumber].insert("", "end",item,text=item,values=('"Text text text"'))
self.table_frame.update_idletasks() # Needed to make bbox info available.
bbox = self.canvas.bbox(ALL) # Get bounding box of canvas with Buttons.
# Define the scrollable region as entire canvas with only the desired
# number of rows and columns displayed.
self.canvas.configure(scrollregion=bbox, width=925, height=200)
app = MyApp()
app.mainloop()
Her is a picture of the table with some lines.
Put the style configuration in the __init__() function and the effect will go away. I'm not clear as to why this works.
def __init__(self):
...
s = ttk.Style()
s.configure('MyStyle.Treeview', rowheight=20)

tkinter: dynamically add checkboxes to grid and read values individually

I would like to dynamically add checkboxes to a tkinter grid and then have the user check some of them and afterwards see, which checkboxes were checked.
Here's the code:
top = tkinter.Tk()
var2 = tkinter.IntVar()
main()
top.mainloop()
# in main
for feat in feats:
counter += 1
tkinter.Checkbutton(top, text=str(merkmal) + " ist " +
str(label), command=lambda: testResult(),
variable=var2).grid(row=counter, sticky="W")
# callback
def testResult():
print(var2)
Two problems:
a) I need a vertical and horizontal scrollbar on my grid
I have looked at other posts and seen that I need to add a canvas and then add the scrollbar to the canvas but I havent gotten it to work. Here's my attempt:
w = tkinter.Canvas(top)
scrollbar = tkinter.Scrollbar(top, orient="vertical",command=w.yview)
scrollbar.grid(row=0,column=1,sticky='ns')
w.config(yscrollcommand=scrollbar.set, xscrollcommand=scrollbar.set)
w.grid(row=0,column=0,sticky='nsew')
and then addind the checkboxes to w instead of top. The vertical scrollbar shows, but I cant scroll.
b) When I check one of the boxes, all of them get checked. How can I make it so that each checkbox can be checked individually?
I am new to tkinter and really don't have to learn too much into it, just need this to work somehow.
Thanks for any help!
EDIT
canvas = tkinter.Canvas(top, bg='#FFFF12', width=300, height=900, scrollregion=(0, 0, 900, 900))
hbar = tkinter.Scrollbar(top, orient="horizontal")
hbar.pack(side="bottom", fill="x")
hbar.config(command=canvas.xview)
vbar = tkinter.Scrollbar(top, orient="vertical")
vbar.pack(side="right", fill="y")
vbar.config(command=canvas.yview)
canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
canvas.pack(side="left", expand=True, fill="both")
It shows the scrollbar, the vertical scrollbar moves a bit, but the content doesnt move. When I attach everything to a frame, then the scrollbar doesnt move either.

How to prevent background scrolling in canvas?

I am using ttk.Scrollbar to scroll elements inside a Tkinter.Canvas. Unfortunately a background image of this canvas will also scroll. How can I prevent this?
Here is an example of what I use:
import Tkinter as tk
from ttk import *
# first canvas with scrollbar
root = tk.Tk()
canv1 = tk.Canvas(root, width=200, height=200, bg='green')
sbar = Scrollbar(root, orient="vertical")
sbar.config(command=canv1.yview)
sbar.pack(side="right", fill="y")
canv1.config(yscrollcommand=sbar.set)
canv1.pack(side="right", fill="y")
# background of dirst canvas
photo_content2 = tk.PhotoImage(file = './sample_image.gif')
canv1.create_image(115,300, image=photo_content2)
# scrollable second canvas insode first
canv2 = tk.Canvas(canv1, width=50, height=30, bg='red')
canv2.pack()
canv1.create_window(20,20, window=canv2)
canv1.config(scrollregion=(0,0,300,1000))
root.mainloop()
Scrolling affects everything drawn on the canvas. You can't add a background image that doesn't scroll along with everything else.
That being said, you can create a function that moves the background image after the scrolling happens, and you get the same effect as if the image didn't scroll.
You do this by connecting the scrollbar to a custom function rather than directly to the widgets yview and/or xview commands. In this function you can call the yview and xview commands as normal, and then re-position the background image.
For example, this is how you would handle vertical scrolling; horizontal scrolling would work in a similar way:
def custom_yview(*args, **kwargs):
canvas.yview(*args, **kwargs)
x = canvas.canvasx(0)
y = canvas.canvasy(0)
canvas.coords("background", x,y)
canvas = tk.Canvas(...)
vsb = tk.Scrollbar(orient="vertical", command=custom_yview)
canvas.create_image(..., tags=("background,"))

Categories

Resources