2

I'm working on a gui with tcl/tk 8.5.9 and python 3.5.1 on Mac OS X 10.11.5.

I'm trying to create a sort of list of tk.text widgets that have a fixed width and adjust height to fit their content whenever it is changed (by the user or programmatically). This should take into account real as well as wrapped displaylines.

I got this to work using answers from Tkinter Resize text to contents and here. However, adjusting to user input only works for the text widget that was created last. All previously created text widgets do not resize anymore, once a new one is added.

I think the problem might be somewhere in the lines that deal with the bindtags. Being new to tkinter, python and relatively new to programming in general, i'm not sure i understand bindtags properly.

Any help is much appreciated, here is my code:

from tkinter import *

class ResizingText(Text):

def __init__(self, parent, *args, **kwargs):
    Text.__init__(self, master=parent, wrap='word', *args, **kwargs)

    # event binding on resize because text.count method for displaylines returns a wrong number when widget is instantiated
    self.bind('<Configure>', self.update_size)

    bindtags = list(self.bindtags())
    bindtags.insert(2, "custom")
    self.bindtags(tuple(bindtags))
    self.bind_class("custom", "<Key>", self.update_size)

def update_size(self, event):

    if self.winfo_width() > 1:
        self.unbind('<Configure>')

    displaylines = self.count("1.0", "end", "displaylines")
    self.config(height=displaylines)

root = Tk()

dynamic_text_1 = ResizingText(root, width=60)
dynamic_text_1.insert('1.0', "Longer text that is long enough to be wrapped into multiple display lines")
dynamic_text_1.grid(column=0, row=0)
# this text widget does not behave as expected:
# no resizing after user inputs a line break or a line long enough to be wrapped

dynamic_text_2 = ResizingText(root, width=60)
dynamic_text_2.insert('1.0', "Longer text that is long enough to be wrapped into multiple display lines")
dynamic_text_2.grid(column=0, row=1)
# this text widget behaves as expected

root.mainloop()
Community
  • 1
  • 1
Sam
  • 191
  • 1
  • 7
  • I run your program and I can not reproduce your problem because whatever the length of the text I insert is, the heights are re-sized automatically to fit and wrap the text. – Billal Begueradj Jun 21 '16 at 10:06
  • @BillalBEGUERADJ: thanks for your feedback, the problem concerns only resizing when the user inputs a line break or a line that gets wrapped. And the text widget last created behaves as i want. Also: i'm on Mac. I updated the question to clarify these points. – Sam Jun 21 '16 at 11:52

1 Answers1

1

I solved this by configuring the bindtags and resetting the event binding on Key-events every time the text widget gets the focus (event binding on FocusIn).

Probably not a clean solution, but it seems to work fine:

from tkinter import *

class ResizingText(Text):
    def __init__(self, parent, *args, **kwargs):
        Text.__init__(self, master=parent, wrap='word', *args, **kwargs)

        # event binding on resize because text.count method for displaylines returns a wrong number when widget is instantiated
        self.bind('<Configure>', self.update_size)
        self.configure_bindtags(event=None)

        # solution: additional binding that resets the binding for <Key> events whenever the text widget gets the focus
        self.bind('<FocusIn>', self.configure_bindtags)

    def configure_bindtags(self, event):
        bindtags = list(self.bindtags())
        bindtags.insert(2, "custom")
        self.bindtags(tuple(bindtags))
        self.bind_class("custom", "<Key>", self.update_size)

    def update_size(self, event):
        if self.winfo_width() > 1:
            self.unbind('<Configure>')

        displaylines = self.count("1.0", "end", "displaylines")
        self.config(height=displaylines)

root = Tk()

dynamic_text_1 = ResizingText(root, width=60)
dynamic_text_1.insert('1.0', "Longer text that is long enough to be wrapped into multiple display lines")
dynamic_text_1.grid(column=0, row=0)

dynamic_text_2 = ResizingText(root, width=60)
dynamic_text_2.insert('1.0', "Longer text that is long enough to be wrapped into multiple display lines")
dynamic_text_2.grid(column=0, row=1)

root.mainloop()
Sam
  • 191
  • 1
  • 7