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.