def nextLine(self): if not self.reader: # We were stopped. return if not self.reader.obj: # The object died, so we should too. self.finish() return bookmark = self.reader.bookmark # Expand to the current line. # We use move end rather than expand # because the user might start in the middle of a line # and we don't want to read from the start of the line in that case. # For lines after the first, it's also more efficient because # we're already at the start of the line, so there's no need to search backwards. delta = self.reader.move(textInfos.UNIT_READINGCHUNK, 1, endPoint="end") if delta <= 0: # No more text. if isinstance(self.reader.obj, textInfos.DocumentWithPageTurns): # Once the last line finishes reading, try turning the page. cb = speech.CallbackCommand(self.turnPage) speech.speakWithoutPauses([cb, speech.EndUtteranceCommand()]) else: self.finish() return # Call lineReached when we start speaking this line. # lineReached will move the cursor and trigger reading of the next line. cb = speech.CallbackCommand( lambda obj=self.reader.obj, state=self.speakTextInfoState.copy( ): self.lineReached(obj, bookmark, state)) spoke = speech.speakTextInfo(self.reader, unit=textInfos.UNIT_READINGCHUNK, reason=controlTypes.REASON_SAYALL, _prefixSpeechCommand=cb, useCache=self.speakTextInfoState) # Collapse to the end of this line, ready to read the next. try: self.reader.collapse(end=True) except RuntimeError: # This occurs in Microsoft Word when the range covers the end of the document. # without this exception to indicate that further collapsing is not possible, say all could enter an infinite loop. self.finish() return if not spoke: # This line didn't include a natural pause, so nothing was spoken. self.numBufferedLines += 1 if self.numBufferedLines < self.MAX_BUFFERED_LINES: # Move on to the next line. # We queue this to allow the user a chance to stop say all. queueHandler.queueFunction(queueHandler.eventQueue, self.nextLine) else: # We don't want to buffer too much. # Force speech. lineReached will resume things when speech catches up. speech.speakWithoutPauses(None) # The first buffered line has now started speaking. self.numBufferedLines -= 1
def finish(self): # There is no more text. # Call stop to clean up, but only after speech completely finishes. # Otherwise, if a different synth is being used for say all, # we might switch synths too early and truncate the final speech. # We do this by putting a CallbackCommand at the start of a new utterance. cb = speech.CallbackCommand(self.stop, name="say-all:stop") speech.speakWithoutPauses( [speech.EndUtteranceCommand(), cb, speech.EndUtteranceCommand()])
def next(self): if not self.walker: # We were stopped. return if self.prevObj: # We just started speaking this object, so move the navigator to it. api.setNavigatorObject(self.prevObj, isFocus=lastSayAllMode==CURSOR_CARET) # Move onto the next object. self.prevObj = obj = next(self.walker, None) if not obj: return # Call this method again when we start speaking this object. callbackCommand = speech.CallbackCommand(self.next, name="say-all:next") speech.speakObject(obj, reason=controlTypes.REASON_SAYALL, _prefixSpeechCommand=callbackCommand)
def nextLine(self): if not self.reader: log.debug("no self.reader") # We were stopped. return if not self.reader.obj: log.debug("no self.reader.obj") # The object died, so we should too. self.finish() return bookmark = self.reader.bookmark # Expand to the current line. # We use move end rather than expand # because the user might start in the middle of a line # and we don't want to read from the start of the line in that case. # For lines after the first, it's also more efficient because # we're already at the start of the line, so there's no need to search backwards. delta = self.reader.move(textInfos.UNIT_READINGCHUNK, 1, endPoint="end") if delta <= 0: # No more text. if isinstance(self.reader.obj, textInfos.DocumentWithPageTurns): # Once the last line finishes reading, try turning the page. cb = speech.CallbackCommand(self.turnPage, name="say-all:turnPage") speech.speakWithoutPauses([cb, speech.EndUtteranceCommand()]) else: self.finish() return # Copy the speakTextInfoState so that speak callbackCommand # and its associated callback are using a copy isolated to this specific line. state = self.speakTextInfoState.copy() # Call lineReached when we start speaking this line. # lineReached will move the cursor and trigger reading of the next line. def _onLineReached(obj=self.reader.obj, state=state): self.lineReached(obj, bookmark, state) cb = speech.CallbackCommand(_onLineReached, name="say-all:lineReached") # Generate the speech sequence for the reader textInfo # and insert the lineReached callback at the very beginning of the sequence. # _linePrefix on speakTextInfo cannot be used here # As it would be inserted in the sequence after all initial control starts which is too late. speechGen = speech.getTextInfoSpeech(self.reader, unit=textInfos.UNIT_READINGCHUNK, reason=controlTypes.REASON_SAYALL, useCache=state) seq = list(speech._flattenNestedSequences(speechGen)) seq.insert(0, cb) # Speak the speech sequence. spoke = speech.speakWithoutPauses(seq) # Update the textInfo state ready for when speaking the next line. self.speakTextInfoState = state.copy() # Collapse to the end of this line, ready to read the next. try: self.reader.collapse(end=True) except RuntimeError: # This occurs in Microsoft Word when the range covers the end of the document. # without this exception to indicate that further collapsing is not possible, say all could enter an infinite loop. self.finish() return if not spoke: # This line didn't include a natural pause, so nothing was spoken. self.numBufferedLines += 1 if self.numBufferedLines < self.MAX_BUFFERED_LINES: # Move on to the next line. # We queue this to allow the user a chance to stop say all. queueHandler.queueFunction(queueHandler.eventQueue, self.nextLine) else: # We don't want to buffer too much. # Force speech. lineReached will resume things when speech catches up. speech.speakWithoutPauses(None) # The first buffered line has now started speaking. self.numBufferedLines -= 1