David 04
David 04

Reputation: 47

How to make a scrollable listbox? tkinter python

I want to create a listbox with an auto hiding scrollbar. I've been searching and found a class example; when I play around with it, the scrollbar doesn't stayed attached to the right side as expected.

This is the code:

from tkinter import *


class AutoScrollbar(Scrollbar):
    # a scrollbar that hides itself if it's not needed.  only
    # works if you use the pack geometry manager.
    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            self.pack_forget()
        else:
            if self.cget("orient") == HORIZONTAL:
                self.pack(fill=X)
            else:
                self.pack(fill=Y)
        Scrollbar.set(self, lo, hi)

    def grid(self, **kw):
        raise (TclError, "cannot use grid with this widget")

    def place(self, **kw):
        raise (TclError, "cannot use place with this widget")


class Window1(Tk):
    def __init__(self):
        super().__init__()

        self.list_frame = Frame(self)
        self.list_frame.pack(fill=BOTH, expand=1)

        self.my_scrollbar = AutoScrollbar(self.list_frame, orient=VERTICAL)

        self.box1 = Listbox(self.list_frame, bg='black', fg='white',
                            width=60,
                            selectforeground='black',
                            yscrollcommand=self.my_scrollbar.set,
                            selectmode=EXTENDED)

        self.my_scrollbar.config(command=self.box1.yview)
        self.my_scrollbar.pack(side=RIGHT, fill=Y, pady=20)

        self.box1.pack(pady=20, fill=BOTH, expand=1)

        for num in range(15):
            self.box1.insert(END, num)


if __name__ == '__main__':
    w = Window1()
    w.mainloop()

This is the first view when I run the code:

enter image description here

When I expand the window the scrollbar disappears properly: enter image description here

But when I minimize the window, the scrollbar appears back but in an incorrect place and a strange shape: enter image description here

Upvotes: 0

Views: 940

Answers (2)

toyota Supra
toyota Supra

Reputation: 4537

@David 04. Your code is working. You cannot used less than 15. Unless you set geometry or resizable. should solved your problem.

Change this:

for num in range(15):
            self.box1.insert(END, num)

to:

for num in range(105):
            self.box1.insert(END, num)

You will get scrollable resizing.

Upvotes: 1

acw1668
acw1668

Reputation: 46669

It is because you didn't specify side option of pack() inside AutoScrollbar.set(), so it is TOP by default. Also you forgot to specify side in self.box1.pack(...).

Below is the modified code to fix the issues:

from tkinter import *


class AutoScrollbar(Scrollbar):
    # a scrollbar that hides itself if it's not needed.  only
    # works if you use the pack geometry manager.
    def set(self, lo, hi):
        if float(lo) <= 0.0 and float(hi) >= 1.0:
            self.pack_forget()
        else:
            if self.cget("orient") == HORIZONTAL:
                self.pack(fill=X, side=BOTTOM) # added side option
            else:
                self.pack(fill=Y, side=RIGHT)  # added side option
        super().set(lo, hi)

    def grid(self, **kw):
        raise (TclError, "cannot use grid with this widget")

    def place(self, **kw):
        raise (TclError, "cannot use place with this widget")


class Window1(Tk):
    def __init__(self):
        super().__init__()

        self.list_frame = Frame(self, pady=20) # added pady=20
        self.list_frame.pack(fill=BOTH, expand=1)

        self.my_scrollbar = AutoScrollbar(self.list_frame, orient=VERTICAL)

        self.box1 = Listbox(self.list_frame, bg='black', fg='white',
                            width=60,
                            selectforeground='black',
                            yscrollcommand=self.my_scrollbar.set,
                            selectmode=EXTENDED)

        self.my_scrollbar.config(command=self.box1.yview)
        self.my_scrollbar.pack(side=RIGHT, fill=Y) # removed pady=20

        self.box1.pack(fill=BOTH, expand=1, side=LEFT) # added side=LEFT and removed pady=20

        for num in range(15):
            self.box1.insert(END, num)


if __name__ == '__main__':
    w = Window1()
    w.mainloop()

Upvotes: 1

Related Questions