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

2024-01-08

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

该小部件放置在垂直方向内panedwindow我想调整panedwindow的窗框显示整个字符串Text widget.

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

As the Text小部件配置为wrap='word',如何计算字符串高度(以像素为单位)以相应地调整窗扇?

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

我也尝试使用Font.measure例程,但这不包括换行方面Text widget.

这是一个最小、完整且可验证的示例:

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 widget.

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

# 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 
        ...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tkinter.text - 如何计算动态字符串的高度? 的相关文章

随机推荐

  • 需要帮助阻止 MSXML 添加命名空间

    我正在使用 MSXML 4 生成以下 xml 字符串
  • Asp.net mvc 授权属性与参数集成

    我想用一个 Authorize 属性在操作上的方式如下 Authorize Roles Administrator or UserId id public ActionResult Edit int id 现在我正在使用这样的逻辑 publ
  • 使用 R 的过程 GLM (SAS)

    我需要测试应该在奶牛遗传评估模型中包含哪些效应 在 SAS 中我会使用 proc GLM SAS 代码为 data paula1 set paula0 proc glm class year herd season model milk y
  • 使用 CUDA 进行行列式计算 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何库或免费可用的代码可以计算a的行列式small 6x6 双精度矩阵完全在 GPU 上 这是计
  • 使用 java 8 玩框架 1.x

    我怎样才能使play framework 1 x与 Java 8 一起工作吗 播放框架版本1 2 5 3 Java version 1 8 0 Java TM SE Runtime Environment build 1 8 0 b132
  • 使用 awk 或 cut 或 perl 选择特定列

    我需要从制表符分隔文件中选择第七列 例如 cat filename awk print 7 问题是第四列中的数据有多个值 中间有空白 示例 以下输出中的最后一行 user Adminis FL vol Design 0 1 group 0
  • 用点图案填充矩形

    下面的图像有一些特定的图案 在第一张图片上最明显 我有一些用小圆圈标记并用线连接的点 他们制作一些网状图案 有些点是错误的并且不适合模式 在第一张图像上标记 目标是填充整个用红色标记的矩形 矩形是从极值点创建的 图案坐标系中具有极值坐标的点
  • 在 MVC Web Api 4 Beta 中从 Json 中删除 Null 属性

    我正在序列化对象并从我的 Web 服务以 json 形式返回 但是 我试图从序列化的 json 中省略 null 属性 有没有办法做到这一点 我正在使用 Web Api MVC 4 beta 当前 ASP NET Web API 有计划 h
  • 我如何从带有变量的类中调用方法?

    给定这个类 class Tacobell public function order taco echo 3 Tacos thank you public function order burrito echo Cheesy bean an
  • 如何将数据从剪贴板复制并粘贴到 R 中?

    我在另一个应用程序 例如 Excel 等电子表格或文本编辑器 中打开了数据 如果我将该数据复制到操作系统剪贴板 如何将其作为 data frame 读入 R 假设 Windows 剪贴板中有数据 例如 从 Excel 复制的数据 将该数据放
  • 关键字或日期类型字段的范围查询?

    我有一个存储插入时间的字段 例如2016 10 10 11 00 00 000 我试过keyword类型和date类型 它们都满足range要求 例如 query range time gte 2016 10 10 11 00 00 000
  • Google Apps脚本类GmailApp批量操作?

    我已经在 GAS 上闲逛了一个月左右 并且我已经相当熟悉使用批处理操作来读取 写入电子表格 例如 getValues setValues 但是 我目前正在编写一个脚本 使用 GmailApp 类从 Gmail 中提取大量数据 我的代码运行速
  • 字符串文字的模板参数推导

    考虑这个简单的函数 template
  • 是否有一种简单的方法可以将特定的*命名* PowerShell 参数直接传递给被调用的函数?

    我确信我在某处读到 有一种简单的方法可以将命名参数从调用函数传递到被调用函数 而无需显式命名和指定每个参数 这不仅仅是重用这个职位 我对传递参数的名称在某些情况下相同但在其他情况下不同的情况感兴趣 我还认为有一种不依赖于职位的方法 func
  • 修改打印机特定配置对话框的属性

    我们构建了一个自定义打印对话框 其中有一个用于显示打印机特定对话框的按钮 我读这个答案 https stackoverflow com questions 939481 display printer specific configurat
  • Makefile 将不同目录中的源文件构建到同一目标目录中

    我有一个目录布局如下的 c 项目 src1 a c b c src2 c c d c objects 我正在尝试将 a b c d 编译成对象文件并将它们保存到对象目录中 这是我的 Makefile 的一部分 src1 src1 src1
  • Jenkins 链接到我的本地 git 存储库

    我也是 Jenkins 和 git 的新手 我创建了一个远程存储库github com并制作了一份本地副本 然后我想通过詹金斯链接它 我安装了 git 集成所需的插件 但我不知道配置新项目时设置它的本地存储库 URL 是什么 有人可以帮我在
  • 我可以将 Android 中的默认推送通知图标从应用程序图标覆盖为自定义图标吗?

    我可以将 Android 中的默认推送通知图标从应用程序图标覆盖为自定义图标吗 当推送通知出现时 我正在使用默认的 firebase 实现在系统托盘中显示通知 由于我的应用程序图标是彩色的并且具有渐变 因此当通知到来时 android尝试制
  • 以编程方式模拟 Android 按钮点击 [重复]

    这个问题在这里已经有答案了 我见过this https stackoverflow com questions 4553374 how to simulate a button click through code in android r
  • Tkinter.text - 如何计算动态字符串的高度?

    我有一个Text包含自定义字符串的小部件 n字符 多行 该小部件放置在垂直方向内panedwindow我想调整panedwindow的窗框显示整个字符串Text widget 该字符串本质上是动态的 这意味着它正在通过我的应用程序中的其他方