Python Forum
Launch pdf and close on delete window event
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Launch pdf and close on delete window event
#1
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.
Reply
#2
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) 
Reply
#3
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.
Reply
#4
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")
Reply
#5
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.
Reply
#6
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)
Reply
#7
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()
                
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Interaction between Matplotlib window, Python prompt and TKinter window NorbertMoussy 3 343 Mar-17-2024, 09:37 AM
Last Post: deanhystad
  [PyQt] command require close window Krissstian 14 2,826 Nov-19-2022, 04:18 PM
Last Post: Krissstian
  [Tkinter] Mouse click event not working on multiple tkinter window evrydaywannabe 2 3,711 Dec-16-2019, 04:47 AM
Last Post: woooee
  tkinter window and turtle window error 1885 3 6,624 Nov-02-2019, 12:18 PM
Last Post: 1885
  Using tkinter on Windows to launch python 3 scripts Mocap 1 2,685 Jul-17-2019, 05:16 AM
Last Post: Yoriz
  update a variable in parent window after closing its toplevel window gray 5 8,976 Mar-20-2017, 10:35 PM
Last Post: Larz60+
  pygtk2, how to disconnect all callback of widget or window event ? harun2525 1 3,262 Feb-19-2017, 11:44 PM
Last Post: Larz60+
  [Tkinter] I have a Toplevel button in tkinker that I want to close the window and then perform Bloodypizza17 2 7,720 Jan-06-2017, 07:18 PM
Last Post: Bloodypizza17
  launch .PY program Help iw2fo 22 19,849 Oct-18-2016, 09:13 PM
Last Post: Barrowman

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020