python – 如何在其默认程序中启动文件,然后在脚本完成时将其关闭?

概要

我有wxPython GUI,允许用户打开文件进行查看.目前我用os.startfile()这样做.不过,我来了解到这不是最好的方法,所以我正在寻求改进. startfile()的主要缺点是,一旦启动文件,我无法控制文件.这意味着用户可以将文件保持打开状态,否则将无法为其他用户使用.

我在找什么

在我的GUI中,可以有子窗口.我通过将GUI对象存储在列表中来跟踪所有这些,然后当父代关闭时,我只是运行列表并关闭所有的孩子.我想对用户选择的任何文件做同样的事情.如何启动文件并保留一个python对象,以便我可以关闭它的命令?提前致谢

我的梦想的解决方案

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

进步到目前为止

这是我计划使用的框架.重要的一点是FileThread类的run()和end()函数,因为这是解决方案的地方.

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)

但是当我轮询()对象时,它总是返回0.文档说“无”值表示进程尚未终止,所以0表示我的进程已经终止.因此,尝试kill()它什么都不做.

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

问题在于这样一个事实,即您的案例中Popen类处理的进程是启动shell命令进程,它在运行与给定文件类型相关联的应用程序之后终止.解决方案是将/ 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出现,文件打开.只要窗口打开,轮询返回None,因为/ WAIT标志.杀死开始的孩子后,Adobe Reader窗口消失.

其他可能的解决方案是找到与注册表中给定文件类型相关联的应用程序,并运行它,而不使用start和shell = True.

我在64位Windows Vista上的32位Python 2.7.5和64位Windows 7上的32位Python 2.7.2上测试过.下面是一个运行在后面的例子.我已经对您的代码进行了一些简单的调整 – 标有手绘红色圆圈(!).

另外,可能值得从documentation

The shell argument (which defaults to False) specifies whether to use the shell as the program to execute. If shell is True, it is recommended to pass args as a string rather than as a sequence.

并运行以下过程:

subprocess.Popen("start /WAIT " + self.file, shell=True)
翻译自:https://stackoverflow.com/questions/20777674/how-do-i-launch-a-file-in-its-default-program-and-then-close-it-when-the-script

转载注明原文:python – 如何在其默认程序中启动文件,然后在脚本完成时将其关闭?