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

标签 python windows shell subprocess popen

总结

我有 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)

但是当我poll() 对象时,它总是返回0。文档说 A None 值表示进程尚未终止 所以 0 表示我的进程已终止。因此,尝试 kill() 它什么都不做。

我怀疑这是因为当 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 出现并打开了文件。由于 /WAIT 标志,只要窗口打开,poll 就会返回 None。终止 start 的子 Adob​​e Reader 窗口后消失。

其他可能的解决方案是在注册表中找到与给定文件类型关联的应用程序,并在不使用 startshell=True 的情况下运行它。

我已经在 64 位 Windows Vista 上的 32 位 Python 2.7.5 和 64 位 Windows 7 上的 32 位 Python 2.7.2 上对此进行了测试。下面是在后者上运行的示例。我对您的代码进行了一些简单的调整 - 用手绘红色圆圈 (!) 标记。

step1 step2 step3 step4

此外,可能值得考虑来自 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)

关于python - 如何在其默认程序中启动文件,然后在脚本完成时关闭它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20777674/

相关文章:

python - GAE 错误 :-/bin/sh: 1: exec: gunicorn: not found

python - 如何在此脚本中找到查找结果以通过电子邮件发送

PHP:可以将 Web 应用程序编译成 Windows 应用程序吗?

windows - docker-compose run 在 Windows 中不起作用

python - python odoo中的循环时间

python - 使用关闭按钮在 DJango 中显示 flash 消息

windows - 如何修改一个exe文件以强制它在没有源代码的情况下以管理员身份启动

bash - 在 Bash 注销时保存最后一个工作目录

python - 为什么当我在 Mac OS X 终端应用程序中键入时不显示标准输入?

shell - 无法从 gitlab-ci.yml 推送