Tkinter 嵌入图形子进程
Posted
技术标签:
【中文标题】Tkinter 嵌入图形子进程【英文标题】:Tkinter embed Graphical subprocess 【发布时间】:2013-12-27 08:25:21 【问题描述】:我正在尝试从 Tkinter 界面执行已编译的 Pygame 图形应用程序。但是,我希望 pygame 界面启动到 800x600 图形框架中。在用于 pygame 应用程序的 800x600 框架下方的同一根窗口中,我正在寻找一种嵌入方法:
一个 xterm,它自动启动位于名为“lib/”的子目录中的 python 可执行文件或
直接调用位于命名“lib/”文件夹中的可执行文件。
我在这个主题上找到的唯一有用的文档在这里: http://poultryandprogramming.wordpress.com/2012/10/28/embedded-terminal-in-pythontk-window/
os.system
路由是我看到任何形式的文档的唯一方法。
可以用subprocess.Popen
做我想做的事吗?
【问题讨论】:
【参考方案1】:这似乎是两个独立的问题。
首先,要在 Python 中直接运行可执行文件,您只需要 subprocess
模块即可。当你说你已经看过os.system
的文档时,我不知道你是怎么错过的,因为这清楚地表明:
subprocess
模块为生成新进程和检索其结果提供了更强大的工具;使用该模块优于使用此功能。请参阅subprocess
文档中的Replacing Older Functions with the subprocess Module 部分,了解一些有用的方法。
特别是,要在“lib/”文件夹中运行一些可执行文件,只需这样做:
p = subprocess.Popen(['lib/the_executable'])
但是,我猜你真的不希望当前工作目录中的lib
,而是主脚本所在目录中的lib
,对吧?为此,您需要在脚本启动时执行以下操作:
scriptdir = os.path.abspath(os.path.dirname(__file__))
…然后当你启动孩子时是这样的:
path = os.path.join(scriptdir, 'lib/the_executable')
p = subprocess.Popen([path])
无论如何,一旦你有一个Popen
对象,你可以检查p
是否仍在运行等,方法是每隔一段时间调用poll
,或者通过产生一个线程来阻塞wait
;你可以通过调用kill
来杀死它;等等。Popen
objects 上的文档显示了您可以做的所有事情。
如果您希望运行启动 Python 可执行文件的 xterm 可执行文件,您可以这样做——只需将 xterm
作为第一个参数传递,并将相关的 xterm 参数作为其余参数传递.
但我看不出这对你有什么好处。您不是试图在窗口中嵌入终端会话,而是游戏本身。当您从 xterm 会话启动 GUI 应用程序时,它不会在 xterm 窗口内运行;它在新窗口中运行。如果嵌入了 xterm 窗口,也会发生同样的事情。
至于在自己的窗口中嵌入 Pygame 窗口,有两种方法。
如果您自己编写 Pygame 游戏,正如 display.init
文档所说:
在某些平台上,可以将 pygame 显示嵌入到已经存在的窗口中。为此,必须将环境变量 SDL_WINDOWID 设置为包含窗口 ID 或句柄的字符串。初始化 pygame 显示时会检查环境变量。请注意,在嵌入式显示器中运行时可能会出现许多奇怪的副作用。
注意Popen
构造函数采用env
参数:
如果env不是
None
,则必须是为新进程定义环境变量的映射;使用这些而不是继承当前进程的环境,这是默认行为。
所以,你可以这样做:
child_env = dict(os.environ)
child_env['SDL_WINDOWID'] = the_window_id
p = subprocess.Popen([path], env=child_env)
问题是,你给它什么窗口ID?好吧,您发布的博客已经有了答案。与 xterm 的-into
相同的 ID。
博客没有解释如何将其限制在窗口的部分,但它所引用的代码必须。答案是(a)在主窗口中嵌入一个子窗口,并将子窗口的 ID 提供给子进程,或者(b)将整个窗口提供给子进程,然后让子窗口立即创建一个子进程。表面并且只绘制到那个而不是整个显示。
但是,对于 Pygame(或其他 SDL)应用程序的特殊情况,与 xterm 不同,在环境中设置 SDL_VIDEO_WINDOW_POS
也应该有效。 (据我所知,这不是 Pygame 的文档功能,但它是 SDL 的文档功能,所以它应该是可靠的。)
最终,您可能需要在这两个应用程序之间进行一些合作。像这样的东西:
Tkinter 父母:
child_env = dict(os.environ)
child_env['SDL_WINDOWID'] = the_window_id
child_env['SDL_VIDEO_WINDOW_POS'] = ','.format(left, top)
p = subprocess.Popen([path, width, height], env=child_env)
Pygame 孩子:
width, height = sys.argv[1:3]
pygame.display.init()
pygame.display.set_mode((width, height), pygame.NOFRAME)
如果您无法修改 Pygame 应用程序,事情就会变得更加棘手。您将必须创建嵌入到父窗口中的两个单独的窗口,或者以某种方式将两个***窗口停靠在一起。 (后者并不像在 X11 中听起来那么可怕。每当一个窗口移动时,以编程方式移动另一个窗口。)无论哪种方式,您都可以启动嵌入在一个子窗口中的 Pygame 应用程序,并将 Tkinter 的东西塞进另一个窗口。你也许可以通过 Tkinter 做到这一点;您可能必须直接进行 Xlib 调用(通过 ctypes
-ing 到 Xlib,或使用类似 python-xlib
的方法)。
【讨论】:
我将 pygame 应用程序嵌入到 Tk 积分中,但该应用程序不接受任何键盘或鼠标输入。我可以为此添加一个 SDL 变量吗? @AlexiK:我不知道您使用了哪种机制,您在哪个窗口管理器下运行等等。但是,我的第一个猜测是 SDL 正在窃取整个窗口中的所有关键事件,唯一的解决方法是(a)将其嵌入子窗口而不是主窗口的区域,或(b)在 Pygame 循环中编写代码以显式传递密钥。我不知道该怎么做 (b),而且您可能需要大量了解 SDL 事件循环才能做到这一点。 @AlexiK:或者,您可以将游戏渲染到屏幕外窗口而不是将其嵌入窗口中,然后让父应用程序从该窗口传输到主窗口的子窗口/区域窗口并将事件转发给孩子。 SDL FAQ 对此进行了一些讨论。 @AlexiK:最后,您可能还想查看 GtkPyGame 和 SugarGame 的代码(如果您能找到最新的链接),它们使用 PyGTK2 执行此操作(尽管我没有知道他们是否在使用嵌入、离屏渲染或 Gtk 套接字。)以上是关于Tkinter 嵌入图形子进程的主要内容,如果未能解决你的问题,请参考以下文章
使用带有 pylab/matplotlib 嵌入的 Tkinter 播放、暂停、停止功能的彩色绘图动画:无法更新图形/画布?