Python Tkinter - Как подсчитать количество символов, которое можно показать в расширяющейся метке? - PullRequest
0 голосов
/ 28 августа 2018

У меня есть несколько виджетов в моем окне. Метка 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 теперь установлены в 0. Потому что он способен расширяться. Есть ли другой способ?

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Спасибо @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()
0 голосов
/ 28 августа 2018

Если вы хотите точно знать, сколько символов поместится, первое, что нужно сделать, это получить размер метки. Это должно произойти после того, как виджет отобразится, так как его ширина не может быть известна, пока это не произойдет. Вы можете сделать это заранее, позвонив по номеру winfo_reqwidth, хотя это число может быть больше или меньше фактической ширины в зависимости от параметров, которые вы используете для pack, place или grid.

Как только вы знаете максимальный размер, вы можете использовать метод font_measure объекта шрифта для вычисления количества пикселей в строке. Тогда просто написать цикл, который определит максимальное количество символов, которое будет соответствовать.

Пример, показывающий, как динамически добавлять «...» для слишком длинной метки, можно найти в этом ответе: https://stackoverflow.com/a/51144251/7432

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...