使Anaconda的tkinter能够识别系统字体或为Anaconda安装新字体

15

我在我的Debian Sid笔记本上安装了两个Python,⑴ 系统自带的Python(v.2.7),包含一些实用程序包(包括Tkinter)和⑵ Anaconda的Python 3。

很容易看出这两个Python版本有多少可用字体。

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

我觉得Anaconda的tkinter只查看了基本的X字体,该发行版附带的见下面的编辑。请问您知道以下替代方案中的哪一个能使Anaconda的tkinter知道系统字体(首选方案),或者安装一些字体到Anaconda树中以便tkinter使用吗?

  • 让Anaconda的tkinter知道系统字体(首选方案)或
  • 在Anaconda树中安装一些字体,以便tkinter可以使用它们?

tia


编辑 确实可用于Anaconda的字体是系统字体,但仅限于已知于xfontsel的字体,即字体路径中可以使用xset进行操作的字体。

我尝试了以下方法:

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

同时xfontsel显示了大约30个更多的字体系列。使用Python 3进行验证,我确认可用字体列表中仅添加了两个字体系列(即'go''gomono' - 没有'consolas'等), 并生成一个标签。

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

使用Python 2和Python 3都能成功,但Debian的Python显示了一个漂亮的抗锯齿文本,而另一个则是(粗糙的)位图渲染。

因此,在某种程度上,我已经部分回答了我的问题,但是:

  1. 并非所有字体系列,如xfontsel所示,都被tkinter接受。
  2. 即使对于极少数被识别的字体系列,呈现效果也远不能令人满意...

我想读到更好、更有用的答案。


1
我怀疑你的问题与以下问题描述的类似:Python3和tkinter中Linux字体的路径 - PicoutputCls
1
@PicoutputCls 我明白了,这些问题至少有点相似... 但是我打算保留我的问题,因为我觉得问题标题更加直接,可能会吸引更多的答案,你觉得呢? - gboffi
1
同意。我只是引用了这篇文章,因为我认为它可能会帮助你或其他人找到解决问题的方法。 - PicoutputCls
有没有FONTCONFIG_PATH/FONTCONFIG_FILE环境变量?你能检查一下类似'FONTCONFIG_PATH' in os.environ的东西吗? - CommonSense
@CommonSense 我已经检查过了,没有合适的(显然与fontconfig有关的)环境变量。 - gboffi
5个回答

11

{tT}kinter工作时会连接到一个Tk/Tcl解释器,这个解释器大致上包含在一对DLL中,特别是图形库是libtk6.0.so

大多数tkinter看不到的额外字体由Freetype库管理,而Anaconda的libtk6.0.so未构建为支持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
$

我尝试了以下可怕的事情

$ 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

最终想法。

  1. 替换DLL不是一个清洁的解决方案。
  2. 字体并不完全相同。 Anaconda肯定有自己的Fontconfig子系统,可能扫描的目录也不同,但我对字体数量上的差异没有正确的理解。
  3. 正确的做法是说服Anaconda公司使用Freetype构建libtk,但我不知道如何向他们报告,例如,如果我到https://www.anaconda.com/search/issues,我看到的是关于发行版的信息文章列表。

更新

关于第3点,我通过一个GitHub问题联系了Anaconda公司,被告知

不,我们不能这样做。 在构建我们的软件时,我们需要非常早地构建Python,远在任何图形化内容之前。 向tkinter添加Freetype作为依赖项会导致构建图中的循环,我们无法再构建发行版。

为什么不使用比tkinter更现代的东西呢?

                                                   --- Ray Donnelly (又名mingwandroid)


1
我已经找到了合适的频道,并在以下问题[tkinter fonts#6833](https://github.com/ContinuumIO/anaconda-issues/issues/6833)中发布了我的发现。 - gboffi
1
我还在包tk似乎没有内置TrueType支持#776中发布了相关内容,这似乎更为相关,并且被发行版的开发人员跟踪(或曾经跟踪)。 - gboffi
7
哇,你得到的回复既有帮助性又带侮辱性。天啊。 - erekalper
2
@erekalper 对于解决问题他们并没有提供太多帮助,但是他们明确表示不会修复问题。另一方面,我怀疑答案的价值,但我不想与那些坚定立场的人讨论这个问题。 - gboffi

2
问题如@gboffi所指出的那样,是因为Conda构建的Tk库不包含对Freetype库的支持,而Freetype库是用于渲染字体的库,导致您遇到的字体渲染问题。
他们认为由于循环依赖关系,他们无法包含Freetype支持在他们的构建过程中。他们推荐的解决方案是放弃tk,改用更现代的GUI库。
@UbuntuUser提出的建议是将Conda环境中的libtk8.6.so与系统的libtk8.6.so进行交换,这可能有效,但前提是libtk8.6.so引用的是完全相同版本的tk(例如,系统上的8.6.12与Conda上的8.6.13不匹配会导致错误)。
解决方案:

另一种方法是自己构建带有Freetype支持的Tcl/Tk库,然后在Conda环境中使用它们。这应该比较通用。以下是步骤:

  1. 安装必要的软件包
$ sudo apt-get install build-essential
$ sudo apt-get install libx11-dev libxft-dev

下载TclTk源代码(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

在接下来的步骤中,请确保使用与您下载的tk版本相关联的文本。
3. 提取源代码。
$ tar xzf tcl8.6.13-src.tar.gz
$ tar xzf tk8.6.13-src.tar.gz

激活您的Conda环境,并确认$CONDA_PREFIX输出正确的路径。
$ conda activate <your-env-name>
$ echo $CONDA_PREFIX

5. 构建并安装Tcl
$ cd tcl8.6.13/unix
$ ./configure --prefix=$CONDA_PREFIX
$ make
$ make install
$ cd ../..

6. 构建并安装Tk
$ cd tk8.6.13/unix
$ ./configure --prefix=$CONDA_PREFIX --with-tcl=$CONDA_PREFIX/lib
$ make
$ make install

如果一切顺利,您现在应该能够在您安装了tk的Conda环境中运行您的tk应用程序,并且它将具有漂亮的字体。以下是一个您可以在之前和之后运行的示例应用程序:
# 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()

干杯

0
五年过去了,这个问题仍然存在。Anaconda似乎没有解决这个问题的意愿。我正在使用带有Wayland的Ubuntu 22.04。以下是我采取的解决此问题的步骤。它似乎有效,但这种方法是否存在任何问题呢?

  1. 为Python 3.7设置conda环境,目前正在使用3.7.16版本

  2. 使用系统环境(Python 3.10.6)安装了tk包

    # apt install python3-tk
    
  3. 将第一个.so文件替换为第二个:

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

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

  4. 享受新字体吧!

以下是conda 3.7环境中.so文件的前后依赖关系:

$ 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)

除了想法和微小的细节之外,与我上面的答案有何不同? - gboffi
我认为我的问题属于您所说的“替换DLL”的范畴。我不确定您为什么使用符号链接?这是为了使其更加适应更新吗?就我的理解而言,我比较简单化地处理Linux,并且我的解决方案在许多计算机上都可以重复执行,并始终能够产生有效的结果。 - UbuntuUser

-1

我正在通过在不使用conda的环境中启动gitk来解决这个问题。虽然我对conda还比较新,但我不知道这样做是否会有微妙/隐藏的问题。

# 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 & )'

我没有给你的解决方案投反对票,但它大致相当于在你坐着的树枝上锯木头。投反对票的人可能是愿意使用Anaconda的Tkinter和漂亮字体的人。 - gboffi
1
(或者是那些向客户销售基于Anaconda+Tkinter解决方案的人,已经听够了他们抱怨字体不清晰的声音) - gboffi
@gboffi 谢谢。但这并不是砍掉我的分支,而是将gitk放到一个分支上,然后砍掉该分支,发现gitk就像一只依赖于自己的翅膀的鸟,不在乎分支是否存在。据我所知,gitk只需要git,与Anaconda环境无关。而且它在子shell中运行,不会影响其他任何东西。 - studog

-3

编辑:正如@gboffi所指出的,这个解决方案只是表面上看起来可行,因为sudo python并没有使用Anaconda的安装程序,而是使用了系统默认的程序。使用完整的Anaconda Python路径和sudo仍然会导致字体选项受限。我会继续探索这个问题,但目前来看,这个答案显然是不正确的。


我遇到了几乎完全相同的问题,而对我来说“解决”方法是使用sudo运行Anaconda的Python。这样做显然使其能够访问其他字体,而出于某种原因,它本身没有这些字体。(在一个Google Groups discussion中找到了这个信息,但该帖子回复很少。)

供参考,我的系统正在运行Ubuntu 16.04,Anaconda 4.4.8和Python 3.6.4。

python my_script.py输出:

Font families without sudo

当执行 sudo python my_script.py 时,会产生以下结果:

Font families with sudo

奇怪的是它们不重叠,但我对Anaconda已经感到足够沮丧了,所以现在我不想再去调查了。希望这个(也许)有所帮助!这是一个不好的解决方案,但对于测试来说已经足够好了。


sudo python script 运行系统的 Python,该 Python 正确构建了 fontconfig 和 truetype。很抱歉我不能为您的答案点赞... - gboffi
你说得完全正确;这是我疏忽大意(而且相当愚蠢)的地方。我会继续探索这个问题,并更新我的答案。 - erekalper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接