摘要
我有wxPython GUI,允许用户打开要查看的文件。目前我使用os.startfile()
执行此操作。但是,我已经知道这不是最好的方法,所以我希望改进。 startfile()
的主要缺点是,一旦启动文件,我就无法控制文件。这意味着用户可以将文件保持打开状态,以便其他用户无法使用。
我在寻找什么
在我的GUI中,可以有子窗口。我通过将GUI对象存储在列表中来跟踪所有这些,然后当父对象关闭时,我只是遍历列表并关闭所有子对象。我想对用户选择的任何文件执行相同操作。如何启动文件并保留python对象,以便我可以在命令中关闭它?提前致谢
我的解决方案梦想
进展到目前为止
这是我计划使用的框架。重要的位是run()
类的end()
和FileThread
函数,因为这是解决方案的用途。
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
命令完成并且文件启动时,该过程完成。我希望即使在文件启动后仍会继续运行,可以使用Popen()
吗?
答案 0 :(得分:6)
问题在于,在您的情况下由Popen
类处理的进程是start
shell命令进程,该进程在运行与给定文件类型关联的应用程序之后终止。解决方案是对/WAIT
命令使用start
标志,因此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。下面是对后者运行的示例。我对你的代码进行了一些简单的调整 - 用手绘红色圆圈(!)标记。
此外,可能值得考虑来自documentation:
的评论shell参数(默认为False)指定是否将shell用作要执行的程序。如果shell为True,建议将args作为字符串而不是序列传递。
并按如下方式运行该过程:
subprocess.Popen("start /WAIT " + self.file, shell=True)