gpt4 book ai didi

python - Tkinter.text - 如何计算动态字符串的高度?

转载 作者:太空宇宙 更新时间:2023-11-04 05:02:04 24 4
gpt4 key购买 nike

我有一个 Text 小部件,它包含一个包含 \n 字符(多行)的自定义字符串。

小部件放置在一个垂直的 panedwindow 中,我想调整 panedwindow 的窗框以在 Text 中显示整个字符串小部件。

该字符串本质上是动态的(这意味着,它正在通过我的应用程序中的其他方法进行更新)。

由于 Text 小部件配置有 wrap='word',我如何计算以像素为单位的字符串高度以相应地调整窗扇?

我尝试在之后使用 text.dlineInfo('end -1c')[1] + text.dlineinfo('end -1c')[3] (用于行的 y 坐标 + 高度)字符串已加载到小部件。问题是,如果最后一行不可见,则 dlineinfo 返回 none

我还尝试使用 Font.measure 例程,但这不包括 Text 小部件的换行方面。

这是一个最小的、完整的和可验证的例子:

import tkinter

from tkinter import scrolledtext

class GUI():
def __init__(self, master):
self.master = master

self.body_frame = tkinter.PanedWindow(self.master, orient='vertical', sashwidth=4)
self.body_frame.pack(expand=1, fill='both')

self.canvas_frame = tkinter.Frame(self.body_frame)
self.description_frame = tkinter.Frame(self.body_frame)
self.body_frame.add(self.canvas_frame, sticky='nsew')
self.body_frame.add(self.description_frame, sticky='nsew')

tkinter.Button(self.canvas_frame, text='Update Text', command = lambda : self.update_text("""
A very long string with new lines
A very long string with new lines
A very long string with new lines
A very long string with new lines
A very long string with new lines
A very long string with new lines
""")).pack(fill='x')

self.field_description = scrolledtext.ScrolledText(self.description_frame, width=20, wrap='word')
self.field_description.pack(expand=1, fill='both')

self.master.update()
self.body_frame.sash_place(0,0,self.body_frame.winfo_height() - 50) # force sash to be lower

def update_text(self, description):
self.field_description.delete('1.0', 'end')
self.field_description.insert('1.0', description)

height = self.body_frame.winfo_height()
lastline_index = self.field_description.index('end - 1c')
text_height = self.field_description.dlineinfo(lastline_index)[1] + \
self.field_description.dlineinfo(lastline_index)[3]
self.body_frame.sash_place(0, 0, height - text_height)

root = tkinter.Tk()

my_gui = GUI(root)
root.mainloop()

最佳答案

我不知道有任何内置方法可以返回 tkinter Text 中的总行数(包括换行) > 小部件。

但是,您可以通过比较文本小部件中完整字符串的长度与文本小部件的确切宽度(减去填充)来手动计算此数字。下面的 LineCounter 类就是这样做的:

Screenshot

# python 2.x
# from tkFont import Font

# python 3.x
from tkinter.font import Font

class LineCounter():
def __init__(self):
"""" This class can count the total number of lines (including wrapped
lines) in a tkinter Text() widget """

def count_total_nb_lines(self, textWidget):
# Get Text widget content and split it by unbroken lines
textLines = textWidget.get("1.0", "end-1c").split("\n")
# Get Text widget wrapping style
wrap = text.cget("wrap")
if wrap == "none":
return len(textLines)
else:
# Get Text widget font
font = Font(root, font=textWidget.cget("font"))
totalLines_count = 0
maxLineWidth_px = textWidget.winfo_width() - 2*text.cget("padx") - 1
for line in textLines:
totalLines_count += self.count_nb_wrapped_lines_in_string(line,
maxLineWidth_px, font, wrap)
return totalLines_count

def count_nb_wrapped_lines_in_string(self, string, maxLineWidth_px, font, wrap):
wrappedLines_count = 1
thereAreCharsLeftForWrapping = font.measure(string) >= maxLineWidth_px
while thereAreCharsLeftForWrapping:
wrappedLines_count += 1
if wrap == "char":
string = self.remove_wrapped_chars_from_string(string,
maxLineWidth_px, font)
else:
string = self.remove_wrapped_words_from_string(string,
maxLineWidth_px, font)
thereAreCharsLeftForWrapping = font.measure(string) >= maxLineWidth_px
return wrappedLines_count

def remove_wrapped_chars_from_string(self, string, maxLineWidth_px, font):
avgCharWidth_px = font.measure(string)/float(len(string))
nCharsToWrap = int(0.9*maxLineWidth_px/float(avgCharWidth_px))
wrapLine_isFull = font.measure(string[:nCharsToWrap]) >= maxLineWidth_px
while not wrapLine_isFull:
nCharsToWrap += 1
wrapLine_isFull = font.measure(string[:nCharsToWrap]) >= maxLineWidth_px
return string[nCharsToWrap-1:]

def remove_wrapped_words_from_string(self, string, maxLineWidth_px, font):
words = string.split(" ")
nWordsToWrap = 0
wrapLine_isFull = font.measure(" ".join(words[:nWordsToWrap])) >= maxLineWidth_px
while not wrapLine_isFull:
nWordsToWrap += 1
wrapLine_isFull = font.measure(" ".join(words[:nWordsToWrap])) >= maxLineWidth_px
if nWordsToWrap == 1:
# If there is only 1 word to wrap, this word is longer than the Text
# widget width. Therefore, wrapping switches to character mode
return self.remove_wrapped_chars_from_string(string, maxLineWidth_px, font)
else:
return " ".join(words[nWordsToWrap-1:])

使用示例:

import tkinter as tk

root = tk.Tk()
text = tk.Text(root, wrap='word')
text.insert("1.0", "The total number of lines in this Text widget is " +
"determined accurately, even when the text is wrapped...")
lineCounter = LineCounter()
label = tk.Label(root, text="0 lines", foreground="red")

def show_nb_of_lines(evt):
nbLines = lineCounter.count_total_nb_lines(text)
if nbLines < 2:
label.config(text="{} line".format(nbLines))
else:
label.config(text="{} lines".format(nbLines))

label.pack(side="bottom")
text.pack(side="bottom", fill="both", expand=True)
text.bind("<Configure>", show_nb_of_lines)
text.bind("<KeyRelease>", show_nb_of_lines)

root.mainloop()

在您的特定情况下ScrolledText 中环绕文本的高度可以在 update_text() 中确定,如下所示:

from tkinter.font import Font
lineCounter = LineCounter()
...
class GUI():
...
def update_text(self, description):
...
nbLines = lineCounter.count_total_nb_lines(self.field_description)
font = Font(font=self.field_description.cget("font"))
lineHeight = font.metrics("linespace")
text_height = nbLines * lineHeight
...

关于python - Tkinter.text - 如何计算动态字符串的高度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45470163/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com