Relationship between font size and part size in Tkinter

I'm Xiaoyu. Today is February 10, 2022.

When I used python to play minesweeping games before, I found that I was always uncertain about the relationship between part size and font. Therefore, I specially wrote a BLOG about the size of labels and buttons in Tkinter at that time. After minesweeping, I had time to look back and find that many of my understanding at that time were wrong. So I deleted the article and I'm going to rearrange it.

Both labels and buttons are called components in Tk. Take the label as an example, in Tkinter Lable's class has two attributes: width and height. The document description says that these two values are used to set the width and height of the label, in pixels. If the two labels are placed in the text, they are related to the values in the label itself. Is a multiple of the basic pixel of the text. The basic pixels of the text are related to the font and should not be related to the component. The size, thickness and inclination of the font will affect the size of the basic pixels of the font. This is the main reason why I was always uncertain about the size of labels and buttons.

Here, we will introduce the class of font encapsulation in Tkinter, Tkinter Font - see the documentation

tkinter.font --- Tkinter font encapsulation

TK encapsulates fonts in classes for our convenience. Therefore, the parameters related to fonts can be obtained from the two main methods of this class, one is measure and the other is metrics

The measure method returns the amount of space the text will occupy on the specified display when formatted with the current font

The usage is "ziti" The font in front of measure ('8 ') is a class instantiated from the encapsulated font library. The following 8 can be any content, that is, you want to know how many pixels the content needs to occupy on the display (see the picture below)

metrics has four attribute values, which are described in the description document. Here I will explain the first three 'ascent', 'descent', 'linespace'

In the description document: ascent -- the distance between the baseline and the highest point; descent -- distance between baseline and lowest point; linespace -- minimum vertical space required (between two characters of the font, so that the two characters do not overlap in the vertical direction)

In fact, linespace = ascent + descent, so what is the baseline? See the following picture

The above figure is a tag I obtained with a screenshot. I set the BD, padx and pady properties of this tag to 0, and I set the width and height of the tag to 1. The font on the label is Arial, the font size is 12, bold and not inclined. Each square in the figure is one pixel, and the Red Square is drawn on the back of the pixel pen to facilitate the number of pixels in the blank. The so-called baseline is the bottom edge of the character, and the desent is the distance from the bottom edge to the bottom edge of the part. This distance is independent of the part and is the requirement of font (obviously, descent is the attribute in font.metrics); The corresponding ascent is the distance from the baseline to the bottom edge of the component. Linespace is the sum of desent and ascent. When the height of the part is 1, the part height (WidgetHeight - WH) is equal to linespace.

In addition, the character width in the figure above is the value obtained by the measure method. The character width has nothing to do with whether the font is tilted or not.

I found an interesting phenomenon. In normal understanding, there is no such thing as line space in the width direction of characters. It should be close to each other word by word. In normal understanding, the width of widget (WW) should be equal to the width of characters (font.measure). But not in the figure above. Obviously, there is an extra pixel distance on the right, that is, WW is 1 larger than measure. This value is related to the thickness and inclination of the font. The reason why I have been uncertain before is here. The situation of different font sizes is different, and I have been unable to find its law. I directly counted all attribute values of Arial font from size 5 to size 48. You can see the following code

# _*_ 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('Width and height = {},{}', format(self.lbwid, self.lbhei))


ck = tk.Tk()
fm = tk.Frame(ck)
lb = tk.Label(fm)
ck.title('test')
ck.geometry('240x240')
print("Font size and symbol ascent descent linespace Component width component height 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()

I use the above code to automatically output and save it in excel. For the thickness and skew, output respectively, and output No. 4 for each font size. The screenshot above is the screenshot of the red line of font No. 12, and the second picture below is the four screenshots of font No. 12

 

It can be seen from the table that skew and bold will affect not only WW, but also WH

At that time, underline and strikeout were also tested during the test, but I found that those two symbols had no effect on WW and WH, so I won't bother to output later.

I won't paste my excel file. If interested friends can copy my code and output it by themselves. Some information can be obtained from Excel files, as follows:

I temporarily call the pixel whose part width is larger than the character width as the width margin. The larger the font size is, the larger the wisent will be. In the same font size, because of the different thickness and skew, the wisent may be different (in most cases, the wisent of the same font size is the same)

Next, use a diagram to illustrate the relationship between width, height, BD, padx and pady in the component attributes. Let me take Label as an example.

In the figure, bd =1,padx =2, pady =2. When the component attributes width and height are 1 respectively, we can obtain the most basic component width and component height (WWB, WHB)

The formula of the total part width and part height (WW,WH) after the whole part includes the inner margin (padx, pady) and the frame (bd) shall be as follows:

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

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

As mentioned above, WHB can be obtained directly from fonts, ziti mertics('linespace')

WWB is not necessarily ziti Measure ('8 '), so it still needs to be obtained by reading the part width

That is, use a font, and then define the width and height of the part as 1, BD, padx and pady = 0 respectively to obtain the label

Then, label winfo_reqwidth() method to get the width. If you want to bold or tilt the text, you'd better also get the windo after bold and tilt_ The value of reqwidth is used as WWB

Keywords: Python Tkinter

Added by shmick25 on Thu, 10 Feb 2022 17:18:17 +0200