Search code examples
pythonmultithreadingpsutilpyttsx3

Multithreading should close the whole process but doesn't


I am trying to do a simple executable that import a PDF and with pyttsx3 reads the PDF. As pyttsx3 can't pause the reading I created a small function with psutil that kills the process named Python.exe (May not be the ideal way). a simple interface with Easygui allows to choose the PDF file, the page where to start from, and close the process when a button is pressed (with psutil).

As I understood multi-threading, there should be a main process with multiple thread executing; therefore I thought that closing the whole process I would stop the script whenever the user press the button. It doesn't work. The thread is closed but the main process not, therefore the script keeps running (the voice keep reading). After I have a working version I plan to use pyinstaller to convert it into an exe file.

The code is:

from pyttsx3 import *
from pyttsx3.drivers import sapi5
from PyPDF2 import PdfFileReader
from threading import *
from psutil import process_iter
from easygui import enterbox,fileopenbox,msgbox,choicebox

class Audiobook():

    def readbook(self,title,page=0):
        book = open(title, 'rb')
        pdfReader = PdfFileReader(book)
        pages = pdfReader.numPages
        speaker = init()

        speaker.setProperty('rate',150)
        
        for num in range(page-1,pages):
            page = pdfReader.getPage(num)
            text = page.extractText()
            speaker.say(text)
            speaker.runAndWait()
            speaker.say("You arrived to page" + str(num+1))

    def title(self):
        title = fileopenbox()
        if title:
            page = int(enterbox("From which page would you like to read? ",'Choose the page'))
        else:
            return
        self.readbook(title,page)

    def killprocess(self):
        kill = msgbox("Click here to stop reading","Close the Application?")
        if kill is not None:
            procname = 'Python.exe' #this line will be changed when converted into an executable with pyinstaller
            for proc in process_iter():
                if proc.name() == procname: 
                    proc.kill()

processcontrol = Audiobook()
bookreader = Audiobook()
process1 = Thread(target=bookreader.title)
process2 = Thread(target=processcontrol.killprocess)
process2.start()
process1.start()

Any idea what am I doing wrong? I spoofed Google and this website but without success.

Thanks


Solution

  • I've changed the kill process and only create one object (two instances weren't needed).

    import pyttsx3
    from pyttsx3.drivers import sapi5
    from PyPDF2 import PdfFileReader
    from threading import Thread
    from psutil import process_iter
    from easygui import enterbox,fileopenbox,msgbox,choicebox
    import os
    import signal
    
    class Audiobook():
    
        def readbook(self,title,page=0):
            book = open(title, 'rb')
            pdfReader = PdfFileReader(book)
            pages = pdfReader.numPages
            speaker = pyttsx3.init()
    
            speaker.setProperty('rate',150)
    
            for num in range(page-1,pages):
                page = pdfReader.getPage(num)
                text = page.extractText()
                speaker.say(text)
                speaker.runAndWait()
                speaker.say("You arrived to page" + str(num+1))
    
        def title(self):
            title = fileopenbox(default="*.pdf",filetypes=["*.pdf"])
            if title:
                page = enterbox("From which page would you like to read? ",'Choose the page')
                page = 1 if page=="" else int(page)
            else:
                return
            self.readbook(title,page)
    
        def killprocess(self):
            kill = msgbox("Click here to stop reading","Close the Application?")
            if kill is not None:
                os.kill(os.getpid(),signal.SIGTERM)
    
    bookreader = Audiobook()
    process1 = Thread(target=bookreader.title)
    process2 = Thread(target=bookreader.killprocess)
    process2.start()
    process1.start()