How to code the tkinter “scrolledtext” module

2019-04-04 01:44发布

问题:

The code below produces an ugly but functional example of using a scroll bar in a text widget and results in a couple of questions. Note: this is done using Python 3 on a windows box.

  1. The scroll bar that appears is attached to the frame and although it scrolls the text box contents, I would prefer that it was attached to the text widget itself. I have been unable to get this to happen.

  2. There are a number of references to a Tkinter module called "scrolledtext" that is supposed to be a much better mechanism for adding scrollbars to text boxes but, I have not found any examples of how to import it and call it that I am able to get to work (probably need an example).


frame1 = tk.Frame(win,width=80, height=80,bg = '#808000')
frame1.pack(fill='both', expand='yes')
scrollbar = Scrollbar(frame1) 
scrollbar.pack(side=RIGHT, fill=Y)
editArea = Text(frame1, width=10, height=10, wrap=WORD, yscrollcommand=scrollbar.set)
editArea.pack()    
scrollbar.config(command=editArea.yview)
editArea.place(x=10,y=30)

回答1:

You were right, you can use the ScrolledText widget from tkinter.scrolledtext module, like this:

import tkinter as tk
import tkinter.scrolledtext as tkst

win = tk.Tk()
frame1 = tk.Frame(
    master = win,
    bg = '#808000'
)
frame1.pack(fill='both', expand='yes')
editArea = tkst.ScrolledText(
    master = frame1,
    wrap   = tk.WORD,
    width  = 20,
    height = 10
)
# Don't use widget.place(), use pack or grid instead, since
# They behave better on scaling the window -- and you don't
# have to calculate it manually!
editArea.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
# Adding some text, to see if scroll is working as we expect it
editArea.insert(tk.INSERT,
"""\
Integer posuere erat a ante venenatis dapibus.
Posuere velit aliquet.
Aenean eu leo quam. Pellentesque ornare sem.
Lacinia quam venenatis vestibulum.
Nulla vitae elit libero, a pharetra augue.
Cum sociis natoque penatibus et magnis dis.
Parturient montes, nascetur ridiculus mus.
""")
win.mainloop()

And there you go:



回答2:

While using the ScrolledText widget might save you a few lines of code, it's not doing anything you can't do yourself. Doing it yourself will help remove some of the mystery.

You're actually doing everything almost correct. The mistake you're making is that you should have your text widget fill it's container completely rather than being just a tiny part of it.

The standard way to do this is like this:

container = tk.Frame(...)
text = tk.Text(container, ...)
scrollbar = tk.Scrollbar(container, ...)
scrollbar.pack(side="right", fill="y")
text.pack(side="left", fill="both", expand=True)

That's all there is to it. Now, no matter how big your container gets due to window resizes, etc, the scrollbar will always appear attached to the text widget. You then treat this whole group of container, text widget and scrollbar as a single widget when adding it to your whole GUI.

Note that you can use grid here as well. Pack is easier if you only have a vertical scrollbar. If you have both horizontal and vertical scrollbars, grid makes a little more sense.

To complete the illusion, you can set the borderwidth of the text widget to zero, and set the borderwidth of the containing frame to 1 with a relief of sunken, and the scrollbar will appear to be "in" the text widget.

Here is a complete working example that looks more-or-less like your example:

import Tkinter as tk

win = tk.Tk()

win.configure(background="#808000")

frame1 = tk.Frame(win,width=80, height=80,bg = '#ffffff',
                  borderwidth=1, relief="sunken")
scrollbar = tk.Scrollbar(frame1) 
editArea = tk.Text(frame1, width=10, height=10, wrap="word",
                   yscrollcommand=scrollbar.set,
                   borderwidth=0, highlightthickness=0)
scrollbar.config(command=editArea.yview)
scrollbar.pack(side="right", fill="y")
editArea.pack(side="left", fill="both", expand=True)
frame1.place(x=10,y=30)

win.mainloop()

Personally I don't recommend doing global imports, and I don't recommend using place, but I wanted to keep this as close to your original as possible.