如何对齐组合框下拉列表中的字符?

How to justify the characters in drop-down list of a Combobox?

如何证明 ttk.Combobox 下拉部分列出的值?我试过 justify='center' 但这似乎只能配置所选项目。也可以使用资源 link 如果有,我找不到它。

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
    import tkinter.ttk as ttk
except ImportError:
    import Tkinter as tk
    import ttk


if __name__ == '__main__':
    root = tk.Tk()
    cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2))
    cbb.pack()
    root.mainloop()

(编辑:请注意,此解决方案适用于 Tcl/Tk 8.6.5 及更高版本。@CommonSense 指出某些 tkinter 安装可能尚未修补, 而这个解决方案将不起作用)。

在 Tcl 中(我不知道 python,所以 python 中的一个人可以编辑问题)。

组合框是 'entry' 小部件和 'listbox' 小部件的合并。有时要进行所需的配置更改,您需要直接访问内部小部件。

Tcl:

% ttk::combobox .cb -values [list a abc def14 kjsdf]
.cb
% pack .cb
% set pd [ttk::combobox::PopdownWindow .cb]
.cb.popdown
% set lb $pd.f.l
.cb.popdown.f.l
% $lb configure -justify center

Python:

cb = ttk.Combobox(value=['a', 'abc', 'def14', 'kjsdf'])

cb.pack()
pd = cb.tk.call('ttk::combobox::PopdownWindow', cb)

lb = cb.tk.eval('return {}.f.l'.format(pd))

cb.tk.eval('{} configure -justify center'.format(lb))

一些注意事项。 ttk::combobox 的内部结构可能会发生变化。 不太可能,不会很快,但在未来,硬编码 .f.l 可能会改变。

ttk::combobox::PopdownWindow 将在调用时强制创建列表框。更好的方法是将居中调整放入 一个过程并在映射 combobox/listbox 时调用该过程。

这将 运行 用于所有组合框,您需要检查参数 在 proc 中确保这是您要调整的组合框。

proc cblbhandler { w } {
   if { $w eq ".cb" } {
     set pd [ttk::combobox::PopdownWindow $w]
     set lb $pd.f.l
     $lb configure -justify center
   }
}

bind ComboboxListbox <Map> +[list ::cblbhandler %W]

这是一种接近您想要的纯粹 Python 方式。下拉列表中的所有项目都经过调整以适应 Combobox 的宽度(或者将使用默认值)。

更新

我的答案的初始版本不太正确的部分原因是代码假定使用的是固定宽度的字体。至少在我的测试平台上不是这种情况,所以我修改了代码以实际测量像素值的宽度而不是整个字符,并且基本上做它最初做的事情,但以字符串长度测量的那些单位。

import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk

class CenteredCombobox(ttk.Combobox):
    DEFAULT_WIDTH = 20  # Have read that 20 is the default width of an Entry.

    def __init__(self, master=None, **kwargs):
        values = kwargs.get('values')
        if values:
            entry = ttk.Entry(None)  # Throwaway for getting the default font.
            font = tkFont.Font(font=entry['font'])
            space_width = font.measure(' ')

            entry_width = space_width * kwargs.get('width', self.DEFAULT_WIDTH)
            widths = [font.measure(str(value)) for value in values]
            longest = max(entry_width, *widths)

            justified_values = []
            for value, value_width in zip(values, widths):
                space_needed = (longest-value_width) / 2
                spaces_needed = int(space_needed / space_width)
                padding = ' ' * spaces_needed
                justified_values.append(padding + str(value))

            kwargs['values'] = tuple(justified_values)

        super().__init__(master, **kwargs)


root = tk.Tk()
ccb = CenteredCombobox(root, justify='center', width=10, values=('I', 'XLII', 'MMXVIII'))
ccb.pack()

root.mainloop()

在深入研究 combobox.tcl 源代码后,我得出了 ttk.Combobox 的以下子类。 JustifiedCombobox 几乎恰好在 1 弹出列表首次创建和自定义然后显示之后 调整弹出列表的项目。创建弹出列表后,将 self.justify 值设置为有效值将在弹出列表首次 显示 后几乎立即自定义对齐方式。享受:

try:                        # In order to be able to import tkinter for
    import tkinter as tk    # either in python 2 or in python 3
    from tkinter import ttk
except:
    import Tkinter as tk
    import ttk


class JustifiedCombobox(ttk.Combobox):
    """
    Creates a ttk.Combobox widget with its drop-down list items
    justified with self.justify as late as possible.
    """

    def __init__(self, master, *args, **kwargs):
        ttk.Combobox.__init__(self, master, *args, **kwargs)
        self.justify = 'center'


    def _justify_popdown_list_text(self):
        self._initial_bindtags = self.bindtags()
        _bindtags = list(self._initial_bindtags)
        _index_of_class_tag = _bindtags.index(self.winfo_class())
        # This dummy tag needs to be unique per object, and also needs
        # to be not equal to str(object)
        self._dummy_tag = '_' + str(self)
        _bindtags.insert(_index_of_class_tag + 1, self._dummy_tag)
        self.bindtags(tuple(_bindtags))
        _events_that_produce_popdown = tuple([  '<KeyPress-Down>',
                                                '<ButtonPress-1>',
                                                '<Shift-ButtonPress-1>',
                                                '<Double-ButtonPress-1>',
                                                '<Triple-ButtonPress-1>',
                                                ])
        for _event_name in _events_that_produce_popdown:
            self.bind_class(self._dummy_tag, _event_name,
                                                self._initial_event_handle)


    def _initial_event_handle(self, event):
        _instate = str(self['state'])
        if _instate != 'disabled':
            if event.keysym == 'Down':
                self._justify()
            else:
                _ = self.tk.eval('{} identify element {} {}'.format(self,
                                                            event.x, event.y))
                __ = self.tk.eval('string match *textarea {}'.format(_))
                _is_click_in_entry = bool(int(__))
                if (_instate == 'readonly') or (not _is_click_in_entry):
                    self._justify()


    def _justify(self):
        self.tk.eval('{}.popdown.f.l configure -justify {}'.format(self,
                                                                self.justify))
        self.bindtags(self._initial_bindtags)


    def __setattr__(self, name, value):
        self.__dict__[name] = value
        if name == 'justify':
            self._justify_popdown_list_text()


def select_handle():
    global a
    _selected = a['values'][a.current()]
    if _selected in ("left", "center", "right"):
        a.justify = _selected


if __name__ == '__main__':
    root = tk.Tk()
    for s in ('normal', 'readonly', 'disabled'):
        JustifiedCombobox(root, state=s, values=[1, 2, 3]).grid()
    a = JustifiedCombobox(root, values=["Justify me!", "left", "center", "right"])
    a.current(0)
    a.grid()
    a.bind("<<ComboboxSelected>>", lambda event: select_handle())
    root.mainloop()

1 它基本上利用了 bindtag 事件队列。由于能够 .

,这在很大程度上是可能的

我有一个单行解决方案。在声明 ttk.Combobox 之后使用 .option_add() 方法。示例:

cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2)) # original
cbb.option_add('*TCombobox*Listbox.Justify', 'center')       # new line added