Automatically resize text widget's height to fit all text

6.2k views Asked by At

How can one automatically resize a text widget to fit a text widget's height

  • There will not be any \n om the text widget, instead the text will wrap (whole word) around and continue down. wrap=WORD

How can this done?

My idea of approaching the problem statement: I was wondering if it was possible to count every time the text was wrapped around in the text widget, and by that given value somehow calculate the height of the text widget? - Just a thought .. I have no clue whether it's possible.

**WHY THIS IS NOT A DUPLICATE **

This is not a duplicate, link to the question you claim it is a duplicate of if you think so. They all have implemented a solution in which it check a '\n' in each keystroke in the text widget. My text widget won't have any '\n' in it at all. But instead wrap the words around !

This is NOT the solution I am looking for, since it is looking for '\n' and changes the height accordingly to how many of them it finds. Since I won't be using any '\n' but instead wrap the words around (Text(frame, wrap=WORDS)) no '\n' will not appeare making that solution USELESS!"

That is why this code, from the question people claim this is a duplicate of, WONT fix this question, this is NOT a duplicate.

wont fix my problem since it looks for '\n':

    import Tkinter

class TkExample(Tkinter.Frame):
   def __init__(self, parent):
      Tkinter.Frame.__init__(self, parent)
      self.init_ui()

   def init_ui(self):
      self.pack()
      text_box = Tkinter.Text(self)
      text_box.pack()
      text_box.bind("<Key>", self.update_size)

   def update_size(self, event):
      widget_width = 0
      widget_height = float(event.widget.index(Tkinter.END))
      for line in event.widget.get("1.0", Tkinter.END).split("\n"):
         if len(line) > widget_width:
            widget_width = len(line)+1
      event.widget.config(width=widget_width, height=widget_height)

if __name__ == '__main__':
    root = Tkinter.Tk()
    TkExample(root)
    root.mainloop()

edit example

This is the reason why I am not using message widgets, they doesn't rearrange the text to fill out the text widget.

enter image description here

1

There are 1 answers

2
Bryan Oakley On BEST ANSWER

The tkinter text widget isn't designed to grow or shrink to fit its contents like a label widget. If all you need is to display plain text with no need to interactively edit more text, a Label is probably a better choice than Text.

That being said, it's possible to get the number of displayed lines in a text widget, and with that information you can resize the widget.

Here's an example that shows how to cause it to resize when you insert text programatically. It won't handle resizing as you type, though it can be made to do it.

The trick is to know that internally the text widget has a count method which you can call to get the number of displayed lines. Unfortunately, this method isn't exposed at the tkinter layer and thus requires a bit of knowledge of how tkinter works internally.

class ExpandoText(tk.Text):
    def insert(self, *args, **kwargs):
        result = tk.Text.insert(self, *args, **kwargs)
        self.reset_height()
        return result

    def reset_height(self):
        height = self.tk.call((self._w, "count", "-update", "-displaylines", "1.0", "end"))
        self.configure(height=height)

Here is an example of how to use it:

root = tk.Tk()
text = ExpandoText(root, width=20, wrap="word")
text.pack(fill="both", expand=True)

root.update_idletasks()
text.insert("1.0", "This is a line of text that will initially be wrapped.")

root.after(5000, text.insert, "end", "This is more text")

root.mainloop()

When you run the code, you should see a window that looks like this:

original screenshot

If you don't manually resize the window, and wait 5 seconds, the window will grow to show the added text:

screenshot with more text

Unfortunately you must call update_idletasks before the insertion so that tkinter knows how wide the window will actually be. This might have visual side effects depending on how the rest of your code works (read: you might see a flash when the UI first starts up).