wx.Font使用字体名称而PIL.ImageFont使用文件名... 有什么方法可以关联它们吗?

2 投票
2 回答
1068 浏览
提问于 2025-04-15 18:06

大家好,抱歉,这个问题有点复杂.. ;)

我现在遇到一个烦人的小问题。我正在为工作开发一个类似RSS的应用程序,这个应用会在办公室的一些大液晶显示屏上展示。基本的想法是建立一个客户端/服务器的架构,用户可以使用一个自定义的编辑器来创建各个文章,选择自己喜欢的字体、粗细、大小等,然后上传到服务器,服务器会把这些文本显示在液晶屏上,最开始是用wx.TextCtrl和wx.TextAttr来实现的。

问题来了..

我已经顺利地搭建了客户端和服务器,服务器的显示也正常工作,但我找不到一种方法可以让文本从右向左流动而不出现闪烁。我们的显示屏是42英寸的,我尝试了很多方法来实现这个目标,但由于这些显示屏需要的文本大小,我就是无法消除闪烁。

到目前为止,我尝试了三种方法来让文本动起来:

方法1:wx.Ticker()

这是一个看起来能完美实现我目标的控件,当我使用较小的字体时,它确实能做到我想要的。但是问题是我们不能使用小字体,我们使用的字体会很大,比如36、48、72等。在测试wxpython演示时,我无法创建出足够平滑的效果,甚至在稍微调整他们的代码后也没能达到我的要求。

方法2:FadingTextCtrl()(自定义控件)

早在我找到wx.Ticker之前,我就创建了一个控件来实现这个功能,并且在之前的一些项目中成功使用过。基本上,我设置了一个带有EVT_TIMER的TextCtrl,把光标放在行的开头,然后不断删除文本的第一个字符,速度由EVT_TIMER控制。这个方法意外地减少了闪烁的频率,但当它闪烁时,效果就显得特别明显。另一个缺点是你需要使用等宽字体,以避免删除字符时的跳动。如果不使用等宽字体,一些字符会因为宽度不同而删除得快慢不一,导致文本的运动看起来时快时慢,这让速度控制变得不可能。

方法3:wx.AnimatedCtrl()和自定义生成的动画GIF

到目前为止,这似乎是我最好的选择。并且我离成功真的很近了!!!

通过使用PIL和一些我在谷歌上找到的随机代码片段,我成功生成了一个动画GIF,并把它放入wx.AnimatedCtrl()中。在大屏幕上测试后,终于看起来我找到了解决方案,文本流畅地移动,没有任何闪烁!在为GIF添加更多帧后,文本的移动比之前的任何方法都要平滑。所以到这里为止,没有闪烁,完全流畅,然后我遇到了一个非常棘手的冲突...

在构建了一个可以按需创建图像的函数后,我回到我的GUI,开始链接所有控件,收集所有文本及其属性来创建动画GIF。我准备把所有信息推送到我的函数时,突然意识到在wx中创建字体的方式与在PIL中不同,特别是如何指定你打算使用的字体。例如,wx.Font()使用字体的名称来指定要使用的字体,而PIL.ImageFont则使用字体的路径/文件名来选择绘制的字体。

你在开玩笑吗.....我现在离完成项目只差一步,却发现无法关联我的字体。我可以推送其余的字体信息,但我无法告诉PIL.ImageFont使用wx.Font()提供的字体数据。

所以最后,我的问题是... 如何将wx.Font()的字体名称与它们的实际文件名关联起来,以便我能找到方法告诉PIL.ImageFont使用我在控件中定义的文本属性?

昨晚我以为找到了关键,当我发现注册表键:

"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"

这个键包含了一堆字符串值,定义了Windows可用的字体名称和相关的文件路径。这是控制面板>>字体使用的信息,所以你会认为这就是我缺失的环节。我迅速构建了另一个模块来索引这些值,并生成所选的字体名称/文件名。这对于TTF(TrueType)字体似乎工作得很好。

然后又遇到了一个问题..

大多数字体名称使用多个字体文件来产生斜体、粗体等效果,但在wx.Font()中,似乎总是使用一个字体名称,比如“Tahoma”,并通过其他参数如样式、粗细等来指定效果。因此,对于常规的Tahoma,你会使用字体文件“TAHOMA.TTF”,但要使用粗体的Tahoma,你会使用字体文件“tahomabd.ttf”。也就是说,注册表中找到的字体名称与wxPython中使用的字体名称并不完全匹配。例如:注册表中Tahoma的字体名称可以是Tahoma(TrueType)或Tahoma Bold(True Type),而wx.Font()只需要“Tahoma”就可以了,它会在后台使用你提供的其他参数匹配合适的字体文件。虽然我很容易去掉注册表字体名称中的垃圾文本,比如(TrueType)或(VGA res),但对于某些字体,我仍然会留下多个字体名称,这就不太好。

现在这是我目前的情况。

我使用PIL创建我的动画GIF,方法是创建一个画布,设置所有文本属性:

  1. 创建两个列表,一个用于文本,一个用于每个文本项的属性。例如:

    msgs = ["*** ", "标题: ", "要打印到图像文件的消息正文。还有什么好说的呢??真的?!还有什么?!?", " ***"]

    fonts = [(font_bold_file, 48, "#000000"), (font_bold_file, 48, "#000000"), (font_norm_file, 48, "#000000"), (font_bold_file, 48, "#000000")]

  2. 创建一个空白画布,宽度是文本宽度的两倍。

  3. 遍历列表,在画布上写入文本,右对齐,每帧稍微向左移动文本的posX。

为了避免wx.Font和PIL.ImageFont之间的字体名称/文件名冲突,我在想我应该先用wxPython创建一个包含文本及其所有属性的位图,然后将其转换为PIL图像。然后,不是用PIL.ImageFont绘制文本,每帧稍微移动文本的位置,而是应该将转换后的wxBitmap粘贴到画布上,每帧稍微移动整个位图。

我现在遇到的问题是,如何正确使用wx的DC(ClientDC、PaintDC、(自动)BufferedDC、MemoryDC)来创建位图。目前我能够使用wxMemoryDC和wxBitmap将文本写入位图,但我还没有找到方法将位图的完整虚拟大小设置为包含整个图像,因为它似乎被限制在窗口的ClientSize内。我本来想从一开始就指定完整的大小,但在获取消息的文本宽度之前我没有这个信息。长远来看,我更希望创建wxBitmap而不在控件中显示,因为它的唯一目的是传递给PIL进行进一步处理。

所以在这里我请求帮助!!能不能请大家帮我想想,如何将wx.Font与PIL.ImageFont关联起来,这样我就可以让我的动画GIF生成器正常工作,或者帮我想想如何使用wxPython创建位图,并在其中绘制文本,以确保所有文本都在位图中,无论当前的ClientSize是什么?我可以轻松将位图转换为PIL,我只需要先弄清楚如何在wx中创建它!

非常感谢任何能够提供帮助的人,抱歉发了这么长的帖子,但我本该几周前就完成这个项目。这个令人困惑的字体问题每次我觉得快解决时就又来捣乱!因为这是一个工作项目,如果你需要任何代码示例,请告诉我,我会准备一些没有工作相关信息的代码。

2 个回答

1

你有没有考虑过使用 wx.MemoryDC 和一个双缓冲窗口(也就是“位块传输”)呢?这个基本的技巧是,你先在屏幕外的地方把文字画好,然后再把它“传输”到屏幕上。这样做可以消除闪烁的问题。

这里有一篇关于如何消除闪烁的讨论,来自wx的维基百科:消除闪烁的讨论

还有一篇关于双缓冲的讨论,来自wxpython的维基百科:双缓冲讨论

0

很久以前就该回答这个问题了...

关于字体的关系,长远来看,我没能找到一个可行的方法来完成这个任务。

根据Christopher的建议,我成功生成了一个带有指定文本的动画GIF,这在一定程度上解决了运动的问题。

不过很遗憾,我无法提供示例,因为这个项目是我几年前为一家公司做的,现在没有办法访问源代码。

撰写回答