adam.hendry
adam.hendry

Reputation: 5625

Double Scrollbars in Tkinter Python

I cannot get x and y scrollbars to work in Tkinter with Python, although I have followed multiple examples:

  1. How to add 2 scrollbars with tkinter in python 3.4
  2. Adding a scrollbar to a group of widgets in tkinter
  3. How to make a proper double scrollbar frame in tkinter
  4. Vertical and Horizontal Scrollbars on Tkinter Widget
  5. Scrolling a Canvas widget horizontally and vertically

The scrollbars appear, but do not activate when the window is smaller than the frame. How can I get this to work (see image below)?

Problem 1

Below is the minimal code that is producing my problem (Python 3.7)

import tkinter as tk
from tkinter import ttk

big_font = ("Arial", 50)

class DoubleScrollbarFrame(ttk.Frame):

    def __init__(self,
                 parent,
                 *args,
                 **kwargs):
        self.parent = parent

        super().__init__(self.parent,
                         *args,
                         **kwargs)

        #Set widgets to fill main window such that they are
        #all the same size
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self.create_widgets()
        self.position_widgets()

    def create_widgets(self):
        self.canvas = tk.Canvas(self)
        self.frame = ttk.Frame(self.canvas)
        self.scroll_x = ttk.Scrollbar(self,
                                      orient = tk.HORIZONTAL,
                                      command = self.canvas.xview)
        self.scroll_y = ttk.Scrollbar(self,
                                      orient = tk.VERTICAL,
                                      command = self.canvas.yview)
        self.canvas.config(xscrollcommand = self.scroll_x.set,
                           yscrollcommand = self.scroll_y.set)
        self.canvas.create_window((0,0),
                                  window = self.frame,
                                  anchor = 'nw')
        self.frame.bind('<Configure>',
                        self.set_scrollregion)
        self.sizegrip = ttk.Sizegrip(self)

        #Test
        self.test1 = tk.Label(self.frame,
                              text = 'Test 1',
                              font = big_font)
        self.test2 = tk.Label(self.frame,
                              text = 'Test 2',
                              font = big_font)
        self.test3 = tk.Label(self.frame,
                              text = 'Test 3',
                              font = big_font)

    def position_widgets(self,
                         **kwargs):
        self.test1.grid(row = 0,
                        column = 0,
                        sticky = 'w')
        self.test2.grid(row = 1,
                        column = 0,
                        sticky = 'w')
        self.test3.grid(row = 2,
                        column = 0,
                        sticky = 'w')
        
        self.scroll_x.grid(row = 1,
                           column = 0,
                           sticky = 'ew')

        self.scroll_y.grid(row = 0,
                           column = 1,
                           sticky = 'ns')

        self.canvas.grid(row = 0,
                         column = 0,
                         sticky = 'nsew')
        
        self.frame.grid(row = 0,
                        column = 0,
                        sticky = 'nsew')

        self.sizegrip.grid(row = 1,
                           column = 1,
                           sticky = 'se')

    def set_scrollregion(self,
                         event):
        print('Frame Dimensions: {} x {}'.format(self.frame.winfo_width(),
                                                 self.frame.winfo_height()))
        print('Canvas Dimensions: {} x {}'.format(self.canvas.winfo_width(),
                                                  self.canvas.winfo_height()))
        self.canvas.configure(scrollregion = self.canvas.bbox('all'))

class MainApp(tk.Tk):

    def __init__(self,
                 *args,
                 **kwargs):

        super().__init__(*args,
                         **kwargs)

        #Set widgets to fill main window such that they are
        #all the same size
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self.create_widgets()
        self.position_widgets()

    def create_widgets(self):
        self.frame = DoubleScrollbarFrame(self)

    def position_widgets(self):
        self.frame.grid(row = 0,
                        column = 0,
                        sticky = 'nsew')

    def exit(self):
        self.destroy()

if __name__ == '__main__':

    #Create GUI
    root = MainApp()

    #Run program
    root.mainloop()

Upvotes: 0

Views: 547

Answers (1)

Bryan Oakley
Bryan Oakley

Reputation: 385870

The problem is in these lines of code inside DoubleScrollbarFrame.position_widgets:

self.frame.grid(row = 0,
                column = 0,
                sticky = 'nsew')

This removes control of the widget from the canvas and gives control to grid. It is no longer a canvas object, so self.canvas.bbox("all") is returning (0, 0, 1, 1). If the scrollregion is set incorrectly, the scrollbars don't know how much to scroll.

The solution is simple: don't call grid on self.frame.

Upvotes: 1

Related Questions