1

how can i add auto-hide scrollbars with python tkinter in a listbox? for example, to hide the scrollbar when the size of the listbox is less than or equal to the size of the frame, and otherwise reappear.

I wrote a code that creates me a listbox with scrollboxes. It is fully working, now I would like to add the ability to hide scrollboxes.

import tkinter
from tkinter import ttk

class ScrollingListbox(ttk.Frame):
    def __init__(self, master, width=80, selectmode="multiple", borderwidth=0, highlightthickness=0):
        ttk.Frame.__init__(self, master)

        self._scrollbar_vertical = ttk.Scrollbar(self)
        self._scrollbar_vertical.pack(side=tkinter.RIGHT, fill=tkinter.Y)

        self._scrollbar_horizontal = ttk.Scrollbar(self, orient="horizontal")
        self._scrollbar_horizontal.pack(side=tkinter.BOTTOM, fill=tkinter.X)

        self._listbox_playlist_tracks = tkinter.Listbox(self, width=width, selectmode=selectmode,
                                                        yscrollcommand=self._scrollbar_vertical.set,
                                                        xscrollcommand=self._scrollbar_horizontal.set,
                                                        borderwidth=borderwidth, highlightthickness=highlightthickness)
        self._listbox_playlist_tracks.pack(expand=1, fill=tkinter.BOTH, padx=5, pady=5)
        self._scrollbar_vertical.config(command=self._listbox_playlist_tracks.yview)
        self._scrollbar_horizontal.config(command=self._listbox_playlist_tracks.xview)

    def insert(self, index=tkinter.END, value=0):
        self._listbox_playlist_tracks.insert(index, value)

    def delete(self, first=0, last=tkinter.END):
        self._listbox_playlist_tracks.delete(first, last)

    def foo(self):
        new_height = self.winfo_height()
        min_height = self._listbox_playlist_tracks.winfo_reqheight()
        print(f'f={new_height}, l={min_height}')


if __name__ == '__main__':
    root = tkinter.Tk()
    root.geometry('700x500')
    _listbox_playlist_tracks = ScrollingListbox(root)
    _listbox_playlist_tracks.pack(expand=1, fill=tkinter.BOTH, padx=5, pady=5)

    for i in range(500):
        _listbox_playlist_tracks.insert(tkinter.END, i)
    _listbox_playlist_tracks.foo()

I tried to implement it with similar methods as in this implementation, but the winfo_reqwidth() method constantly produces the same value. Perhaps there are some other methods for getting the dimensions of the listbox and comparing it with the dimensions of the parent frame.

another example for canvas i found here: https://www.geeksforgeeks.org/autohiding-scrollbars-using-python-tkinter/

Mike
  • 43
  • 5

1 Answers1

1

The basic idea is as follows:

The only time you need to hide or show the scrollbars is when the widget changes size, or when you change the contents of the listbox. When that happens, you can query the xview or yview of the widget to see if the scrollable area is bigger than the visible part of the listbox. If all of the contents fit, the result of calling xview and yview functions will be the tuple (0.0, 1.0). When something scrolls off of the visible area those numbers will change.

So, start by creating a function that does the calculation and then hides or shows the scrollbar. It's easiest if you use grid rather than pack, since grid can remember it's configuration. However, you can use pack with just a little more work.

For the vertical scrollbar it might look like this:

def _manage_scrollbar(self, event=None):
    yview = self._listbox_playlist_tracks.yview()
    if yview == (0.0, 1.0):
        self._scrollbar_vertical.pack_forget()
    else:
        self._scrollbar_vertical.pack(before=self._scrollbar_horizontal,
                                      side=tkinter.RIGHT, fill=tkinter.Y)

You can then call this function when you modify the contents of the listbox:

for i in range(50):
    _listbox_playlist_tracks.insert(tkinter.END, i)
self._manage_scrollbar()

Finally, bind this to the <Configure> event of the listbox, so that when the size of the widget changes, the function is called:

class ScrollingListbox(ttk.Frame):
    def __init__(self, master, width=80, selectmode="multiple", borderwidth=0, highlightthickness=0):       
        ...
        self._listbox_playlist_tracks.bind("<Configure>", self._manage_scrollbar)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685