How can I make a single Tkinter Listbox element appear with line breaks? How can I make a single Tkinter Listbox element appear with line breaks? tkinter tkinter

How can I make a single Tkinter Listbox element appear with line breaks?


Like Bryan Oakley said, there is no native support in Listbox for carriage returns, so I tried building the 'fake' version I mentioned in the question, and it turns out that it isn't really that hard.

My solution is to parse each 'raw' string before inserting them into the Listbox, breaking strings into individual lines using splitlines, recording the number of lines and which indices in the Listbox's element roll correspond to which unbroken input string, and then selecting all the parts whenever the Listbox's selection changes using Listbox.bind('<<ListboxSelect>>', self._reselection_fxn).


class Multiline_Single_Selector(object):      ## Go ahead and choose a better class name than this, too. :/    def __init__(self, itemlist, ..):              # ..        lb_splitlines = self._parse_strings(itemlist)           # ^ splits the raw strings and records their indices.          #  returns the split strings as a list of Listbox elements.        self.my_Listbox.insert(0, *lb_splitlines)           # ^ put the converted strings into the Listbox..        self.my_Listbox.bind('<<ListboxSelect>>', self._reselect)          # ^ Whenever the Listbox selection is modifed, it triggers the          #  <<ListboxSelect>> event. Bind _reselect to it to determine          #  which lines ought to be highlighted when the selection updates.              # ..    def _parse_strings(self, string_list):        '''Accepts a list of strings and breaks each string into a series of lines,logs the sets, and stores them in the item_roster and string_register attributes.Returns the split strings to be inserted into a Listbox.'''        self.index_sets = index_sets = []          # ^ Each element in this list is a tuple containing the first and last          #  Listbox element indices for a set of lines.         self.string_register = register = {}          # ^ A dict with a whole string element keyed to the index of the its          #  first element in the Listbox.        all_lines = []           # ^ A list of every Listbox element. When a string is broken into lines,          #  the lines go in here.        line_number = 0        for item in string_list:             lines = item.splitlines()            all_lines.extend(lines) # add the divided string to the string stack            register[line_number] = item                # ^ Saves this item keyed to the first Listbox element it's associated                #  with. If the item is selected when the Listbox closes, the original                 #  (whole) string is found and returned based on this index number.            qty = len(lines)            if qty == 1: # single line item..                index_sets.append((line_number, line_number))            else: # multiple lines in this item..                element_range = line_number, line_number + qty - 1                   # ^ the range of Listbox indices..                index_sets.extend([element_range] * qty)                   # ^ ..one for each element in the Listbox.            line_number += qty # increment the line number.        return all_lines    def _reselect(self, event=None):        "Called whenever the Listbox's selection changes."        selection = self.my_Listbox.curselection() # Get the new selection data.        if not selection: # if there is nothing selected, do nothing.            return        lines_st, lines_ed = self.index_sets[selection[0]]            # ^ Get the string block associated with the current selection.        self.my_Listbox.selection_set(lines_st, lines_ed)             # ^ select all lines associated with the original string.    def _recall(self, event=None):        "Get the complete string for the currently selected item."        selection = self.my_Listbox.curselection()        if selection: # an item is selected!            return self.string_register[selection[0]]        return None   # no item is selected.

If you adjust this code to match your own and drop it into an existing Listbox setup, it ought to let you simulate carriage returns. It's not the same as a native line-wrap or \n-parsing function directly in Listbox, but it does basically the same thing.

To get the whole string corresponding to the current selection, just bind _recall to whatever input or event you want to have return it. In this case, it returns None when no item is selected.

It's also kind of a lot of work considering the sophistication of the desired effect and it may not be appropriate to every situation. But at least you can do it.


It is not possible for a listbox item to be spread across more than one line or row.