Make Anaconda's tkinter aware of system fonts or install new fonts for Anaconda
Asked Answered
L

5

16

I have two Python installations on my Debian Sid notebook, ⑴ the system's Python (v.2.7) with a little bunch of utility packages (including Tkinter) and ⑵ Anaconda's Python 3.

It is easy to see which (well, here how many...) fonts are available for the two Python distributions.

Python 2

>>> from Tkinter import Tk
>>> from tkFont import families
>>> Tk(); available = families()   ### Tk() is needed to have a running tcl interpreter
<Tkinter.Tk instance at 0x7f977bcbfb90>
>>> len(available)
3011

Python 3

>>> from tkinter import Tk
>>> from tkinter.font import families
>>> Tk() ; available = families()
<tkinter.Tk object .>
>>> len(available)
68

It seems to me that Anaconda's tkinter only looks at the basic X fonts that came with the distributionsee edit below.

Do you know a procedure to, alternatively

  • let Anaconda's tkinter know of the system fonts (preferred alternative) or
  • install a few fonts in the Anaconda's tree so that tkinter can use them?

tia


Edit the fonts available to Anaconda are indeed system fonts, but only the fonts that are known to xfontsel, i.e., the fonts in the font path that can be manipulated using xset.

I tried the following

$ cd ~/.fonts ; mkfontscale ; mkfontdir ; xset fp+ `pwd`

and xfontsel showed about 30 more font families. Checking with Python 3 I verified that only two font families were added to the list of available fonts (namely 'go' and 'gomono' — no 'consolas' etc) and producing a label

...
r = Tk() ; Label(r, text="Go Mono", font=('gomono', 24)).pack()

with Python 2 and Python 3 succeeded in both cases, but Debian's Python showed a nice antialiased text while the other was a (rough) bitmap rendition.

So, in a sense, I have partially answered my question, but

  1. not every font family, as shown by xfontsel, was taken up by tkinter
  2. even for the very few that were recognized, the rendition leaves too much to be desired...

and I'd like to read a better, more useful answer.

Lankton answered 12/12, 2017 at 9:29 Comment(5)
I suspect you issue is similar to what is described in the following question: Path to Linux Fonts in Python3 and tkinterNaumachia
@Naumachia I see, the questions are similar to say the least... However I'm going to leave posted my question because I feel the question title is more up to the point and could attract more answers, what do you think?Lankton
Agreed. I just referenced this post because I thought it might help you or someone else track down a solution to the problem.Naumachia
Is there any FONTCONFIG_PATH/FONTCONFIG_FILE environment variable? Can you check something like 'FONTCONFIG_PATH' in os.environ?Backbite
@Backbite I've checked, No suitable (evidently fontconfig related) environment variable.Lankton
L
11

{tT}kinter works linking to a Tk/Tcl interpreter that, loosely speaking, is contained in a couple of DLL, in particular the graphical library is libtk6.0.so.

Most of the extra fonts not seen by tkinter are managed by the Freetype library and Anaconda's libtk6.0.so is not built against Freetype...

$ ldd /usr/lib/x86_64-linux-gnu/libtk8.6.so | grep freetype
        libfreetype.so.6 => /usr/lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007f0a24597000)
$ ldd miniconda3/lib/libtk8.6.so | grep freetype
$

I've tried the following, horrible thing

$ mv lib/miniconda3/lib/libtk8.6.so lib/miniconda3/lib/libtk8.6.sav
$ ln -s /usr/lib/x86_64-linux-gnu/libtk8.6.so lib/miniconda3/lib/libtk8.6.so
$ ipython
Python 3.6.3 |Anaconda, Inc.| (default, Nov 20 2017, 20:41:42) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from tkinter import Tk, Label ; from tkinter.font import families
In [2]: r = Tk() ; a = families() ; len(a)
Out[2]: 328
In [3]: r=Tk() ;  Label(r, text="Constantia", font=("Constantia", 60)).pack()
In [4]: r.mainloop()

enter image description here

Final thoughts.

  1. Substituting the DLL is not a clean solution.
  2. The fonts are not exactly the same. For sure Anaconda has its own Fontconfig subsystem and possibly the directories that are scanned are different, but I have not a correct understanding of the discrepancy in the number of fonts.
  3. The correct course of action is to persuade Anaconda, Inc. to build libtk against Freetype, but I don't know how to report to them, e.g., if I go to https://www.anaconda.com/search/issues what I see is a list of informational articles on the distribution.

Update

W.r.to point 3, I contacted via a github issue Anaconda Inc. and I was told

No we cannot do this. When building our software we need python built very early, well before anything graphical gets built. Adding Freetype as a dep for tkinter causes a cycle in the build graph and we can no longer build the distro.

Why not use something more modern than tkinter anyway?

                                                   --- Ray Donnelly (aka mingwandroid)

Lankton answered 13/12, 2017 at 7:35 Comment(4)
I've found the appropriate channel and I've posted my findings in the following issue tkinter fonts #6833Lankton
I posted also in package tk appears to be built without truetype support #776, that appears to be even more relevant and is (was?) tracked by a developer of the distribution.Lankton
Wow. The reply you got is simultaneously helpful and insulting. Jeez.Week
@Week Helpful inasmuch they make it clear that they are not going to fix the problem. On the other hand, I suspect that the merit of the answer is b.s. but I do not feel like having a discussion with a person that stands so bold on their position.Lankton
I
3

The issue as @gboffi pointed out is that Conda's build of the Tk library doesn't include support for the Freetype library, which is a library used for rendering fonts, resulting in the poor font rendering issue you're experiencing.

They hold that they can't include Freetype support due to the circular dependencies it would introduce in their build process. Their recommended solution is to move away from tk and use more modern GUI libraries instead.

The suggestion proposed by @UbuntuUser of swapping Conda environment's libtk8.6.so with the system's libtk8.6.so may work, but only if the libtk8.6.so refers to the exact same version of tk (e.g., a mismatch between 8.6.12 on the system vs 8.6.13 on Conda would result in an error).

Solution

Another approach is to build the Tcl/Tk libraries with Freetype support yourself, and then using them in your Conda environment. This should work more generally. Here are the steps:

  1. Install necessary packages
$ sudo apt-get install build-essential
$ sudo apt-get install libx11-dev libxft-dev
  1. Download Tcl and Tk source code (http://www.tcl.tk/software/tcltk/download.html).
$ wget http://downloads.sourceforge.net/tcl/tcl8.6.13-src.tar.gz
$ wget http://downloads.sourceforge.net/tcl/tk8.6.13-src.tar.gz

*In the next steps, make sure you use the text associated with your downloaded tk version.

  1. Extract the source code
$ tar xzf tcl8.6.13-src.tar.gz
$ tar xzf tk8.6.13-src.tar.gz
  1. Activate your Conda environment and confirm the $CONDA_PREFIX outputs the correct path
$ conda activate <your-env-name>
$ echo $CONDA_PREFIX
  1. Build and install Tcl
$ cd tcl8.6.13/unix
$ ./configure --prefix=$CONDA_PREFIX
$ make
$ make install
$ cd ../..
  1. Build and install Tk
$ cd tk8.6.13/unix
$ ./configure --prefix=$CONDA_PREFIX --with-tcl=$CONDA_PREFIX/lib
$ make
$ make install

If all went well, you should now be able to run your tk app within the Conda environment that you installed tk onto, and it will have nice font. Here's an example app you can run before and after:

# tkinter example to check font quality
import tkinter as tk

window = tk.Tk()

label = tk.Label(window, text="Hello, world!")
label.pack()

entry_field = tk.Entry(window)
entry_field.pack()

button = tk.Button(window, text="Press me!")
button.pack()

window.mainloop()

Cheers

Illuse answered 29/7, 2023 at 18:1 Comment(0)
H
2

Five years later, this is still an issue. Anaconda seems to have no will to fix this. I'm using Ubuntu 22.04 with Wayland. Here are the steps I've taken that solved this issue. It seems to be working, would there be any issues with this approach?

  1. Set up a conda environment for python 3.7, currently using 3.7.16

  2. Using the system environment (python 3.10.6) installed tk

    # apt install python3-tk
    
  3. Replaced first .so with second one:

    ~/anaconda3/envs/py37/lib/libtk8.6.so

    /usr/lib/x86_64-linux-gnu/libtk8.6.so

  4. Enjoy new fonts!

Here are the before and after dependencies of the .so file for the conda 3.7 environment:

$ ldd ~/anaconda3/envs/py37/lib/libtk8.6.so

linux-vdso.so.1 (0x00007ffe6d9d3000)
libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007f500f163000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f500f15e000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f500f077000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f500ee00000)
libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f500f04d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f500f41d000)
libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007f500f045000)
libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f500f03d000)
libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f500ede8000)
libmd.so.0 => /lib/x86_64-linux-gnu/libmd.so.0 (0x00007f500f030000)

$ ldd ~/anaconda3/envs/py37/lib/libtk8.6.so

linux-vdso.so.1 (0x00007ffdcbbc5000)
libXft.so.2 => /lib/x86_64-linux-gnu/libXft.so.2 (0x00007fe816ffd000)
libfontconfig.so.1 => /lib/x86_64-linux-gnu/libfontconfig.so.1 (0x00007fe816fb3000)
libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007fe816e73000)
libXss.so.1 => /lib/x86_64-linux-gnu/libXss.so.1 (0x00007fe816e6e000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe816d87000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe816a00000)
libfreetype.so.6 => /lib/x86_64-linux-gnu/libfreetype.so.6 (0x00007fe816cbd000)
libXrender.so.1 => /lib/x86_64-linux-gnu/libXrender.so.1 (0x00007fe816cb0000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007fe816c7f000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007fe816c76000)
libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007fe816c4c000)
libXext.so.6 => /lib/x86_64-linux-gnu/libXext.so.6 (0x00007fe816c37000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe81719f000)
libpng16.so.16 => /lib/x86_64-linux-gnu/libpng16.so.16 (0x00007fe8169c5000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fe8169a9000)
libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007fe81699b000)
libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007fe816c2f000)
libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007fe816993000)
libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007fe816970000)
libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007fe816958000)
libmd.so.0 => /lib/x86_64-linux-gnu/libmd.so.0 (0x00007fe81694b000)
Howie answered 27/4, 2023 at 18:18 Comment(2)
What is different, in terms of idea and minute details notwithstanding, from my answer above?Lankton
I believe mine falls under your category of "Substituting the DLL" I'm not sure why you used a symbolic link? Is that to make it resilient to updates? I'm a little more simple minded linux-wise and my solution makes sense to me as an action that can be repeated on many machines and always yield a working resultHowie
T
-1

I'm working around this by launching gitk in a conda-free environment. I'm newish to conda though so I don't know if there are subtle/hidden problems with doing this.

# Fix gitk fonts in conda environments
# The Tcl/Tk environment in conda has font linking issues, so hard-to-read fonts get chosen
# Start a subshell ( ), any changes within will die with the subshell
# Detect if conda is active, if so deactivate until it isn't
# Run gitk in a conda-free environment
alias gitk='( [ -n "${CONDA_SHLVL}" ] && while [ $CONDA_SHLVL -gt 0 ]; do conda deactivate; done; gitk --all & )'
Thursby answered 5/10, 2021 at 16:28 Comment(3)
I have not down voted but your solution is roughly equivalent to sawing the branch you are sitting on. The down voter is possibly someone willing to use Anaconda's Tkinter AND nice fonts.Lankton
(or someone who sells an Anaconda+Tkinter-based solution to their clients, and is sick and tired of hearing their complaints about the jagged fonts)Lankton
@Lankton Thanks. It's not really sawing off my branch though, it's putting gitk onto a branch, sawing off that branch, then noticing gitk is a bird that depends on its wings and doesn't care if the branch is there or not. gitk needs git and nothing from the Anaconda environment as far as I can tell. And it's in a subshell so it doesn't affect anything else.Thursby
W
-3

EDIT: As @gboffi pointed out, this solution only seems like it works, as sudo python doesn't use Anaconda's install, but rather the system default. Using the full Anaconda Python path with sudo still yields the limited font options. I'm going to keep exploring this, but this answer as it stands is clearly incorrect.


I was having almost the exact same problem, and the "fix" for me was to run Anaconda's Python with sudo. Doing so apparently gives it access to the rest of the fonts that, for whatever reason, it doesn't natively have. (Found this info in a sparsely populated Google Groups discussion.)

For reference, my system is running Ubuntu 16.04, and Anaconda 4.4.8 with Python 3.6.4.

python my_script.py yields:

Font families without sudo

while sudo python my_script.py yields:

Font families with sudo

Oddly they don't overlap, but I'm frustrated enough with Anaconda at this point that I'm done investigating for now. Hope this (maybe) helps! It's a bad solution that's good enough for testing.

Week answered 21/3, 2018 at 21:21 Comment(2)
sudo python script runs the system's Python, that is correctly built against fontconfig and truetype. I'm afraid that I cannot upvote your answer...Lankton
You're absolutely correct; this is a total oversight on my part (and rather foolish to boot). I'm going to keep exploring this, but will update my answer.Week

© 2022 - 2024 — McMap. All rights reserved.