2025, Sep 28 09:15
Как убрать зазор между Listbox и горизонтальной Scrollbar в Tkinter
Пошаговое решение проблемы зазора между Listbox и горизонтальной Scrollbar в Tkinter: настройка sticky в grid и альтернатива с Frame. Примеры кода, нюансы.
Как добиться, чтобы полоса прокрутки Tkinter прилегала вплотную к Listbox
Иногда, казалось бы, простая раскладка в Tkinter никак не хочет выровняться. Типичный случай — горизонтальная полоса прокрутки, которая не прилегает вплотную к Listbox и оставляет неожиданный зазор, даже если параметры highlightthickness, отступы и значения sticky выглядят правильными. Становится сложнее, когда в той же сетке есть другие виджеты, например Canvas: они влияют на размер строк.
Минимальная конфигурация, на которой видно проблему
В примере ниже Canvas расположен слева, а справа — Listbox с горизонтальной полосой прокрутки. Зазор обычно появляется, когда у Listbox задана фиксированная высота; в таком варианте он проявляется на разных платформах.
import tkinter as tk
app = tk.Tk()
lb = tk.Listbox(app)
hbar = tk.Scrollbar(app)
cnv = tk.Canvas(app, width=205, height=205)
img = tk.PhotoImage(file="password_img.png")
cnv.img_ref = img
cnv.create_image(102, 102, image=img)
cnv.config(highlightthickness=0)
cnv.grid(column=0, row=0, rowspan=2)
lb.config(height=4, xscrollcommand=hbar.set)
lb.grid(column=1, row=0, sticky="EW")
hbar.config(orient="horizontal", command=lb.xview)
hbar.grid(column=1, row=1, sticky="EW")
app.mainloop()Что на самом деле вызывает зазор
Это поведение проявляется особенно заметно, когда высота Listbox фиксирована. Менеджер геометрии grid выравнивает высоты строк по всем столбцам, и сочетание размеров виджетов может дать вертикальный сдвиг, если элементы не «прижаты» друг к другу. В приведённом примере Listbox не опущен к нижнему краю своей ячейки, а Scrollbar не подтянут к верхнему краю своей, поэтому между ними остаётся тонкая полоска пустого пространства.
Решение: «прижать» обе стороны через sticky
Заставьте Listbox прилипнуть к южной границе, а Scrollbar — к северной, сохранив растяжение по оси восток–запад. В результате их края сходятся, и зазор исчезает.
import tkinter as tk
app = tk.Tk()
lb = tk.Listbox(app)
hbar = tk.Scrollbar(app)
cnv = tk.Canvas(app, width=205, height=205)
img = tk.PhotoImage(file="password_img.png")
cnv.img_ref = img
cnv.create_image(102, 102, image=img)
cnv.config(highlightthickness=0)
cnv.grid(column=0, row=0, rowspan=2)
lb.config(height=4, xscrollcommand=hbar.set)
lb.grid(column=1, row=0, sticky="EWS")
hbar.config(orient="horizontal", command=lb.xview)
hbar.grid(column=1, row=1, sticky="NEW")
app.mainloop()Такое уточнение удерживает всё на своих местах и не затрагивает другие вертикальные отступы, которые вы, возможно, хотите сохранить.
Альтернатива: изолировать через Frame
Есть и другой надёжный приём: поместить Listbox и Scrollbar в Frame и управлять их взаимным расположением внутри контейнера. Тогда на них не будет влиять сетка, используемая в остальной части окна. В этом варианте Canvas не нужен rowspan.
import tkinter as tk
app = tk.Tk()
cnv = tk.Canvas(app, width=205, height=205)
img = tk.PhotoImage(file="password_img.png")
cnv.img_ref = img
cnv.create_image(102, 102, image=img)
cnv.config(highlightthickness=0)
cnv.grid(column=0, row=0)
holder = tk.Frame(app)
holder.grid(column=1, row=0)
lb = tk.Listbox(holder)
hbar = tk.Scrollbar(holder)
lb.config(xscrollcommand=hbar.set, height=4)
lb.grid(column=1, row=0, sticky="EW")
hbar.config(orient="horizontal", command=lb.xview)
hbar.grid(column=1, row=1, sticky="EW")
app.mainloop()Такой макет держит пару вместе и даёт тот же визуальный результат, при этом остальная часть интерфейса остаётся независимой.
Почему это важно
Строки, которыми управляет grid, делят высоту между столбцами — примерно как в электронных таблицах. Более высокий виджет в одном столбце может увеличить высоту строки и «вскрыть» зазоры в других местах, если элементы не закреплены корректно. Явные настройки sticky и, при необходимости, изоляция связанных виджетов в Frame защищают от мелких смещений при росте, сжатии или усложнении интерфейса.
Итог
Если полоса прокрутки не прилегает вплотную к Listbox, закрепите обе стороны: добавьте S к sticky у Listbox и N — у Scrollbar, сохранив растяжение по горизонтали. Если в окне есть другие виджеты, влияющие на размеры grid, объедините Listbox и Scrollbar в Frame и располагайте их внутри контейнера. Эти два приёма делают компоновку предсказуемой и избавляют от охоты за непонятными зазорами.
Статья основана на вопросе с StackOverflow от Nabodita Gangully и ответе furas.