Search code examples
tkintercanvasscrollbar

How to precisely align widgets in a tkinter canvas (with scrollbar)


How to align spinboxes with a grid of boxes, with the grid() method?

In this example code, the spinboxes are aligned with place() but when you move the scrollbar, the spinboxes do not move:

import tkinter as tk
from tkinter import ttk

cellule_taille = 50
rows = 10
cols = 10
x = 50
y = 35
x_texte = 0
y_lettre_pos = 0

root = tk.Tk()

grille_frame = tk.Canvas(root, borderwidth=2, relief="solid")
grille_frame.grid(row=0, column=0, padx=5, sticky="nsew")
yscrollbar_grille = ttk.Scrollbar(root, orient="vertical", command=grille_frame.yview)
yscrollbar_grille.grid(row=0, column=1, columnspan=2, sticky="ns")
xscrollbar_grille = ttk.Scrollbar(root, orient="horizontal", command=grille_frame.xview)
xscrollbar_grille.grid(row=1, column=0, rowspan=2, sticky="ew")
grille_frame.config(yscrollcommand=yscrollbar_grille.set, xscrollcommand=xscrollbar_grille.set)

for r in range(rows):
    y_temp = y +(cellule_taille // 5)
    x = 50
    spinbox_row = tk.Spinbox(grille_frame, from_=0, to=cols, width=2)

    spinbox_row.place(x=x_texte, y=y_temp)
    temp = []
    for c in range(cols):
        cell = grille_frame.create_rectangle((x, y), (x + cellule_taille, y + cellule_taille), fill="white", outline= "black")
        #temp.append(cell)
        x = x + cellule_taille
    y = y + cellule_taille    
x = 50
for c in range(cols):
    x_temp = x
    spinbox_col = tk.Spinbox(grille_frame, from_=0, to=rows, width=2)
    spinbox_col.place(x=x_temp, y=y_lettre_pos)
    x = x + cellule_taille

root.geometry()
root.mainloop()

In this other example, using grid(), spinboxes move with scrollbars, but are not aligned:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 29 12:52:17 2024

@author: jojo
"""

import tkinter as tk
from tkinter import ttk

cellule_taille = 30
rows = 10
cols = 10
x = 30
y = 30
x_texte = 0
y_lettre_pos = 0

root = tk.Tk()

grille_frame = tk.Canvas(root, borderwidth=2, relief="solid")
grille_frame.grid(row=0, column=0, padx=5, sticky="nsew")
yscrollbar_grille = ttk.Scrollbar(root, orient="vertical", command=grille_frame.yview)
yscrollbar_grille.grid(row=0, column=1, rowspan=1, sticky="ns")
xscrollbar_grille = ttk.Scrollbar(root, orient="horizontal", command=grille_frame.xview)
xscrollbar_grille.grid(row=1, column=0, rowspan=1, sticky="ew")
grille_frame.config(yscrollcommand=yscrollbar_grille.set, xscrollcommand=xscrollbar_grille.set)
test1_frame = tk.Frame(grille_frame)
test2_frame = tk.Frame(grille_frame)

for r in range(rows):
    y_temp = y +(cellule_taille // 5)
    x = 30
    spinbox_row = tk.Spinbox(test1_frame, from_=0, to=cols, width=2)
    

    spinbox_row.grid(row=r, column=0)
    temp = []
    for c in range(cols):
        cell = grille_frame.create_rectangle((x, y), (x + cellule_taille, y + cellule_taille), fill="white", outline= "black")
        temp.append(cell)
        x = x + cellule_taille
    y = y + cellule_taille    
x = 30
for c in range(cols):
    x_temp = x
    spinbox_col = tk.Spinbox(test2_frame, from_=0, to=rows, width=2)
    spinbox_col.grid(row=0, column=c)
    x = x + cellule_taille
grille_frame.create_window(10, 10, window=test1_frame)
grille_frame.create_window(10, 10, window=test2_frame)
grille_frame.update_idletasks()
grille_frame.config(scrollregion=grille_frame.bbox("all"))

root.mainloop()

I tried with pack() and grid() but couldn't align the widgets...


Solution

  • You need to put those spinboxes using grille_frame.create_window(...) instead of .grid() or .pack(). In this case, those spinboxes should be children of grille_frame.

    Updated code:

    import tkinter as tk
    from tkinter import ttk
    
    cellule_taille = 30
    rows = 10
    cols = 10
    x = 30
    y = 30
    x_texte = 0
    y_lettre_pos = 0
    
    root = tk.Tk()
    
    grille_frame = tk.Canvas(root, borderwidth=2, relief="solid")
    grille_frame.grid(row=0, column=0, padx=5, sticky="nsew")
    yscrollbar_grille = ttk.Scrollbar(root, orient="vertical", command=grille_frame.yview)
    yscrollbar_grille.grid(row=0, column=1, rowspan=1, sticky="ns")
    xscrollbar_grille = ttk.Scrollbar(root, orient="horizontal", command=grille_frame.xview)
    xscrollbar_grille.grid(row=1, column=0, rowspan=1, sticky="ew")
    grille_frame.config(yscrollcommand=yscrollbar_grille.set, xscrollcommand=xscrollbar_grille.set)
    
    for r in range(rows):
        y_temp = y +(cellule_taille // 5)
        x = 30
        spinbox_row = tk.Spinbox(grille_frame, from_=0, to=cols, width=2)
        #spinbox_row.grid(row=r, column=0)
        grille_frame.create_window((x, y+cellule_taille/2), window=spinbox_row, anchor="e")
    
        temp = []
        for c in range(cols):
            cell = grille_frame.create_rectangle((x, y), (x + cellule_taille, y + cellule_taille), fill="white", outline= "black")
            temp.append(cell)
            x = x + cellule_taille
        y = y + cellule_taille
    
    x = y = 30
    for c in range(cols):
        x_temp = x
        spinbox_col = tk.Spinbox(grille_frame, from_=0, to=rows, width=2)
        #spinbox_col.grid(row=0, column=c)
        grille_frame.create_window((x+cellule_taille/2, y), window=spinbox_col, anchor="s")
        x = x + cellule_taille
    
    grille_frame.update_idletasks()
    grille_frame.config(scrollregion=grille_frame.bbox("all"))
    
    root.mainloop()
    

    Result:

    enter image description here