You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何精准获取CTkLabel的像素级实际宽度?

如何精准获取CTkLabel的实际像素宽度?

我使用customtkinter已有一段时间,希望根据字体大小和显示字符串的长度,获取CTkLabel的精确像素级宽度,但尝试的多种方法均不准确。以下是复现问题的代码示例:

import customtkinter as ctk


class MeasuringLabel(ctk.CTkLabel):
    def __init__(self, master, font_size=30, length=20, **kwargs):
        self.font_size = font_size
        self.length = length
        self.font = ctk.CTkFont('Courier new', size=self.font_size)
        self.text = '0' * self.length # Sample text
        super().__init__(master=master, font=self.font, text=self.text,
                         fg_color='red', **kwargs)

    # Measure using ctk.CTkFont.measure
    def measured_width(self):
        return self.font.measure(self.text, displayof=self)

    # Measure one character and extrapolate
    def extrapolated_width(self):
        return self.font.measure('0', self) * self.length

    # Take 0.6*font_size as the width of one character and extrapolate, then round
    def theoretical_width(self):
        return round(self.font_size * MeasuringLabel.font_width_to_size * self.length)

    # Same as above, but round the character width first
    def theoretical_round_first_width(self):
        return round(self.font_size * MeasuringLabel.font_width_to_size) * self.length

    # Get the width of the label using the specified method
    def get_width(self, width_method='measured'):
        width_function = MeasuringLabel.width_functions[width_method]
        return width_function(self)

    # Return a CTkFrame with the width as measured using the specified method
    def width_bar(self, width_method='measured'):
        width = self.get_width(width_method)
        print(f'{width_method+": ":<25} {width}')
        return ctk.CTkFrame(self.master, width=width, height=10,
                            corner_radius=0, fg_color='blue')


    # This is specific to Courier new. The exact value is 1229/2048 according to
    # https://stackoverflow.com/a/19114963/.
    font_width_to_size = 0.6

    # These functions, when called with a MeasuringLabel instance as the argument,
    # return the width as calculated using that method.
    width_functions = {
        'measured': measured_width,
        'extrapolated': extrapolated_width,
        'theoretical': theoretical_width,
        'theoretical_round_first': theoretical_round_first_width,
        'winfo_width': ctk.CTkLabel.winfo_width,
        'winfo_reqwidth': ctk.CTkLabel.winfo_reqwidth,
    }


class App(ctk.CTk):
    def __init__(self, font_size, length):
        super().__init__()

        self.title('Width measuring')
        self.geometry('600x200')

        self.label = MeasuringLabel(self, font_size, length)
        self.label.grid(row=0, column=0, sticky='w') # Left-align
        
        self.update_idletasks() # Update, so winfo_width returns something useful

        for i, width_method in enumerate(MeasuringLabel.width_functions):
            new_bar = self.label.width_bar(width_method)
            new_bar.grid(row=i+1, column=0, pady=(2,0), sticky='w') # Left-align


app = App(font_size=30, length=20)
app.mainloop()

我在MeasuringLabel类中实现了6种宽度测量方法(其中2种继承自CTkLabel)。初始化应用时,会创建指定font_size、文本为'0'*length的MeasuringLabel,并生成6个对应不同测量方法宽度的CTkFrame,期望其中一个能与Label宽度完全匹配,但所有方法均不准确:

  • font_size=30、length=20时的测试效果:
    font_size=30、length=20时的测试效果

  • font_size=37、length=20时的测试效果:
    font_size=37、length=20时的测试效果

可以明显看到,winfo_width和winfo_reqwidth的结果始终过长,完全无法使用,推测这可能与屏幕缩放有关,希望了解如何对此进行补偿。

此外,measured、extrapolated和theoretical_round_first这三种方法的结果始终一致,这让我认为CTkFont.measure方法是逐个计算字符宽度并取整后求和,但我不理解为何这与CTkLabel的实际宽度不符。

最后,我尝试手动为每种字体大小设置匹配的宽度,当length=20时得到以下经验数据:

# The keys are the font_sizes, and the values are the correct widths.
empirical_widths = \
    {15: 176, 16: 192, 17: 208, 18: 208, 19: 224,
     20: 240, 21: 256, 22: 272, 23: 272, 24: 288, 
     25: 304, 26: 304, 27: 320, 28: 336, 29: 352,
     30: 368, 31: 368, 32: 384, 33: 400, 34: 400,
     35: 416}

最糟糕的是,这些数据并非线性的。我制作了对比图表来比较不同方法的结果:
标签宽度与字体大小对比

font_size=24附近的特写:
font_size=24附近的特写

可以看到,measured、extrapolated和theoretical_round_first方法的结果重叠(红色),winfo_width和winfo_reqwidth的结果也重叠(灰色),但所有方法都仅偶尔能与实际宽度(黑色)匹配。现寻求能精准获取CTkLabel实际宽度的有效方法。


内容的提问来源于stack exchange,提问作者Truls Henriksson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:55:07