tkinter: dynamically add checkboxes to grid and read values individually - python

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.

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.

Tkinter: How to resize a scroll bar if the canvas changes size?

I am working on a project where I need to display information when a button is clicked, and hide information when that button is clicked again. The information can be several rows and extend beyond the window, so I am trying to add a scroll bar to get around this issue. The problem is that when the information is displayed, the scroll bar does not show. Scrolling is still possible but the actual bar is not there. Resizing the window fixes this issue for some reason. Also when the button is clicked again to hide the information, the size of the canvas and the scrollbar remain the same, so you can scroll far beyond the button into empty space. My theory is that the scroll region is not updating when the widgets in the canvas change size - but I'm not sure how to go about fixing that.
I realize this explanation may be a bit confusing, so I have provided a simplified example below. This example has a single button that when clicked reveals several lines of "other info". The scrollbar and/or canvas does not resize properly upon interacting with this button.
My strategy right now was to have a method called add_scrollbar that removes the current scrollbar and creates a new one every time the widgets change in hopes that the new one would be the right size; however this still is not working.
from tkinter import *
from tkinter import ttk
def add_scrollbar(outer_frame, canvas):
if len(outer_frame.winfo_children()) == 2:
# canvas is at index 0 of the outer frame, if a scrollbar has been added it will be at index 1
outer_frame.winfo_children()[1].destroy()
# my strategy here was to destroy the existing scroll bar
# and create a new one each time the widget changes size
scrollbar = ttk.Scrollbar(outer_frame, orient=VERTICAL, command=canvas.yview)
scrollbar.pack(side=RIGHT, fill=Y)
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
return
def create_example():
root = Tk()
root.geometry("1200x1200")
my_outer_frame = Frame(root)
my_outer_frame.pack(fill=BOTH, expand=1)
my_canvas = Canvas(my_outer_frame)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
inner_frame = Frame(my_canvas)
my_canvas.create_window((0, 0), window=inner_frame)
# ^^^ Sets up the ability to have a scroll_bar
changing_frame = Frame(inner_frame, borderwidth=4) # this is the frame that will be changing its contents
changing_frame.pack(side=LEFT, anchor="n")
display_frame(changing_frame, my_outer_frame, my_canvas)
# this method re-displays the changing frame depending on the specified size ('big' or 'small'
root.mainloop()
return
def display_frame(frame, outer_frame, canvas, size='small'):
for widget in frame.winfo_children():
widget.destroy()
if size == 'small':
Button(frame, height=5, width=5, text="Show",
command=lambda this_frame=frame: display_frame(this_frame, outer_frame, canvas, size='big')).grid(row=0,
column=0)
elif size == 'big':
Button(frame, height=5, width=5, text="Hide",
command=lambda this_frame=frame: display_frame(this_frame, outer_frame, canvas, size='small')).grid(
row=0, column=0)
for n in range(1, 100):
Label(frame, text="Other Stuff!").grid(row=n, column=0)
frame.pack(side=LEFT)
add_scrollbar(outer_frame, canvas) # this method is supposed to destroy the existing scrollbar and make a new one
return
if __name__ == '__main__':
create_example()

Scrollbar in canvas

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.

Why does this scrollbar not work after inserting widgets into a listbox?

Basically, I am trying to add a scrollbar to a window containing widgets. I am able to successfully add a scrollbar to a Listbox widget and after inserting stuff, the program works just the way I want it to. But, I face a problem when I place widgets into the Listbox. The scrollbar appears but it seems to be disabled.
from tkinter import *
root = Tk()
root.geometry("640x480")
root.resizable(0,0)
myscrollbar = Scrollbar(root)
myscrollbar.pack(side=RIGHT, fill=Y)
mylist = Listbox(root, width=640, height=480, yscrollcommand=myscrollbar.set)
mylist.pack(fill=BOTH, expand=True)
for x in range(1, 101):
mylist.insert(END, Label(mylist, text="Label: "+str(x)).grid(row=x, column=0))
myscrollbar.config(command = mylist.yview)
root.mainloop()
Any way to fix this code?
from tkinter import *
def myScrollcmd(event):
mycanvas.config(scrollregion=mycanvas.bbox('all'))
root = Tk()
mycanvas = Canvas(root)
mycanvas.pack(fill=BOTH, expand=True)
myFrame = Frame(mycanvas)
mycanvas.create_window((0, 0), window=myFrame, anchor=NW)
myScrollbar = Scrollbar(mycanvas, orient=VERTICAL, command=mycanvas.yview)
myScrollbar.pack(side=RIGHT, fill=Y)
mycanvas.config(yscrollcommand=myScrollbar.set)
mycanvas.bind("<Configure>", myScrollcmd)
for x in range(100):
Label(myFrame, text="Text "+str(x)).pack()
root.mainloop()
This works. However, there is one problem that might not be a major one. The problem is, when my cursor is on the canvas, and I'm moving my mouse wheel, the scrollbar doesn't budge. However the scroll bar moves along with my mouse wheel when my cursor is on top of the scroll bar. I can drag the scroll box to scroll up and down and use the scroll buttons to scroll up and down but the mouse wheel only works when my cursor is hovering over the scrollbar widget.

tkinter put scrollbar on canvas at bottom position

Question on scrollbar positions on my tkinter canvas. I have a frame with 3 canvas widgets. Courtesy to this post for the idea. I added a horizontal scrollbar and each canvas has a 50+ column 500+ row pandas dataframe. The load is not very fast but that isn't an objective.
New rows will be added to the bottom of each dataframe. This new row needs a validation. So instead of scrolling down every time, it would be great if the scrollbar / or canvas shows the bottom part.
See below the code where the 3x canvas and 3x scrollbars (x+y) are defined.
def createBox(window):
list_ = ['df1', 'df2', 'df3'] # 3 dataframes
for i in range(3):
mybox = LabelFrame(window, padx=5, pady=4)
mybox.grid(row=i, column=0)
createWindow(mybox, list_[i], i)
def createWindow(box, lt_actual, i):
canvas = Canvas(box, borderwidth=0)
frame = Frame(canvas)
vsbY = Scrollbar(box, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsbY.set, width=1200, heigh=200)
vsbY.pack(side="right", fill="y")
vsbX = Scrollbar(box, orient="horizontal", command=canvas.xview)
canvas.configure(xscrollcommand=vsbX.set, width=1200, heigh=200)
vsbX.pack(side="bottom", fill="x")
#canvas.yview_moveto(1) - no effect
#canvas.yview_moveto(1.0) - no effect
canvas.pack(side="left", fill="both", expand=True)
canvas.create_window((4,4), window=frame, anchor="nw", tags="frame")
# be sure that we call OnFrameConfigure on the right canvas
frame.bind("<Configure>", lambda event, canvas=canvas: OnFrameConfigure(canvas))
I read on this forum and on some info (effbot) pages that i should use the moveto() / yview_moveto() command option but so far this doesn't seem to work.
Question 1. Should I put the y-scrollbar to the bottom or should i put the canvas view to the bottom.
Question 2. Can you provide some guidance on how to use the moveto or should I follow a different approach?
Thanks so much!
The yview_moveto method of the canvas is indeed the right function to use. Its argument is the fraction of the total height of the canvas that you want off-screen. So using 0 as argument shows the top of the canvas and 1, the bottom.
Reference:
Tkinter.Canvas.yview_moveto-method
Adjusts the canvas to the given Scroll offset.
Offset '0.0' is the beginning of the scrollregion, '1.0' the end.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root)
canvas.grid(row=0, column=0)
canvas.create_oval(0, 0, 20, 20, fill='red')
canvas.create_oval(0, 800, 20, 820, fill='blue')
ys = tk.Scrollbar(root, orient='vertical', command=canvas.yview)
ys.grid(row=0, column=1, sticky='ns')
# configure scrolling
canvas.configure(yscrollcommand=ys.set, scrollregion=canvas.bbox('all'))
# show bottom of canvas
canvas.yview_moveto('1.0')
root.mainloop()
By the way, I don't see any difference between putting the y-scrollbar to the bottom or putting the canvas view to the bottom because the two are linked. But I guessed you wanted to know whether to do it using a method of the scrollbar or of the canvas, and I gave the answer above.
I have found that it is necessary to use idle_tasks before moving the scroll tab:
self.canvas.update_idletasks()
self.canvas.yview_moveto(0)

Categories

Resources