Summary
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.
What I'm Looking For
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
My Dreams of a Solution
- Launch the file in such a way that there is a Python object which I can pass between functions
- Some way to launch a file in its default program and return the PID
- A way to retrieve the PID with the file name
Progress So Far
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()
Some Extra Notes
- 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
Update
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)
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.
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()
?
See Question&Answers more detail:
os