如何使单个 Tkinter Listbox 元素出现换行符?

Posted

技术标签:

【中文标题】如何使单个 Tkinter Listbox 元素出现换行符?【英文标题】:How can I make a single Tkinter Listbox element appear with line breaks? 【发布时间】:2017-07-29 17:42:15 【问题描述】:

我有一个可用的 Tkinter.Listbox 对象,但我想对其进行设置,以便它的元素可以有回车,而不必以某种方式设置多个链接项。

例如,如果我想生成一个包含如下所示项目的选择窗格..

    # Here are four elements for the selector Listbox..
lb_items = ('mama', 'luigi', 'my birds', \
            'this is a single element\n spanning two lines!')
    # This generates and displays the selector window..
tk_selector = SingleSelect(lb_items, "TEST SELECTOR")
tk_selector.run_selector()

..如果我能让输出看起来像这个模型就好了..

..而不是它实际生成的,就是这个..

列表框似乎完全忽略了'\n' 和带有换行符的三引号字符串;如果使用\n,则既不出现字符也不出现换行符。

是否可以有单独的、可选择的列表框元素以换行符出现?

我也会对自动换行选项感到满意,但经过一番查找,我在 ListboxTk 中找不到任何此类选项。

我可能会通过将多行字符串制作成多个元素来伪造效果,然后将其设置为在调用其中任何一个时返回整行,但对于可能有一个简单解决方案的东西来说,这感觉像是一种考验。

【问题讨论】:

【参考方案1】:

就像 Bryan Oakley 所说,Listbox 中没有对回车的原生支持,所以我尝试构建我在问题中提到的“假”版本,结果证明它并没有那么难。

我的解决方案是在将每个“原始”字符串插入Listbox 之前对其进行解析,使用splitlines 将字符串分成单独的行,记录行数以及列表框元素滚动中的哪些索引对应于哪个完整的输入字符串,然后在列表框的选择更改时使用 Listbox.bind('<<ListboxSelect>>', self._reselection_fxn) 选择所有部分。

下面有一个删节和注释的示例,或者您可以see my complete, working, even-more-heavily annotated code here。
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.

如果您调整此代码以匹配您自己的代码并将其放入现有的列表框设置中,它应该可以让您模拟回车。它与直接在Listbox 中的原生换行或\n-parsing 函数不同,但它的作用基本相同。

要获取与当前选择对应的整个字符串,只需将_recall 绑定到您想要返回的任何输入或事件。在这种情况下,当没有选择任何项目时,它会返回None

考虑到所需效果的复杂性,这也是一项繁重的工作,而且可能并不适合所有情况。但至少你可以做到。

【讨论】:

【参考方案2】:

列表框项目不能分布在多行或多行中。

【讨论】:

以上是关于如何使单个 Tkinter Listbox 元素出现换行符?的主要内容,如果未能解决你的问题,请参考以下文章

Python3 Tkinter基础 Listbox Button 点击按钮删除选中的单个内容

Python3 Tkinter基础 Listbox for循环与insert 将一个列表中元素添加到Listbox中

Tkinter Listbox 以蓝色突出显示一个元素,但在另一个元素周围放置黑色边框?

Python中tkinter控件中的Listbox控件详解

如何清除 Tkinter ListBox Python

Python 的Tkinter 如何画线?线框?