TheFaithfulLearner
TheFaithfulLearner

Reputation: 693

Python, tkinter and the scrollbar in a grid

This is me trying to get a ListBox with a Scrollbar. I've read about 20 other answers on this site and none seem to work in my situation, so I have to ask my own.

I have a LabelFrame widget in which I have a few labels and a ListBox widget. I want the ListBox widget to have a scrollbar. I cannot seem to get it to work.

Here is the offending code:

    self.lbDates = tk.Listbox(master=group, width=12)
    self.lbDates.grid(row=3, column=1, padx=3, pady=3)

    self.scrollDates = tk.Scrollbar(self.lbDates, orient="vertical")
    self.scrollDates.config(command=self.lbDates.yview)
    self.scrollDates.grid(row=3, column=1)

    self.lbDates.config(yscrollcommand=self.scrollDates.set)

A few questions: is the scrollbar supposed to be in the same space (i.e. the same grid row and column) as the ListBox widget? I supposed it should be, but given how much trouble I've had with this, I feel I should ask.

I should say that the list box is populated by pressing a button elsewhere. Without the scrollbar in place, this date is populated correctly so I'm assuming the problem isn't there.

What happens when I run this code? Well I don't see a ListBox, but I to see the scroll bar. Now when there's supposed to be data in there (and I know because I populate it), I see what you might imagine is the correct scrollbar, although the ListBox is either invisible or hidden or something. It flickers on for a fraction of a second when it's populated but then is hidden again.

I can show you, in fact, an example of this.

enter image description here

The top one has no data in and the second has data in. Of course you can't see the data, but you can see that it's supposed to be there. And if I remove the scrollbar, I do get the data I expect in the ListBox widgets. I would like to show an image of the moment it populates before it disappears but it seems like a single frame and I can't catch it.

I'm sure my mistake must be something very simple here, and I very much hope someone can assist. I am using tkinter for the widgets and Python 3.7.

Edit: I have made some alterations as suggested below, so that my ListBox and Scrollbar now exist in a Frame and that Frame is within the LabelFrame object. As follows:

    fr = tk.Frame(master = group)
    fr.grid(row=4, column=1)

    self.lbDates = tk.Listbox(master=fr, width=12)
    self.lbDates.pack()

    self.scrollDates = tk.Scrollbar(master=self.lbDates, orient="vertical")
    self.scrollDates.config(command=self.lbDates.yview)
    self.scrollDates.pack()

    self.lbDates.config(yscrollcommand=self.scrollDates.set)

Unfortunately there is no difference and I seem to be experiencing the exact same problems as before.

Upvotes: 0

Views: 707

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385870

is the scrollbar supposed to be in the same space (i.e. the same grid row and column) as the ListBox widget?

It's up to you. Typically you do not put the scrollbar inside the listbox, as it would obscure some of the data in the listbox. The listbox and scrollbar will have the same parent. If you're using grid, usually the vertical scrollbar belongs in the column immediately to the right of the listbox.

What happens when I run this code? Well I don't see a ListBox, but I to see the scroll bar.

That is because the listbox shrinks to fit its children. The listbox is there, but it's only as wide and tall as the scrollbar. This is another reason why you shouldn't put the scrollbar inside the listbox.


If you want the scrollbar to appear as if it's inside the listbox, the best practice is to create a frame that holds the listbox and scrollbar.

You can make the frame sunken and the listbox flat, and it will give the illusion that the scrollbar is inside the listbox. You can then put that frame anywhere you like in your UI and the scrollbar will always be with the listbox.

Both the listbox and the scrollbar need to be children of the frame for this to work.

Here's a simple example:

import tkinter as tk

root = tk.Tk()

lb_frame = tk.Frame(root, bd=1, relief="sunken")
listbox = tk.Listbox(lb_frame, bd=0, yscrollcommand=lambda *args: vsb.set(*args))
vsb = tk.Scrollbar(lb_frame, orient="vertical", command=listbox.yview)

vsb.pack(side="right", fill="y")
listbox.pack(side="left", fill="both", expand=True)

lb_frame.pack(padx=20, pady=20)

for i in range(1, 100):
    listbox.insert("end", f"Item {i}")

root.mainloop()

screenshot of window with listbox

Upvotes: 1

Related Questions