1

I want my text widgets to have scrollbar capability within each text widget. I have created a canvas and within it have embedded a frame using create_window. I then put 2 Text widgets into this frame. I want each of the text widgets to have a scrollbar, however when I add the scrollbars one of them fits the entire frame whilst the second one fits half of the frame. How would I solve this problem?

import tkinter as tk
import os
import tkinter.filedialog
import tkinter.messagebox

class Main(tk.Tk):

def __init__(self, *args, **kwargs):
    '''This initialisation runs the whole program'''
    #textBoxList = []

    tk.Tk.__init__(self, *args, **kwargs)
    self.title('Untitled')
    self.geometry('500x500')
    self.canvas = tk.Canvas(self)
    self.scroll = tk.Scrollbar(self, orient='vertical', command=self.canvas.yview)
    self.canvas.configure(yscrollcommand=self.scroll.set)
    self.frame = tk.Frame(self.canvas) # frame does not get pack() as it needs to be embedded into canvas throught canvas.
    self.scroll.pack(side='right', fill='y')
    self.canvas.pack(fill='both', expand='yes')
    self.canvas.create_window((0,0), window=self.frame, anchor='nw')
    self.frame.bind('<Configure>', self.canvas.configure(scrollregion=self.canvas.bbox('all')))

    # 1st Text Widget
    self.journal = tk.Text(self.frame)
    self.vsb = tk.Scrollbar(self.frame)
    self.vsb.config(command=self.journal.yview)
    self.journal.config(yscrollcommand=self.vsb.set)
    self.vsb.pack(side='right', fill='y')
    self.journal.pack()

    #2nd Text Widget
    self.good = tk.Text(self.frame)
    self.vsb2 = tk.Scrollbar(self.frame)
    self.vsb2.config(command=self.good.yview)
    self.good.config(yscrollcommand=self.vsb2.set)
    self.vsb2.pack(side='right', fill='y')
    self.good.pack()

root = Main()
root.mainloop()
2
  • This is hard to answer without an MCVE. Commented Jun 17, 2015 at 17:02
  • Sorry abour that, I edited the code so that it is MCVE Commented Jun 17, 2015 at 17:12

3 Answers 3

1

Two issues (apart from the messed-up indentation which probably just happened when pasting your code):

  1. The frame was bound to the returned value of a function rather than to a function itself. You can fix this with a lambda function.
  2. You're trying to create a complex layout with pack(). Try grid() instead.

 

import tkinter as tk
import os
import tkinter.filedialog
import tkinter.messagebox

class Main(tk.Tk):

    def __init__(self, *args, **kwargs):
        '''This initialisation runs the whole program'''
        #textBoxList = []

        tk.Tk.__init__(self, *args, **kwargs)
        self.title('Untitled')
        self.geometry('500x500')
        self.canvas = tk.Canvas(self)
        self.scroll = tk.Scrollbar(self, orient='vertical', command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)
        self.frame = tk.Frame(self.canvas) # frame does not get pack() as it needs to be embedded into canvas throught canvas.
        self.scroll.pack(side='right', fill='y')
        self.canvas.pack(fill='both', expand='yes')
        self.canvas.create_window((0,0), window=self.frame, anchor='nw')
        self.frame.bind('<Configure>', lambda x: self.canvas.configure(scrollregion=self.canvas.bbox('all'))) # lambda function

        # 1st Text Widget
        self.journal = tk.Text(self.frame)
        self.vsb = tk.Scrollbar(self.frame)
        self.vsb.config(command=self.journal.yview)
        self.journal.config(yscrollcommand=self.vsb.set)
        self.journal.grid(row=0, column=0) # grid instead
        self.vsb.grid(row=0, column=1, sticky='ns') # grid instead

        #2nd Text Widget
        self.good = tk.Text(self.frame)
        self.vsb2 = tk.Scrollbar(self.frame)
        self.vsb2.config(command=self.good.yview)
        self.good.config(yscrollcommand=self.vsb2.set)
        self.good.grid(row=1, column=0) # grid instead
        self.vsb2.grid(row=1, column=1, sticky='ns') # grid instead

root = Main()
root.mainloop()
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, could you possibly expand on issue 1 as I don't really understand: a) how frame is bound to a returned value b) How providing lambda makes it bind to a function instead
See this question for more info on that. Basically, something like multiply(2,3) isn't a function, it's just the number 6. The lambda keyword defines a function and basically serves as a wrapper.
1

I'm not sure why this is happening, but you can work around it by creating a frame for each text/scrollbar pair.

    first_text_frame = tk.Frame(self.frame)
    first_text_frame.pack()
    # 1st Text Widget
    self.journal = tk.Text(first_text_frame)
    self.vsb = tk.Scrollbar(first_text_frame)
    self.vsb.config(command=self.journal.yview)
    self.journal.config(yscrollcommand=self.vsb.set)
    self.vsb.pack(side='right', fill='y')
    self.journal.pack()

    second_text_frame = tk.Frame(self.frame)
    second_text_frame.pack()
    #2nd Text Widget
    self.good = tk.Text(second_text_frame)
    self.vsb2 = tk.Scrollbar(second_text_frame)
    self.vsb2.config(command=self.good.yview)
    self.good.config(yscrollcommand=self.vsb2.set)
    self.vsb2.pack(side='right', fill='y')
    self.good.pack()

Comments

1

If you want to have two text widgets, one on top of the other, each with a scrollbar on the right, pack is the wrong choice of geometry managers. You should either switch to grid, or use some internal frames to make things fit.

Your other choice would be to create a custom class that is a frame which contains one text widget and one scrollbar. Then, create two instances of this class and pack one on top of the other.

Comments

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.