先前信息:我在 Mac 上。
问:如果我用 py2app 编译它以重新分发,如何从程序执行中获得类似终端的文本输出?
我的案例是一个复制大量大文件并需要一段时间来处理的程序,所以我希望每次复制每个文件时至少有一个输出通知。
如果我在命令行上运行它,这很容易,我可以打印一个新行。
但是当我做一个自给自足的包时,它只是在底部的底座上打开,没有窗口,完成后关闭。
一个简单的文本窗口就可以了。
提前致谢。
先前信息:我在 Mac 上。
问:如果我用 py2app 编译它以重新分发,如何从程序执行中获得类似终端的文本输出?
我的案例是一个复制大量大文件并需要一段时间来处理的程序,所以我希望每次复制每个文件时至少有一个输出通知。
如果我在命令行上运行它,这很容易,我可以打印一个新行。
但是当我做一个自给自足的包时,它只是在底部的底座上打开,没有窗口,完成后关闭。
一个简单的文本窗口就可以了。
提前致谢。
如果你想创建一个简单的文本窗口,你需要选择一个 GUI 框架来做这件事。对于这么简单的事情,没有理由不使用Tkinter
(任何 Python 附带的)或PyObjC
(Apple 的 Python 2.7 预装的),除非您碰巧更熟悉wx
, gobject
,Qt
等。
无论如何,无论你怎么做,你都需要编写一个函数来接收消息并将其附加到文本窗口(如果需要,可能会懒惰地创建它),然后在你通常会调用的任何地方调用该函数print
。你可能还想编写和安装一个logging
处理程序来做同样的事情,这样你就可以了log.info
。(您可以改为创建一个类似文件的对象来执行此操作并重定向stdout
和/或stderr
,但除非您无法控制print
ing 代码,否则这将是更多的工作。)
这里唯一真正的问题是 GUI 需要一个事件循环,而您可能只是将代码编写为顺序脚本。
解决此问题的一种方法是将整个当前脚本变成后台线程。如果您使用的是允许您从后台线程访问小部件的 GUI 库,那么一切都很容易;你printfunc
就是这样做的textwidget.append(msg)
。如果没有,它可能至少有一个call_on_main_thread
类型函数,所以你printfunc
的call_on_main_thread(textwidget.append, msg)
. 如果最坏的情况变得最糟(我相信 Tkinter 确实如此),您必须创建一个显式队列来推送消息,并在事件循环中编写一个队列处理程序。这个食谱应该给你一个想法。用您的代码替换正文workerThread
,并以 . 结尾self.endApplication()
。(那里可能有更好的例子;这正是我在快速搜索中首先找到的。)
另一种方法是让您的代码与事件循环协同操作。一些库,比如wx
,SafeYield
如果你在每个处理块之后调用它,就可以让事情正常工作。其他人没有,但有办法从您的代码中显式驱动事件循环。其他人都没有——但每个事件循环框架都必须有一种方法来安排新事件,因此您可以将代码分解为一系列函数,每个函数都快速完成,然后执行类似root.after_idle(nextfunc)
.
但是……您确定需要这样做吗?
首先,任何应用程序,包括由 . 创建的应用程序,py2app
如果您使用Foo.app/Contents/MacOS/Foo
. 如果您愿意,您甚至可以进行设置,使其以open Foo.app
这种方式工作。显然,这对于只在 Finder 中双击应用程序的人没有帮助(因为那时没有终端),但有时只需在人们需要时输出可用并知道如何遵循说明就足够了。
您还可以更进一步:创建一个Foo.command
类似 的文件,$(dirname $0)/Foo.app/Contents/MacOS/Foo
当您双击该文件时,它会启动 Terminal.app 并运行您的脚本。
或者您可以变得更简单:只需使用logging
syslog 输出,如果您想查看每个文件何时完成,只需查看日志消息在Console.app
.
最后,你甚至py2app
首先需要吗?如果您没有任何外部依赖项,只需将您的脚本重命名为Foo.command
,然后双击它将在 Terminal.app 中运行它。如果您确实有外部依赖项,您可能仍然可以将它们捆绑在一起作为一个文件夹,其中包含一个.command
而不是一个.app
.
显然,这些想法都不是构建界面的专业或新手友好方式,因此如果这很重要,您将不得不创建一个 GUI。