0

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

  • 1
    I don't know the internals of `process_iter`, but deleting an item from a list processed by an iterator, as `proc.kill` presumably does, may have strange effects and you may have missed an instance. Perhaps you should collect all Python.exe processes into another list, and in a separate loop afterwards, do the kills. – RufusVS Oct 25 '20 at 18:52
  • My python processes were "python", not "Python". you should probably change your process lookup line to: `if proc.name().lower() == procname.lower():` to remove case sensitivity. – RufusVS Oct 25 '20 at 19:07
  • 1
    Added a more concise version. One more comment: `from xxx import *` is usually a bad idea. You may have collisions with variables in different modules. And hard to find where a function came from when debugging. – RufusVS Oct 25 '20 at 19:23

1 Answers1

1

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()
RufusVS
  • 4,008
  • 3
  • 29
  • 40