Tkinter嵌入了图形子流程

时间:2013-12-10 00:51:51

标签: python tkinter embed subprocess

我正在尝试从Tkinter接口执行已编译的Pygame Graphical应用程序。但是,我希望pygame界面启动到800x600图形框架。在用于pygame应用程序的800x600帧下面的同一根窗口中,我正在寻找一种嵌入方式:

  1. 启动python可执行文件的xterm,它自动位于名为“lib /”的子目录中

  2. 直接调用位于命名的“lib /”文件夹中的可执行文件。

  3. 我在这个主题上找到的唯一有用的文档是: http://poultryandprogramming.wordpress.com/2012/10/28/embedded-terminal-in-pythontk-window/

    os.system路线是我看到任何形式的文档的唯一方法。

    是否可以使用subprocess.Popen执行我想要的操作?

1 个答案:

答案 0 :(得分:4)

这似乎是两个不同的问题。


首先,要在Python中直接运行可执行文件,您只需要subprocess模块。当你说你看过os.system的文档时,我不知道你是怎么错过的,因为这清楚地说明了:

  

subprocess模块提供了更强大的工具来生成新流程并检索其结果;使用该模块比使用此功能更可取。有关一些有用的配方,请参阅Replacing Older Functions with the subprocess Module文档中的subprocess部分。

特别是,要在“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 - 使用Xlib,或使用python-xlib之类的内容。