简体   繁体   English

如何调整组合框下拉列表中的字符?

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

How to justify the values listed in drop-down part of a ttk.Combobox ? 如何证明ttk.Combobox的下拉部分中列出的值的ttk.Combobox I have tried justify='center' but that seems to only configure the selected item. 我尝试过justify='center'但这似乎只配置所选的项目。 Could use a resource link too if there is, I couldn't find it. 如果有的话,也可以使用资源链接,我找不到它。

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

(Edit: Note that this solution works for Tcl/Tk versions 8.6.5 and above. @CommonSense notes that some tkinter installations may not be patched yet, and this solution will not work). (编辑:请注意,该解决方案适用于Tcl / Tk版本8.6.5及更高版本。@CommonSense指出,某些tkinter安装可能尚未打补丁,并且该解决方案将不起作用)。

In Tcl ( I don't know python, so one of the python people can edit the question). 在Tcl中(我不了解python,因此其中一个python人员可以编辑问题)。

A combobox is an amalgamation of an 'entry' widget and a 'listbox' widget. 组合框是“条目”小部件和“列表框”小部件的组合。 Sometimes to make the configuration changes you want, you need to access the internal widgets directly. 有时要进行所需的配置更改,您需要直接访问内部窗口小部件。

Tcl: 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))

Some caveats. 一些警告。 The internals of ttk::combobox are subject to change. ttk::combobox的内部结构可能会发生变化。 Not likely, not anytime soon, but in the future, the hard-coded .fl could change. 不太可能,不会很快,但是在将来,硬编码的.fl可能会更改。

ttk::combobox::PopdownWindow will force the creation of the listbox when it is called. ttk::combobox::PopdownWindow将在列表框被调用时强制创建。 A better method is to put the centering adjustment into a procedure and call that procedure when the combobox/listbox is mapped. 更好的方法是将居中调整放入过程中,并在映射组合框/列表框时调用该过程。

This will run for all comboboxes, you will need to check the argument in the proc to make sure that this is the combobox you want to adjust. 这将在所有组合框上运行,您需要检查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]

Here's one pure Python way that gets close to what you want. 这是一种接近您想要的纯Python方式。 The items in the dropdown list all get justified to fit within the Combobox 's width (or a default value will be used). 下拉列表中的所有项目都将被证明适合组合Combobox的宽度(或将使用默认值)。

Update 更新

Part of the reason the initial version of my answer wasn't quite right was because the code assumed that a fixed-width font was being used. 我的答案的初始版本不太正确的部分原因是因为代码假定正在使用固定宽度的字体。 That's not the case on my test platform at least, so I've modified the code to actually measure the width of values in pixels instead of whole characters, and do essentially what it did originally, but in those units of string-length measure. 至少在我的测试平台上不是这种情况,因此我修改了代码,以实际上以像素而不是整个字符为单位来测量值的宽度,并且基本上执行了它最初的工作,但是以那些字符串长度测量为单位。

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

I have a one-liner solution. 我有一个一线解决方案。 Use the .option_add() method after declaring ttk.Combobox . 在声明ttk.Combobox之后使用.option_add()方法。 Example: 例:

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

After digging through combobox.tcl source code I've come up with the following subclass of ttk.Combobox . 在浏览combobox.tcl源代码之后,我提出了ttk.Combobox的以下子类。 JustifiedCombobox justifies the pop-down list's items almost precisely after 1 pop-down list's been first created & customized and then displayed. JustifiedCombobox在首先创建并自定义1个下拉列表然后显示之后,几乎完全可以对下拉列表的项目进行JustifiedCombobox After the pop-down list's been created, setting self.justify value to a valid one will again, customize the justification almost right after the pop-down list's first been displayed . 创建弹出列表后,将self.justify值设置为有效值将再次在弹出列表首次显示后立即自定义对齐。 Enjoy: 请享用:

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 It basically makes use of bindtag event queue. 1它基本上利用bindtag事件队列。 This was mostly possible thanks to being able to creating a custom bindtag . 由于能够创建自定义的bindtag ,因此这几乎是可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM