Tkinter嵌入图形子过程
问题内容:
我正在尝试从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
吗?
问题答案:
这似乎是两个独立的问题。
首先,要直接在Python中运行可执行文件,您只需要subprocess
模块。我说您看过的文档时不知道您怎么会错过这一点os.system
,因为这显然表明:
该
subprocess
模块提供了更强大的功能来生成新流程并检索其结果。使用该模块优于使用此功能。有关一些有用的食谱,请参见文档中的用子过程模块替换较早的功能部分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
对象的文档显示了您可以做的所有事情。
如果您希望运行可启动Python可执行文件的xterm可执行文件,则 可以 执行此操作-
只需将其xterm
作为第一个参数传递,并将相关的xterm参数作为其余参数传递。
但是我看不到有什么好处。您不是要在窗口中嵌入终端会话,而是游戏本身。从xterm会话启动GUI应用程序时,它不会在xterm窗口中运行;而是在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相同的ID -into
。
该博客没有解释如何将其限制在窗口的 一部分
,但是它所引用的代码必须。答案是:(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
)。