如何在默认程序中启动文件,然后在脚本完成后将其关闭? [英] How do I launch a file in its default program, and then close it when the script finishes?

查看:97
本文介绍了如何在默认程序中启动文件,然后在脚本完成后将其关闭?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘要

我有wxPython GUI,它允许用户打开文件进行查看.目前,我使用os.startfile()进行此操作.但是,我发现这不是最好的方法,因此我正在寻求改进. startfile()的主要缺点是,一旦启动该文件,我将无法对其进行控制.这意味着用户可以使文件保持打开状态,因此其他用户将无法使用该文件.

I have wxPython GUI which allows the user to open files to view. Currently I do this with os.startfile(). However, I've come to learn that this is not the best method, so I'm looking to improve. The main drawback of startfile() is that I have no control over the file once it has been launched. This means that a user could leave a file open so it would be unavailable for another user.

我在寻找什么

在我的GUI中,可能会有子窗口.我通过将GUI对象存储在列表中来跟踪所有这些对象,然后在关闭父对象时,我会遍历列表并关闭所有子对象.我想对用户选择的任何文件执行相同的操作.如何启动文件并保留python对象,以便可以在命令中将其关闭?预先感谢

In my GUI, it is possible to have children windows. I keep track of all of these by storing the GUI objects in a list, then when the parent is closed, I just run through the list and close all the children. I would like to do the same with any file the user selects. How can I launch a file and retain a python object such that I can close it on command? Thanks in advance

我对解决方案的梦想

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

到目前为止的进展

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

Here's the frame I plan on using. The important bits are the run() and end() functions of the FileThread class as this is where the solution will go.

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可执行文件
  • I'm not concerned with portability, this will only be run on a few controlled computers around the office
  • I don't think this matters, but I'm running the pythonw executable through a batch file

更新

我在subprocess.Popen()上玩了一点,但是遇到了同样的问题.我可以使用

I played around a bit with subprocess.Popen(), but ran into the same issue. I can make the Popen object using

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()不会执行任何操作.

but when I poll() the object, it always returns 0. The docs say that A None value indicates that the process hasn’t terminated yet so the 0 means that my process has terminated. Thus, attempting to kill() it does nothing.

我怀疑这是因为在start命令完成并启动文件后,该过程完成了.我想要即使文件启动后仍能继续运行的东西,可以用Popen()完成吗?

I suspect this is because the process completes when the start command finishes and the file is launched. I want something that will keep going even after the file is launched, can this be done with Popen()?

推荐答案

问题在于一个事实,在您的情况下,由Popen类处理的进程是start shell命令进程,该进程仅终止在运行与给定文件类型关联的应用程序之后.解决方案是对start命令使用/WAIT标志,因此start进程等待其子进程终止.然后,例如使用 psutil 模块,可以按照以下顺序实现您想要的:

The problem lies within the fact, that the process being handled by the Popen class in your case is the start shell command process, which terminates just after it runs the application associated with given file type. The solution is to use the /WAIT flag for the start command, so the start process waits for its child to terminate. Then, using for example the psutil module you can achieve what you want with the following sequence:

>>> 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窗口后消失.

After the third line Adobe Reader appears with the file opened. poll returns None as long as the window is open thanks to the /WAIT flag. After killing start's child Adobe Reader window disappears.

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

Other probably possible solution would be to find the application associated with given file type in the registry and run it without using start and shell=True.

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

I've tested this on 32 bit Python 2.7.5 on 64 bit Windows Vista, and 32 bit Python 2.7.2 on 64 bit Windows 7. Below is an example run on the latter. I've made some simple adjustments to your code - marked with a freehand red circles (!).

此外,也许值得考虑来自文档的注释:

Also, possibly it is worth to consider this comment from the documentation:

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

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.

并按如下所示运行该过程:

and run the process as follows:

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

这篇关于如何在默认程序中启动文件,然后在脚本完成后将其关闭?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆