【问题标题】:python Tkinter - How to count the quantity of characters can be shown in expanding Label?python Tkinter - 如何计算扩展标签中可以显示的字符数量?
【发布时间】:2018-08-28 16:07:50
【问题描述】:

我的窗口上有一些小部件。标签 lb3 随着我的窗口展开。我想从长数组中放一些字符。因此,如果窗口很大,则必须有更多的字符,如果窗口变小,则字符会更少。 因此我需要知道字符中标签的当前高度和宽度。 我的问题有一个例子:

import Tkinter as tk
import tkFont
import sys
import os    

last_event_H = 0
last_event_W = 0
LONG_ARRAY = ''

# when window sized
def sizing(event):
    global last_event_H
    global last_event_W
    global LONG_ARRAY
    if (event.width == last_event_W and event.height == last_event_H):
        return
    last_event_H = event.height
    last_event_W = event.width

    width_in_chars = lb3['width']
    height_in_chars = lb3['height']
    first_shown = int(lb1_text.get())
    lb3_text.set(LONG_ARRAY[first_shown:first_shown + width_in_chars * height_in_chars])
    lb2_text.set(LONG_ARRAY[first_shown + width_in_chars * height_in_chars] + ' ...')

class WrappingLabel(tk.Label):
    '''a type of Label that automatically adjusts the wrap to the size'''
    def __init__(self, master=None, **kwargs):
        tk.Label.__init__(self, master, **kwargs)
        self.bind('<Configure>', lambda e: self.config(wraplength=self.winfo_width()))

if __name__ == '__main__':
    root = tk.Tk()
    root.geometry("1280x640")
    dFont=tkFont.Font(family="Arial", size=30) # fixed Font
    LONG_ARRAY = 'a' * 100000 # of symbols

    tb_date = tk.Entry(root, font=dFont)
    tb_date.grid(column=0, row=0, columnspan=3, sticky=tk.NSEW)
    bt_find = tk.Button(root, text="...", font=dFont)
    bt_find.grid(column=9, row=0, columnspan=2, sticky=tk.NSEW)

    lb1_text = tk.StringVar()
    lb1_text.set("1")
    lb1 = tk.Label(root, textvariable=lb1_text, width=10, font=dFont, anchor=tk.NW)
    lb1.grid(column=0, row=1, sticky=tk.NSEW)

    lb2_text = tk.StringVar()
    lb2 = tk.Label(root, textvariable=lb2_text, width=10, font=dFont, anchor=tk.NW)
    lb2.grid(column=1, row=10, columnspan=9, sticky=tk.NSEW)

    lb3_text = tk.StringVar()
    lb3 = WrappingLabel(root, textvariable=lb3_text, font=dFont, anchor=tk.NW, justify=tk.LEFT)
    lb3.grid(column=1, row=1, columnspan=9, rowspan=9, sticky=tk.NSEW)

    for x in range(11):
      tk.Grid.columnconfigure(root, x, weight=1)
    for y in range(11):
      tk.Grid.rowconfigure(root, y, weight=1)
    root.bind("<Configure>", sizing)

    width_in_chars = lb3['width']
    height_in_chars = lb3['height']
    print width_in_chars, height_in_chars # !!!!!!!!!!!!!
    print "-------"
    lb3_text.set(LONG_ARRAY[:width_in_chars * height_in_chars])
    lb2_text.set(LONG_ARRAY[width_in_chars * height_in_chars] + ' ...') # last shown character
    root.mainloop()

lb3 的 width 和 height 成员现在设置为 0。 因为它可以扩展。还有其他方法吗?

【问题讨论】:

  • widthheight 选项只会反映您配置标签的值,而不是小部件的实际大小。无论如何,实际适合的字符数取决于这些字符是什么,除非你给它一个等宽字体。如果我正确理解您的要求,我相信解决方案是使用 Text 小部件,设置为 disabledreadonly 模式,除非您更改内容 - 这将简单地剪切任何不适合,而不是像标签那样调整自己的大小。
  • 听起来您的问题实际上可能与此重复:Display three dots in the end of a Tkinter Label text

标签: python tkinter


【解决方案1】:

如果您想确切知道可以容纳多少个字符,首先要做的是获取标签的大小。这需要在小部件显示后发生,因为在此之前无法知道它的宽度。您可以通过调用 winfo_reqwidth 预先完成此操作,但该数字可能大于或小于实际宽度,具体取决于您用于 packplacegrid 的选项。

一旦知道最大尺寸,就可以使用字体对象的font_measure 方法来计算字符串中的像素数。然后编写一个循环来计算出适合的最大字符数是一件简单的事情。

可以在此答案中找到一个示例,说明如何为太长的标签动态添加“...”:https://stackoverflow.com/a/51144251/7432

【讨论】:

    【解决方案2】:

    感谢@BryanOakley 提供有用的链接

    我的解决方案版本如下 (如果有人可以做得更好,请回复)

    import Tkinter as tk
    import tkFont
    import sys
    import os
    import bisect
    
    LONG_ARRAY = ''
    
    class KeyList(object):
        # bisect doesn't accept a key function, so we build the key into our sequence.
        def __init__(self, l, key):
            self.l = l
            self.key = key
        def __len__(self):
            return len(self.l)
        def __getitem__(self, index):
            return self.key(self.l[index], index)
    
    class WrappingLabel(tk.Label):
        '''a type of Label that automatically adjusts the wrap to the size'''
        def __init__(self, master=None, **kwargs):
            tk.Label.__init__(self, master, **kwargs)
            self.bind('<Configure>', self.fit)
            if (not hasattr(self, "original_text")):
                # preserve the original text so we can restore it if the widget grows.
                self.original_text = self["text"]
            self.font = tkFont.nametofont(self["font"])
            self.font_height = 48.88 # self.font.metrics('linespace')
    
        def fit(self, event):
            max_width = event.width
            max_height = event.height / self.font_height # rough n_lines
            text = LONG_ARRAY[:2000] # TODO !!! self.original_text
            actual_width = self.font.measure(text)
            if (actual_width  > max_width * max_height):
                # the original text won't fit. Keep shrinking until it does
                i = bisect.bisect_left(KeyList(text, key=lambda x,i: self.font.measure(text[:i]+ '...')), max_width * max_height)
                lb3_text.set(text[:i] + '...') # TODO !!! self.original_text
            return self.config(wraplength=self.winfo_width())
    
    if __name__ == '__main__':
        root = tk.Tk()
        root.geometry("1280x640")
        dFont=tkFont.Font(family="Arial", size=30) # fixed Font
        LONG_ARRAY = 'a' * 100000 # of symbols
    
        root.grid_columnconfigure(0, weight=1)
        frame = tk.Frame(root)
        tk.Grid.rowconfigure(root, 0, weight=1)
        tk.Grid.columnconfigure(root, 0, weight=1)
        frame.grid(row=0, column=0, sticky=tk.NSEW)
    
        tb_date = tk.Entry(frame, font=dFont)
        tb_date.grid(column=0, row=0, columnspan=3, sticky=tk.NSEW)
        bt_find = tk.Button(frame, text="...", font=dFont)
        bt_find.grid(column=9, row=0, columnspan=2, sticky=tk.NSEW)
    
        lb1_text = tk.StringVar()
        lb1_text.set("1")
        lb1 = tk.Label(frame, textvariable=lb1_text, width=10, font=dFont, anchor=tk.NW)
        lb1.grid(column=0, row=1, sticky=tk.NSEW)
    
        lb3_text = tk.StringVar() 
        lb3 = WrappingLabel(frame, textvariable=lb3_text, font=dFont, anchor=tk.NW, justify=tk.LEFT)
        #lb3 = tk.Text(frame, font=dFont, state=tk.DISABLED, wrap=tk.CHAR) # bg=frame["bg"], fg='black', 
        lb3.grid(column=1, row=1, rowspan=2, columnspan=9, sticky=tk.NSEW)
    
        lb2_text = tk.StringVar()
        lb2 = tk.Label(frame, textvariable=lb2_text, width=10, font=dFont, anchor=tk.NW)
        lb2.grid(column=1, row=4, columnspan=9, rowspan=2, sticky=tk.NSEW)
        lb2_text.set('................................................')
    
        for y in range(11):
            tk.Grid.columnconfigure(frame, y, weight=1)
        for x in range(5):
            tk.Grid.rowconfigure(frame, x, weight=1)
        lb3_text.set(LONG_ARRAY[:2000])
        #lb3.insert(tk.INSERT, LONG_ARRAY[:2000])
        #lb3.insert(tk.END, "")
        root.mainloop()
    

    【讨论】:

    • 这不是一个好的通用解决方案,因为您对字体高度进行了硬编码。你为什么这样做?将实际宽度与max_width * max_height 进行比较也没有任何意义。由于event.height 可能是字体的两倍或一半,因此在某些情况下您可能会得到非常意想不到的结果。
    • 感谢您的 cmets。在我的情况下, actual_width 是整个文本宽度,因为它是单行的并且由字符包裹。不幸的是,我找不到计算实际行数的正确方法,所以我只是找到了系数,它给了我最好的 tk.Text 填充。我希望有人可以改进它,如果找出方法。
    猜你喜欢
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-12
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多