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