Skip to content

In linux, copying a tkinter font doubles its size when using Tk 8.6 and a hi-res screen. #128719

Description

@culler

Bug report

Bug description:

>>> import tkinter
>>> from tkinter.font import Font
>>> root = tkinter.Tk()
>>> f1 = Font(name='TkDefaultFont', exists=True)
>>> f1.actual()
{'family': 'Noto Sans', 'size': 20, 'weight': 'normal', 'slant': 'roman', 'underline': 0, 'overstrike': 0}
>>> f2 = f1.copy()
>>> f2.actual()
{'family': 'Noto Sans', 'size': 40, 'weight': 'normal', 'slant': 'roman', 'underline': 0, 'overstrike': 0}
>>> f3 = f2.copy()
>>> f3.actual()
{'family': 'Noto Sans', 'size': 80, 'weight': 'normal', 'slant': 'roman', 'underline': 0, 'overstrike': 0}
>>> f4 = f3.copy()
>>> f4.actual()
{'family': 'Noto Sans', 'size': 160, 'weight': 'normal', 'slant': 'roman', 'underline': 0, 'overstrike': 0}

The example above is from Ubuntu 24.04 running on an Asus Zenbook with EVO screen. The display scaling from the system settings is the default, namely 200%. This does not happen when the display scaling is 100%.

Note that font f1 is already (more than) doubled in size, compared to what wish8.6 (built from 8.6.15 source) reports:

% info patchlevel
8.6.15
% font actual TkDefaultFont
-family {Noto Sans} -size 9 -weight normal -slant roman -underline 0 -overstrike 0
% font config TkDefaultFont
-family sans-serif -size -12 -weight normal -slant roman -underline 0 -overstrike 0

The negative value -12 means that the size is 12 pixels, as opposed to 12 points. I think the intitial expansion being slightly more than a factor of 2 may be caused by rounding when converting pixels to points.

Note that the behavior of Tk 9 is different. Using wish9.0 on the same system produces:

% info patchlevel
9.0.0
% font actual TkDefaultFont
-family {Noto Sans} -size 10 -weight normal -slant roman -underline 0 -overstrike 0
% font config TkDefaultFont
-family sans-serif -size 10 -weight normal -slant roman -underline 0 -overstrike 0

Also, the pending new version of tkinter from #124156 does not have this doubling behavior when used with Tk 9.0. (I have not tested the new tkinter with Tk 8.6, but I expect it to have the broken behavior since I don't think that the PR changes Font.copy.)

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Jan 10, 2025
  2. NathanDunfield commented on Jan 11, 2025

    @NathanDunfield

    I think the doubling behavior is because tkinter's Font.copy relies on Font.actual:

    def copy(self):
            "Return a distinct copy of the current font"
            return Font(self._tk, **self.actual())
    

    On a high-res Linux screen with Ubuntu 24.04, the size and self.actual()['size'] differ, apparently by the scaling factor:

    >>> import tkinter
    >>> root = tkinter.Tk()
    >>> f1 = Font(font='TkFixedFont')
    >>> f1['size']
    20
    >>> f1.actual()['size']
    40
    

    This seems consistent with the Tk docs which say that font actual:

    Returns information about the actual attributes that are obtained when font is used on window's display; the actual attributes obtained may differ from the attributes requested due to platform-dependant limitations, such as the availability of font families and pointsizes.

  3. NathanDunfield commented on Jan 11, 2025

    @NathanDunfield

    Also, on stock Ubuntu 24.04, I see a very different result from @culler's built-from-source Tk 8.6:

    $ wish
    % info patchlevel
    8.6.14
    % font actual TkDefaultFont
    -family {Noto Sans} -size 20 -weight normal -slant roman -underline 0 -overstrike 0
    % font config TkDefaultFont
    -family sans-serif -size 10 -weight normal -slant roman -underline 0 -overstrike 0
    

    Note everything here is in points, not a mix of points and pixels. The same difference between config and actual shows up in user-defined fonts:

    % font create MyFont -family {Noto Sans} -size 8
    MyFont
    % font actual MyFont
    -family {Noto Sans} -size 16 -weight normal -slant roman -underline 0 -overstrike 0
    % font config MyFont
    -family {Noto Sans} -size 8 -weight normal -slant roman -underline 0 -overstrike 0
    
  4. culler commented on Jan 11, 2025

    @culler
    ContributorAuthor

    This is probably a difference between 8.6.14 and 8.6.15. There is now a release of 8.6.16. I will report if it differs from 8.6.15.

  5. NathanDunfield commented on Jan 11, 2025

    @NathanDunfield

    @culler: I think this Tk ticket is related.

  6. culler commented on Jan 11, 2025

    @culler
    ContributorAuthor

    Indeed! It looks like they ended up fixing a related Tk bug but never got to the tkinter issue raised in the original report, which seems to be the same as this one..

  7. NathanDunfield commented on Jan 11, 2025

    @NathanDunfield

    I installed the proposed Ubuntu package libtk 8.6.16-1 by downloading the binary from launchpad. Now I get:

    % font actual TkDefaultFont
    -family {Noto Sans} -size 10 -weight normal -slant roman -underline 0 -overstrike 0
    % font config TkDefaultFont
    -family sans-serif -size 10 -weight normal -slant roman -underline 0 -overstrike 0
    % font create MyFont -family {Noto Sans} -size 8
    MyFont
    % font actual MyFont
    -family {Noto Sans} -size 8 -weight normal -slant roman -underline 0 -overstrike 0
    % font config MyFont
    -family {Noto Sans} -size 8 -weight normal -slant roman -underline 0 -overstrike 0
    
  8. NathanDunfield commented on Jan 11, 2025

    @NathanDunfield

    I'm thinking maybe there's nothing to do here for tkinter. I suspect it would have been a better design to have Font.copy() invoke config instead of actual as this seems like a poor copy:

    >>> f1 = Font(family='sans-serif', size='10')
    >>> f1['family']
    'sans-serif'
    >>> f1.copy()['family']
    'Noto Sans '
    

    However, if Tk itself is working right, they should always display the same. Moreover, Font.__init__ invokes actual which is why one sees:

    >>> Font(font='TkDefaultFont').config()['family']
    'Noto Sans`
    

    whereas wish would say sans-serif. But presumably changing from actual to config would cause minor havoc and be more trouble than it's worth.

  9. serhiy-storchaka commented on Jun 27, 2026

    @serhiy-storchaka
    Member

    This was a Tk bug in font actual on HiDPI displays — the resolved size was scaled relative to the configured size — and it was fixed upstream in Tk 8.6.16.

    As confirmed earlier in this thread, 8.6.16 makes font actual agree with font config, so Font.copy() (which snapshots via actual()) no longer doubles the size. I re-verified on Tk 9.0.4 with tk scaling ≈ 2.0: config and actual both report size 10, and a repeated .copy() chain stays at 10.

    The fix is in Tk, not tkinter, and all current Tk releases (8.6.16+, 9.0) include it, so I'm closing this as resolved upstream. Please upgrade Tk to 8.6.16 or later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions