概要
我有wxPython GUI,它允许用户打开文件来查看。 目前我用os.startfile()
做到这一点。 不过,我已经知道这不是最好的方法,所以我正在寻求改进。 startfile()
的主要缺点是一旦启动文件,就无法控制文件。 这意味着用户可以将文件保留为打开状态,以便其他用户不可用。
我在找什么
在我的GUI中,可以有孩子的窗户。 我通过将GUI对象存储在列表中来跟踪所有这些,然后当父对象closures时,我只是遍历列表并closures所有的子对象。 我想对用户select的任何文件做同样的事情。 我怎样才能启动一个文件,并保留一个python对象,以便我可以closures它的命令? 提前致谢
我的一个解决scheme的梦想
目前的进展
这是我打算使用的框架。 重要的位是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()
一些额外的笔记
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窗口消失。
其他可能的解决办法是找到与注册表中给定文件类型相关的应用程序,并运行它,而不使用start
和shell=True
。
我已经在64位Windows Vista上的32位Python 2.7.5和64位Windows 7上的32位Python 2.7.2上测试了这个。下面是在后者上运行的一个例子。 我已经对你的代码做了一些简单的调整 – 用一个徒手画的红圈(!)标记。
另外,可能值得从文档中考虑这个评论:
shell参数(默认为False)指定是否使用shell作为要执行的程序。 如果shell为True,建议将args作为字符串传递,而不是作为序列传递。
并运行过程如下:
subprocess.Popen("start /WAIT " + self.file, shell=True)