Tkinter中字体大小与部件大小的关系

2023-11-15

我是小鱼,今天是2022年2月10日。

之前用python在做扫雷游戏时,发现总是拿不准部件大小与字体之间的关系,为此,当时还特地写了一篇BLOG《Tkinter中的标签(Lable)与按钮(Button)的大小问题》,扫雷写完后我有时间慢慢回头再来看这个问题,发现当时的理解有很多是错误的。于是那篇文章我删除了,我准备重新再整理一下。

无论是标签还是按钮,在Tk中都被称为部件。以标签为例,在Tkinter.Lable的类中有width和height这两个属性。文档说明是说这两个值是用来设置标签的宽度与高度的,单位为像素。如果标签中放置的是图片的话,的确是这个样子,但如果标签中是文本的话,这两个值就与文本本身有关了。是文本的基本像素的倍数。文本的基本像素是与字体有关的,与部件应当无关。而字体的大小,粗细,倾斜都会影响字体的基本像素的大小。这才是导致我当时总是拿不准标签与按钮的大小的主要原因。

这里要在引入Tkinter中对字体的封装的类,Tkinter.Font——参见说明文档

tkinter.font --- Tkinter 字体封装

TK将字体封装在类当中,为了方便我们的使用。因此与字体有关的参数都可以从这个类的两个主要方法中获取出来,一个是measure, 另一个是metrics

measure方法是:返回以当前字体格式化时文本将在指定显示上占用的空间量

用法是  ziti.measure('8') 前面的字体是从封装的字体库中实例化的类,后面的8可以是任意内容,即为你想知道的这些内容在显示器上需要占多少个像素的宽度(见下面图片)

metrics有4个属性值,说明文档中有说明,这里我解释一下前面3个 'ascent', 'descent', 'linespace'

说明文档中:ascent -- 基线和最高点之间的距离; descent -- 基线和最低点之间的距离; linespace -- 所需最小垂直间距(在两个该字体的字符间,使得这两个字符在垂直方向上不重叠)

其实linespace = ascent + descent, 那么基线是什么,见下面的图片

 上图是我用截屏获得的一个标签,这个标签的bd,padx,pady属性我都设置成了0,标签的width和height我都设置成1。标签上的字体是Arial, 字号是12号,粗体,不倾斜。 图中每个方块即是1个像素,红色方块是我用像素笔后面画上去的,为了方便看空白处的像素数。所谓的基线就是字符的下底边,desent是下底边到部件下底边的距离,这个距离与部件无关,是font的要求(明显的,descent是font.metrics中的属性);相应的ascent就是基线到部件上底边的距离。linespace是desent与ascent的和,当部件的height为1的时候,部件高度(WidgetHeight - WH)就等于linespace。

另外,上图中的字符宽就是measure方法所获得的值,字符的宽与字体粗细是否倾斜都没有关系。

我发现了一个有趣的现象,正常的理解,在字符的宽度方向上并没有像行间距(linespace)这种东西的,应该就是一个字一个字的紧挨着,正常理解部件宽度(WidgetWidth - WW)应该就等于字符宽(font.measure)。但是上图中不是,明显在右边多了一个像素的距离,也就是说WW比measure大了1。而这个值与字体的粗细,倾斜都会有关系。之前我一直拿不准的原因就在这里,不同的字号情况是不同的,而且我一直找不出它的规律来。我直接统计了Arial字体从5号到48号字的所有属性值,可以看下面的代码

# _*_ coding : UTF-8 _*_
# @time: 2022/1/27 16:24
# @file: test_tkLabel.py
# @Author:yyh
# @Software:PyCharm
import tkinter as tk
import tkinter.font as tkf

from utilit import log


class FrameSet:
    def __init__(self, fram):
        # self.fm = fram
        fram['height'] = 80
        fram['width'] = 240
        fram['bd'] = 3
        fram['bg'] = '#808080'
        fram['relief'] = 'sunken'
        fram.pack(padx=4, pady=0, anchor='w')
        fram.pack_propagate(0)


class LabelSet:
    def __init__(self, labl, zity):
        self.lb = labl
        self.lb['text'] = '8'
        self.lb['font'] = zity
        self.lb['image'] = ''
        self.lb['height'] = 1
        self.lb['width'] = 1
        self.lb['bd'] = 0
        self.lb['relief'] = 'solid'
        self.lb['padx'] = 0
        self.lb['pady'] = 0
        self.lbwid = self.lb.winfo_reqwidth()
        self.lbhei = self.lb.winfo_reqheight()
        self.zihao = zity.measure('tkf')

    def update_lb(self):
        self.lb.pack()

    def log_lb_info(self):
        log('宽、高 = {}、{}', format(self.lbwid, self.lbhei))


ck = tk.Tk()
fm = tk.Frame(ck)
lb = tk.Label(fm)
ck.title('测试')
ck.geometry('240x240')
print("字号 字符号 ascent descent linespace 部件宽 部件高 weight slant underline overstrike")
for n in range(5, 49):
    for weight in ('normal', 'bold'):
        for slant in ('roman', 'italic'):
            ziti = tkf.Font(family='Arial', size=n, weight=weight, slant=slant, underline=0, overstrike=0)
            frame = FrameSet(fm)
            label = LabelSet(lb, ziti)
            # label.update_lb()
            print(n, ziti.measure('8'), end=' ')
            for met in ('ascent', 'descent', 'linespace',):
                print(ziti.metrics(met), end=' ')
            print(lb.winfo_reqwidth(), lb.winfo_reqheight(), end=' ')
            for art in ('weight', 'slant', 'underline', 'overstrike',):
                print(ziti.cget(art),end=' ')
            print(' ')

ck.mainloop()

我用上述代码自动输出,并且存到excel中。对于粗细,正斜分别输出,每个字号输出4行,上面那张截图即为12号字体的涂红那行的截图,下面第二张图片是12号字体的4个截图

 

 从表格中可以看出来,倾斜与加粗不仅会影响WW,而且还会影响WH。但是它们不会影响字符自身的宽度,即measure。

当时测试时也测试了下划线与删除线,但是我发现那两个符号对WW与WH没有影响,所以后面就不再累赘输出了。

我就不贴上我的excel文件了,如果感兴趣的朋友可以复制我的代码到然后自己输出。从excel文件中可以获取一些信息,如下:

我暂时把部件宽度比字符宽度大的那1个像素称为宽度边距(wisent),对于越是大的字号,这个wisent也会出现越大的现象,并且在同一个字号中,因为粗细、正斜的不同,wisent有可能会出现不同(大部分情况下同一个字号的wisent都一样)

==================分割线==================

下面,再用一张图来说明一下部件的属性中width,height,bd,padx,pady的关系。我还是以Label为例。

 图中是bd =1,padx =2, pady =2的样子。当部件属性width和height分别都是1的时候,能获得最基本的部件宽与部件高(WWB, WHB)

整个部件包含内边距(padx,pady)和包含边框(bd)后的总的部件宽与高(WW,WH)的公式应当如下:

WW = width * WWB + 2 * padx + 2 * bd

WH = height * WHB + 2 * pady +2 * bd 

上面已经说了,WHB可以直接从字体中获得,ziti.mertics('linespace')

而WWB却不一定是 ziti.measure('8')的值,所以还是需要通过读取部件宽度的方式来获得

即使用某个字体,然后定义部件的width和height分别为1,bd,padx,pady=0,获得标签

然后,Label.winfo_reqwidth()的方法来获取宽度,如果想要加粗或者倾斜文字的话,最好也获取一下加粗与倾斜后的 winfo_reqwidth 的值来作为WWB

最后,我再有一点补充。之前我非常执着的认为WWB是与字符宽(measure)和字号有一个函数关系的,即 Z = f(X,Y) 这样的函数关系。这也是当时使我一直陷入一个僵局,然后执着的找多元函数回归拟合的方法。但是当我把字体正斜也放入考虑的范畴之后,我就发现,相同的字符宽与字号也能出现不同的WWB。12号字就是一个最好的例子,7号字也有这样的现象。于是才释然,不在执着用计算获得某个字号WWB的值。而是生成部件后从部件信息(winfo)中去获取。

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

Tkinter中字体大小与部件大小的关系 的相关文章

随机推荐

  • 【mysql】mysql 常用建表语句

    1 建立员工档案表 要求字段 员工员工编号 员工姓名 性别 工资 email 入职时间 部门 2 合理选择数据类型及字段修饰符 要求有NOT NULL auto increment primary key等 make by kakane D
  • arcgis10.2破解版下载及其详细教程;;;附带10.1-10.6的破解版,没有教程

    1 arcgis10 2破解版 https blog csdn net bigemap article details 81131840 2 arcgis10 1 10 5破解版安装包 https blog csdn net e wsq a
  • 【c++】8.map和vector容器查找、删除指定元素、emplace、insert

    1 查找与删除 vector 和 map 容器中指定元素 vector 查找或删除vector的指定元素 123 方法1 使用迭代器 不同于map map有find方法 vector本身没有find这一方法 std vector
  • 从浅到深理解bert

    更多查看https github com B C WANG AI Storage 4 2 4 2从浅到深理解bert 4 2 1 理解Attention 参考https www cnblogs com robert dlut p 86382
  • Pytorch save_image和make_grid函数详解

    Pytorch save image和make grid函数详解 make grid用于把几个图像按照网格排列的方式绘制出来 save image用于保存图像 这两个函数的函数签名差不多 所以只说一个 def make grid tenso
  • excel python插件_再见 VBA!神器工具统一 Excel 和 Python

    大家好 我是东哥 经常给大家推荐好用的数据分析工具 也收到了铁子们的各种好评 这次也不例外 我要再推荐一个 而且是个爆款神器 Excel和Jupyter Notebok都是我每天必用的工具 而且两个工具经常协同工作 一直以来工作效率也还算不
  • 线段树模板

    线段树属于高级数据结构 本文粗略地讲解了一下线段树的模板 大家直接拿去用就好 long long ls int x return x lt lt 1 long long rs int x return x lt lt 1 1 const i
  • 电感选型计算

    转载 https www richtek com Design 20Support Technical 20Document AN053 电感之种类与其特性分析 摘要 电感器是开关转换器中非常重要的元器件 如用于储能及功率滤波器 电感器的种
  • react render中进行if判断

    在render中进行if条件判断然后加载相应的模块进行渲染方法如下 第一种 第二种
  • Spark的DataFrame和Schema详解和实战案例Demo

    1 概念介绍 Spark是一个分布式计算框架 用于处理大规模数据处理任务 在Spark中 DataFrame是一种分布式的数据集合 类似于关系型数据库中的表格 DataFrame提供了一种更高级别的抽象 允许用户以声明式的方式处理数据 而不
  • 【数据分析之道-NumPy(四)】numpy广播机制

    文章目录 专栏导读 1 广播机制 2 一维数组和二维数组的广播 3 二维数组和三维数组的广播 4 标量和数组的广播 5 形状不兼容的数组不能进行广播 专栏导读 作者简介 i阿极 CSDN Python领域新星创作者 专注于分享python领
  • 小游戏:红色警戒争霸战!

    这个是当年自己在学校里面写的小游戏 现在看看好弱智啊 第一代的代码 public struct Heros public string name public double hp public double mp public double
  • a标签下载pdf文档

    开发过程中 有时我们需要点击a标签然后可以下载pdf文档 但是结果经常是pdf文档直接就在浏览器中打开了 那么想要直接下载需要怎么实现呢 实现方式 在a标签的href中写上要下载的pdf文档的地址 加上download下载属性 最后记得让后
  • Tencent://Message/协议的实现原理 .

    Tencent Message 协议的实现原理 2008年07月17日 星期四 12 04 腾讯官方通过 Tencent Message 协议可以让QQ用户显示QQ TM的在线状态发布在互联网上 并且点击 不用加好友也可以聊天 官方链接 h
  • C语言中关键字一次说清楚!!!

    目录 一 static 1 作用 2 一些例子帮助大家更深刻的理解static的几个作用 1 修饰局部变量 2 修饰全局变量和函数 二 const 1 作用 使得变量不允许被修改 提高代码的健壮性 2 本质 给编译器看的 在编译阶段起作用
  • golang 中strconv包用法

    https blog csdn net chenbaoke article details 40318357
  • .netcore windows app启动webserver

    创建controller using Microsoft AspNetCore Mvc using Microsoft Extensions Logging using System using System Collections Gen
  • 判断input输入框的值,用到input事件,blur事件,focus事件

    需求 当输入框有字或者正在输入时 右边的圆形取消按钮才出现 否则不出现
  • 网络h

    注 所有标题带h的博客不保证准确性和正确性 写这篇博客原因是因为网络知识学了又忘 忘又学 翻来覆去 所以做个笔记 正如上文注明 此篇博客所写内容不够准确和专业 甚至不正确 只是为了便于理解记忆 概念 以太网 ethernet 双绞线 同轴线
  • Tkinter中字体大小与部件大小的关系

    我是小鱼 今天是2022年2月10日 之前用python在做扫雷游戏时 发现总是拿不准部件大小与字体之间的关系 为此 当时还特地写了一篇BLOG Tkinter中的标签 Lable 与按钮 Button 的大小问题 扫雷写完后我有时间慢慢回