0

I wanted to try to use tkinter for the first time, and had a little program I wanted to try.

It needed some text in a "table" and because of the number of rows and columns, I needed scrollbars for vertical and horizontal scrolling.

I took two examples from https://pythonguides.com/python-tkinter-scrollbar/ and tried to combine the two to get effectively the first example, with more columns and a horizontal scrollbar.

It works perfectly, except that the horizontal scrollbar doesn't extend across the whole of the bottom of the canvas, but is fixed in the bottom right.

I cannot figure out what I have done wrong, but any guidance would be most appreciated.

#!/usr/bin/python3

import tkinter as tk

root = tk.Tk()
root.title("Customer Data")

# Create a canvas and scrollbar
canvas = tk.Canvas(root)
vertScrollbar = tk.Scrollbar(root, orient=tk.VERTICAL, command=canvas.yview)
horzScrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=canvas.xview)
# canvas.configure(yscrollcommand=vertScrollbar.set, xscrollcommand=horzScrollbar.set)


#############


canvas.config(
    xscrollcommand=horzScrollbar.set,
    yscrollcommand=vertScrollbar.set
    )


#############


# Create a frame inside the canvas
frame = tk.Frame(canvas)
canvas.create_window((0, 0), window=frame, anchor='nw')

# Add table headers
headers = ['Name', 'Email', 'Phone', 'Location', 'Animal']
for col, header in enumerate(headers):
    tk.Label(frame, text=header, font=('Arial', 12, 'bold')).grid(row=0, column=col, padx=10, pady=5)

# Add customer data to the table
customers = [
    ('John Doe'  , '[email protected]', '(123) 456-7890', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3210', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3211', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3212', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3213', 'Williamstown, Washington', 'Elephant'),
    # Add more customer data here
]

for row, customer in enumerate(customers, start=1):
    for col, item in enumerate(customer):
        tk.Label(frame, text=item).grid(row=row, column=col, padx=10, pady=5)

# Update the scroll region
frame.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'))

canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
vertScrollbar.pack(side=tk.RIGHT, fill=tk.Y)
horzScrollbar.pack(side=tk.BOTTOM, fill=tk.X)


root.mainloop()

Many thanks for the reply @Seýdylla Gurbangeldiýew. I created a new file and copied your answer verbatim, but it hung. When I managed to exit, I got the error

import-im6.q16: attempt to perform an operation not allowed by the security policy `PS' @ error/constitute.c/IsCoderAuthorized/426.
./fil2.py: line 9: syntax error near unexpected token `('
./fil2.py: line 9: `root = tk.Tk()'

which I didn't understand, so I tried to copy the lines that seemed different to my code:

# Container for canvas and vertical scrollbar
container = tk.Frame(root)
container.pack(fill="both", expand=True)

canvas = tk.Canvas(container)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

vertScrollbar = tk.Scrollbar(container, orient=tk.VERTICAL, command=canvas.yview)
vertScrollbar.pack(side=tk.RIGHT, fill=tk.Y)

horzScrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=canvas.xview)
horzScrollbar.pack(side=tk.BOTTOM, fill=tk.X)


'''
# Create a canvas and scrollbar
canvas = tk.Canvas(root)
vertScrollbar = tk.Scrollbar(root, orient=tk.VERTICAL, command=canvas.yview)
horzScrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=canvas.xview)
# canvas.configure(yscrollcommand=vertScrollbar.set, xscrollcommand=horzScrollbar.set)
'''

And that almost worked! What now happens is that initially, the horizontal scrollbar appears, but when I resize the program, so that the horizontal scrollbar isn't needed, it disappears, but doesn't return when needed.

My apologies...

Your help would be most appreciated!

Many thanks @detlef - that provided what I had been looking for without realising it! I will also try changing the packing as suggested as well! Thank you for your help.

3
  • The order of packing the scrollbars and canvas matters. Try packing the canvas after packing the two scrollbars. Commented Nov 17 at 1:52
  • @acw1668 - I have just tried this, and packing the canvas after packing the scrollbars does work, however, the horizontal scrollbar vanishes if the program is resized to be larger than both X & Y data - then reduce the Y dimension so that the X scrollbars are needed - but they are not visible. When the Y dimension is extended far beyond the data, then the horizontal scrollbar reappears. This is OK for small datasets, but my dataset exceeds the screen size. How can I ensure the X scrollbar remains visible whenever the data exceeds the displayed width? Many thanks, Commented Nov 17 at 6:11
  • I don't understand what you said. The horizontal scrollbar is always visible in my Windows 11 with Python 3.13.9. Commented Nov 17 at 7:17

2 Answers 2

1

I would like to present the following example, which uses a ttk.TreeView and, instead of pack, the grid layout to position the scrollbars. It can easily be integrated into a Frame if the display needs to be expanded with additional elements.

import tkinter as tk 
from tkinter import ttk 

def main():
    headers = ('Name', 'Email', 'Phone', 'Location', 'Animal',)
    customers = [
        ('John Doe'  , '[email protected]', '(123) 456-7890', 'Williamstown, Washington', 'Elephant'),
        ('Jane Smith', '[email protected]', '(987) 654-3210', 'Williamstown, Washington', 'Elephant'),
        ('Jane Smith', '[email protected]', '(987) 654-3211', 'Williamstown, Washington', 'Elephant'),
        ('Jane Smith', '[email protected]', '(987) 654-3212', 'Williamstown, Washington', 'Elephant'),
        ('Jane Smith', '[email protected]', '(987) 654-3213', 'Williamstown, Washington', 'Elephant'),
        # Add more customer data here
    ]
    root = tk.Tk()
    root.title('Customer Data')
    root.geometry('640x360')

    treeview = ttk.Treeview(root, columns=tuple(range(len(headers))), show='headings')
    h_scroll = ttk.Scrollbar(root, orient=tk.HORIZONTAL, command=treeview.xview)
    v_scroll = ttk.Scrollbar(root, orient=tk.VERTICAL, command=treeview.yview)
    treeview.configure(yscrollcommand=v_scroll.set, xscrollcommand=h_scroll.set)

    root.grid_rowconfigure(0, weight=1)
    root.grid_columnconfigure(0, weight=1)
    root.grid_columnconfigure(1, weight=0)

    treeview.grid(column=0, row=0, sticky='nsew')
    v_scroll.grid(column=1, row=0, sticky='nsew')
    h_scroll.grid(column=0, row=1, columnspan=1, sticky='nsew')

    for i,h in enumerate(headers):
        treeview.heading(i, text=h)
        treeview.column(i, minwidth=150, width=180)

    for row in customers:
        treeview.insert('', tk.END, values=row)

    root.mainloop()

if __name__ == '__main__':
    main()
Sign up to request clarification or add additional context in comments.

Comments

0

You're very close — the only issue is how you're packing the canvas and the horizontal scrollbar.

Because it's packed on the left, Tkinter allocates space for it first, then places the vertical scrollbar on the right, and finally your horizontal scrollbar at the bottom — but only under the vertical scrollbar, not under the canvas.
Tkinter “thinks” the canvas occupies only the left side, so the horizontal scrollbar is constrained to the remaining space (below the right-side scrollbar).

Full corrected version of your script

#!/usr/bin/python3

import tkinter as tk

root = tk.Tk()
root.title("Customer Data")

# Container for canvas and vertical scrollbar
container = tk.Frame(root)
container.pack(fill="both", expand=True)

canvas = tk.Canvas(container)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

vertScrollbar = tk.Scrollbar(container, orient=tk.VERTICAL, command=canvas.yview)
vertScrollbar.pack(side=tk.RIGHT, fill=tk.Y)

horzScrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=canvas.xview)
horzScrollbar.pack(side=tk.BOTTOM, fill=tk.X)

canvas.config(
    xscrollcommand=horzScrollbar.set,
    yscrollcommand=vertScrollbar.set
)

# Frame inside canvas
frame = tk.Frame(canvas)
canvas.create_window((0, 0), window=frame, anchor='nw')

headers = ['Name', 'Email', 'Phone', 'Location', 'Animal']
for col, header in enumerate(headers):
    tk.Label(frame, text=header, font=('Arial', 12, 'bold')).grid(row=0, column=col, padx=10, pady=5)

customers = [
    ('John Doe'  , '[email protected]', '(123) 456-7890', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3210', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3211', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3212', 'Williamstown, Washington', 'Elephant'),
    ('Jane Smith', '[email protected]', '(987) 654-3213', 'Williamstown, Washington', 'Elephant'),
]

for row, customer in enumerate(customers, start=1):
    for col, item in enumerate(customer):
        tk.Label(frame, text=item).grid(row=row, column=col, padx=10, pady=5)

frame.update_idletasks()
canvas.configure(scrollregion=canvas.bbox('all'))

root.mainloop()
New contributor
Seýdylla Gurbangeldiýew is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

1 Comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.