Python Forum
Launch pdf and close on delete window event - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: Launch pdf and close on delete window event (/thread-9110.html)



Launch pdf and close on delete window event - ashtona - Mar-21-2018

Hello I'm currently working on a GUI tool in python that launches PDF files from an SQL server. Right now I write the binary to a file and then launch it using subprocess Popen, I track the file name and store the actual subprocess object in a list. When the WM_DELETE_WINDOW event is called I iterate over the child processes killing them and then use os.remove to delete the files.

Here is my code. I can launch the PDF files fine but I can't get the files to close, I receive two errors (ProcessLookupError, psutil.NoSuchProcess: psutil.NoSuchProcess process no longer exists). My file also won't delete as the process is still in use.

 #destroy window and clean up after self
        self.window.destroy()
        #start by killing any processes
        for process in self.processes:
            for child in psutil.Process(process.pid).children():
                child.kill()
            process.kill()
        for file in self.saved_files:
            os.remove(file)
Any help is appreciated thanks.


RE: Launch pdf and close on delete window event - woooee - Mar-21-2018

How are you getting the subprocess' pids? Note that psutil can only kill a process that is a child of the program. See if the code below prints anything useful and if so try killing the uid's as they are psuitl processes and not subprocesses processes (glad I don't have to try and say that). I use multiprocessing in these types of situations so can post an example using multiprocessing if you can't get subprocess to work.

for each_pid in pids:
    p = psutil.Process(each_pid)
    p_name  = p.name()
    uid = p.uids().real
    print(p.name, p, uid) 



RE: Launch pdf and close on delete window event - ashtona - Mar-21-2018

It errors at
p = psutil.Process(process.pid)
I get, psutil.NoSuchProcess: psutil.NoSuchProcess no process found with pid 2500.
I get the pids from the sub process object and append them to a list
doc = Popen("start /WAIT " + self.SectionList.get(element), shell=True)
self.processes.append(doc)
So when the window destruction event is closed I just iterate over them.
If I understand you correctly I shouldn't be using psutil to kill subprocess objects?
If so what is my other option?

Thanks for your feedback, this has been stumping me quite a bit.


RE: Launch pdf and close on delete window event - woooee - Mar-21-2018

There is something it doesn't like about the pid and I don't know where the pid is coming from. Because we use what we know, I use multiprocessing (example below) because I know multiprocessing works, and because multiprocessing will use multiple cores when available, so try modifying this code to fit you, and see if it works any better, and obviously post back if it doesn't.
## very basic and simple but should do the trick

import multiprocessing
import os
import psutil
import time


def killtree(pid, including_parent=True):
    parent = psutil.Process(pid)
    for child in parent.children(recursive=True):
        print("killing child", child)
        child.kill()
    if including_parent:
        parent.kill()

def print_numbers(spaces):
    ctr = 0
    for x in range(11):
        ctr +=1
        print(" "*spaces, ctr)
        time.sleep(0.5)

## do this first as it gets the latest pid started
## should be the pid of this program
## if you wait and the OS starts another process, you wil get that pid
pid=os.getpid()

list_of_multis=[]
for ctr in range(5):
    list_of_multis.append(multiprocessing.Process(target=print_numbers, args=(ctr,)))
    list_of_multis[-1].start()

## wait 2 seconds and kill all processes
time.sleep(2)
killtree(pid) 

And I got so caught up in psutil that I didn't post terminating each process. This is better than psutil because you can join() each process, which allows anything in the background to be cleaned up.
import multiprocessing
import time

def print_numbers(spaces):
    ctr = 0
    for x in range(11):
        ctr +=1
        print(" "*spaces, ctr)
        time.sleep(0.5)

list_of_multis=[]
for ctr in range(5):
    list_of_multis.append(multiprocessing.Process(target=print_numbers, args=(ctr,)))
    list_of_multis[-1].start()

## wait 2 seconds and kill all processes
time.sleep(2)
for process in list_of_multis:
    process.terminate()
    process.join()
    print(process, "terminated")



RE: Launch pdf and close on delete window event - ashtona - Mar-22-2018

Ok here is the code I have now. I use multiprocessing to launch the pdf, however it's still not closing my file.
def LaunchPDF(filename):
    """
    Launches PDF by filename and tracks PID
    @param filename, the name of the file to launch
    @return None
    """
    print(filename)
    os.startfile(filename)

def QueryShubertFiles(self):
    """
    Queries the blob data from table based on filename.
    @param, self the App object.
    @return None.
    """
    for element in self.SectionList.curselection():
       value = self.Query("SELECT Data FROM ShubertDocs WHERE Filename = '" + self.SectionList.get(element) + "';")
       if ".JPG" in self.SectionList.get(element):
           bio = BytesIO(value[0][0])
           Image.open(bio).show()
       elif ".PDF" in self.SectionList.get(element) or ".pdf" in self.SectionList.get(element):
           #track files saved to disk
           self.saved_files.append(self.SectionList.get(element))
           try:
               with open(self.SectionList.get(element), 'wb') as f:
                   f.write(value[0][0])
           except:
               pass
           #doc = Popen("start /WAIT " + self.SectionList.get(element), shell=True)
           self.processes.append(multiprocessing.Process(target=LaunchPDF, args=(self.SectionList.get(element),)))
           self.processes[-1].start()


def on_closing(self):
        self.window.destroy()
        
        ## wait 2 seconds and kill all processes
        time.sleep(2)
        print(self.processes)
        for process in self.processes:
            process.terminate()
            process.join()
            print(process, "terminated")

        #clean up 
        for file in self.saved_files:
            os.remove(file)
When I print the processes it tells me they are all stopped, then when I attempt to remove the files I receive a process is still being used. The pdf never closes.


RE: Launch pdf and close on delete window event - Gribouillis - Mar-22-2018

Not a windows user here but instead of opening the pdf with os.startfile(), which creates another process out of the reach of your program, you could launch directly a specific pdf viewer with the subprocess module. For example supposing you open pdf files with Acrobat Reader, it could be
viewer = shutil.which('AcroRd32')
proc = Popen([viewer, filename])
processes.append(proc)



RE: Launch pdf and close on delete window event - ashtona - Mar-22-2018

Thanks for the help. I resolved this issue by adding one line of code, by checking to see if the sub process object is still running using process.poll().
 for process in self.processes:
            if process.poll() == None:
                for child in psutil.Process(process.pid).children():
                    child.kill()
                process.kill()