Automatically resize text widget's height to fit all text
Asked Answered
W

1

5

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

Weathercock answered 6/9, 2017 at 18:11 Comment(4)
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 !Weathercock
Is there a reason you aren't using a Label or Message widget? Do you need the ability for the user to edit text?Simpleton
I am not using a Message widgets because they doesn't rearrange the text to fill out the horizontal view of the widget as you can see in the "edit example" I just updated my question with. And the reason why I am not using a label should be pretty clear to you.Weathercock
I'm sorry, but the reason for not using a label is definitely not clear. If you don't need the user to be able to edit the contents, the label is probably the right choice. Please try to rewrite your question to make it more clear what you're actually trying to accomplish. The fact is, the text widget doesn't support what you want, so we need to know what you're really trying to accomplish so we can find the right solution. For example, do you need any of the features of the text widget, or are you just looking for a way to display wrapped text?Simpleton
S
12

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).

Simpleton answered 7/9, 2017 at 15:35 Comment(2)
Excellent! Where did you read/learn about this count method (internal stuff in general). I quote from @nbro: "As far as I know there is no official documentation for tk.call()." The reason why I am asking is because I would like to know more on this subject, in order to to proper implement it in my code. quote from: https://mcmap.net/q/801395/-what-does-the-quot-tk-call-quot-function-do-in-python-tkinterWeathercock
The count method is documented in the official tk documentation: tcl.tk/man/tcl8.5/TkCmd/text.htm#M72, and the call method I learned by examining the tkinter source code.Simpleton

© 2022 - 2024 — McMap. All rights reserved.