如何在默认程序中启动文件,然后在脚本结束时closures文件?

概要

我有wxPython GUI,它允许用户打开文件来查看。 目前我用os.startfile()做到这一点。 不过,我已经知道这不是最好的方法,所以我正在寻求改进。 startfile()的主要缺点是一旦启动文件,就无法控制文件。 这意味着用户可以将文件保留为打开状态,以便其他用户不可用。

我在找什么

在我的GUI中,可以有孩的窗户。 我通过将GUI对象存储在列表中来跟踪所有这些,然后当父对象closures时,我只是遍历列表并closures所有的子对象。 我想对用户select的任何文件做同样的事情。 我怎样才能启动一个文件,并保留一个python对象,以便我可以closures它的命令? 提前致谢

我的一个解决scheme的梦想

  • 以这样一种方式启动文件,即可以在函数之间传递一个Python对象
  • 以某种方式在默认程序中启动文件并返回PID
  • 一种用文件名检索PID的方法

目前的进展

这是我打算使用的框架。 重要的位是FileThread类的run()end()函数,因为这是解决scheme将要去的地方。

 import wx from wx.lib.scrolledpanel import ScrolledPanel import threading import os class GUI(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, 'Hey, a GUI!', size=(300,300)) self.panel = ScrolledPanel(parent=self, id=-1) self.panel.SetupScrolling() self.Bind(wx.EVT_CLOSE, self.OnClose) self.openFiles = [] self.openBtn = wx.Button(self.panel, -1, "Open a File") self.pollBtn = wx.Button(self.panel, -1, "Poll") self.Bind(wx.EVT_BUTTON, self.OnOpen, self.openBtn) self.Bind(wx.EVT_BUTTON, self.OnPoll, self.pollBtn) vbox = wx.BoxSizer(wx.VERTICAL) vbox.Add((20,20), 1) vbox.Add(self.openBtn) vbox.Add((20,20), 1) vbox.Add(self.pollBtn) vbox.Add((20,20), 1) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(vbox, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.EXPAND, border = 10) self.panel.SetSizer(hbox) self.panel.Layout() def OnOpen(self, event): fileName = "AFileIWantToOpenWithTheFullPath.txt" self.openFiles.append(FileThread(fileName)) def OnPoll(self, event): self.openFiles[0].Poll() def OnClose(self, event): for file in self.openFiles: file.end() self.openFiles.remove(file) self.Destroy() class FileThread(threading.Thread): def __init__(self, file): threading.Thread.__init__(self) self.file = file self.start() def run(self): doc = subprocess.Popen(["start", " /MAX", "/WAIT", self.file], shell=True) return doc def Poll(self): print "polling" print self.doc.poll() print self.doc.pid def end(self): try: print "killing file {}".format(self.file) except: print "file has already been killed" def main(): app = wx.PySimpleApp() gui = GUI() gui.Show() app.MainLoop() if __name__ == "__main__": main() 

一些额外的笔记

  • 我不关心可移植性,只能在办公室的几台受控计算机上运行
  • 我不认为这很重要,但我通过batch file运行pythonw可执行文件

更新

我用subprocess.Popen()玩了一下,但遇到了同样的问题。 我可以使用Popen对象

 doc = subprocess.Popen(["start", "Full\\Path\\to\\File.txt"], shell=True) 

但是当我poll()对象时,它总是返回0 。 文档说, A None value indicates that the process hasn't terminated yet所以0意味着我的过程已经终止。 因此,试图kill()它什么都不做。

我怀疑这是因为当start命令完成并start文件时,进程完成。 我想要的东西,即使在文件启动后,会继续下去,这可以用Popen()吗?

问题在于,在你的情况下, Popen类处理的进程是start shell命令进程,在运行与给定文件类型关联的应用程序之后,进程就会终止。 解决方法是对start命令使用/WAIT标志,因此start进程等待其子进程终止。 然后,使用例如psutil模块,您可以通过以下顺序实现您想要的功能:

 >>> import psutil >>> import subprocess >>> doc = subprocess.Popen(["start", "/WAIT", "file.pdf"], shell=True) >>> doc.poll() >>> psutil.Process(doc.pid).get_children()[0].kill() >>> doc.poll() 0 >>> 

打开文件后,出现第三行Adobe Reader。 只要窗口打开, poll返回None ,这要归功于/WAIT标志。 杀害start的孩子Adobe Reader窗口消失。

其他可能的解决办法是找到与注册表中给定文件类型相关的应用程序,并运行它,而不使用startshell=True

我已经在64位Windows Vista上的32位Python 2.7.5和64位Windows 7上的32位Python 2.7.2上测试了这个。下面是在后者上运行的一个例子。 我已经对你的代码做了一些简单的调整 – 用一个徒手画的红圈(!)标记。

步骤1第2步第三步:步骤4

另外,可能值得从文档中考虑这个评论:

shell参数(默认为False)指定是否使用shell作为要执行的程序。 如果shell为True,建议将args作为字符串传递,而不是作为序列传递。

并运行过程如下:

 subprocess.Popen("start /WAIT " + self.file, shell=True)