'Python: How to throw an exception as soon as the user presses a certain specific key (other than ctrl + c)
I'm using selenium on Python and, I wish I could interrupt the program, catch the exception and do something with it, as soon as the user presses a certain specific key (other than ctrl + c ,because I've already cought this exception). Do you think it is possible ?
As an exemple: I would like to convert a text to speech with selenium by using this website: "https://www.naturalreaders.com/online" (I already did it) and then, when the user presses (for exemple) the space key, I would like to pause the speech (and resume the speech if the user presses this key again)
here is my code:
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.ui import WebDriverWait
from bs4 import BeautifulSoup
from ebooklib import epub
from colorama import Fore, Style
import time, logging, ebooklib, os
if "__main__" == __name__:
def load_site(url, silent= False):
#those four varables are inside this function
global driver, r_click, wait, ignored_exceptions
#close all Chrome windows before starting
os.system("taskkill /f /im chrome.exe 2> nul")
#this is if I would like to connect to the google account
chr_options = webdriver.ChromeOptions()
if silent:
#to run a site in the background
chr_options.add_argument("headless")
driver = webdriver.Chrome(executable_path= <were it is located on your device>, options= chr_options)
driver.delete_all_cookies()
driver.get(url)
#this is a called function which wait until an element is clickable on the driver...
wait = WebDriverWait(driver, 20,ignored_exceptions=ignored_exceptions)
driver.maximize_window();time.sleep(1)
def connect_voice(driver):
#here I choose the voice on the site inside the driver
global wait
speakers = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[3]"))).click()
free = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/div[2]/div[2]/div/div/div/app-voice-list/div/mat-tab-group/mat-tab-header/div[2]/div/div/div[1]"))).click(); time.sleep(0.5)
Paul = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/div[2]/div[2]/div/div/div/app-voice-list/div/mat-tab-group/div/mat-tab-body[1]/div/app-voice/div/mat-selection-list/mat-list-option[6]/div/div[2]/div/div[2]/div/div"))).click()
def say(text, WPM = 180,to_print= False):
global driver
nb_words = len(text.split())
if nb_words == 0: return None
#I replace this character for a good prononciation
text = list(text.replace("—", "-"))
#here I delete all the useless numbers of the text
for i in range(len(text)-1):
num = 1
while i + num < len(text) and text[i].isalpha() and text[i+num].isnumeric():
text[i+num] = ""
num += 1
text = "".join(text)
#here I calculate the good speed for printting a letter
LPM = (len(text) * WPM) / nb_words
SPL = (1 / LPM) * 60 - 0.015
#I don't forget to delete the previous text
clear = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-home/div/app-input/div/div/div[1]/div[2]/button[5]"))); ActionChains(driver).move_to_element(clear).click(clear).perform()
#and I paste the actual text on the site
paste = wait.until(expected_conditions.presence_of_element_located((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-home/div/app-input/div/div/div[2]"))).send_keys(text)
#here I play the text, and he read it
play = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[1]"))).click()
time.sleep(0.6)
for i, l in enumerate(text):
#Here I color all the upper characters in upper words
if l.isupper() and (i == len(text)-1 or text[i+1].isupper() or text[i+1] in [" ", ".", ";",",",":","!","(",")","-"]):
l = f"{c2}{l}{reset}"
print(l, end= ""); time.sleep(SPL)
print()
logging.captureWarnings(True)
#colors for the print function
c1 = Style.BRIGHT + Fore.YELLOW
c2 = Style.BRIGHT + Fore.CYAN
reset_color = Style.RESET_ALL
ignored_exceptions =(NoSuchElementException,StaleElementReferenceException,)
#the site is a text to speech converter
load_site("https://www.naturalreaders.com/online/", silent= True)
print("starting reading...")
connect_voice(driver)
#I read a book which is downloaded on my device
book = epub.read_epub(<path of the epub book>)
#get the item objects corresponding to all chapters
book = list(book.get_items_of_type(ebooklib.ITEM_DOCUMENT))
#for each item
for chap in book[1:]:
try:
#I get only the text of the html code
soup = BeautifulSoup(chap.get_content(), "lxml")
if soup is not None:
texte = soup.text.splitlines()
for line in texte:
#and I read it line by line
say(line + ' ', to_print= True)
except KeyboardInterrupt:
#If the user click on ctrl + c, the program read the next chapter
#(but before, I just pause the actual reading)
play = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[2]"))).click()
print('\n\n')
I would like to add a try/except inside the say function, whose purpose is to stop temporarily the reading when the user click on a specific key (maybe F7, or a key not too accessible) or stop the reading by holding an accesible key(a space). Does anyone is in a situation similar to mine or knows how to raise an exception by clicking or holding a key (other than ctrl + c because already used)?
Solution 1:[1]
I just found how to do it:
import keyboard
for i, l in enumerate(sent):
if keyboard.is_pressed('p'):
pause = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[2]"))).click(); time.sleep(0.5)
while not(keyboard.is_pressed('p')):
time.sleep(0.05)
play = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[1]"))); ActionChains(driver).move_to_element(pause).click(pause).perform()
id_sent -= 1; break
if l.isupper() and (i == len(sent)-1 or sent[i+1].isupper() or sent[i+1] in [" ", ".", ";",",",":","!","(",")","-"]):
l = f"{c2}{l}{reset}"
print(l, end= ""); time.sleep(SPL)
like you have seen, I added this part of code inside the for loop:
if keyboard.is_pressed('p') #do someting
pause = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[2]"))).click()
time.sleep(0.5) #it is important, because if you don't set a sleep, the while loup will be True directly
while not(keyboard.is_pressed('p')): #we wait for the user to press this key again, to do an other thing
time.sleep(0.05)
play = wait.until(expected_conditions.element_to_be_clickable((By.XPATH,"/html/body/app-root/div/app-main/mat-sidenav-container/mat-sidenav-content/app-header/mat-toolbar/div[2]/div[2]/app-reader/div/button[1]"))); ActionChains(driver).move_to_element(pause).click(pause).perform()
the condition: if keyboard.is_pressed('p') ; doesn't wait for you to press the 'p' key, so the for loop continue (that's why you need to put this condition inside a for loop, because if you don't, the condition could be true, but already executed and already returned False)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
