3

I'm trying to build a lightweight Text-to-Speech GUI where I can select text in, say, Word or Chrome, and then press a button in my GUI and have it read it.

I've already figured out all the pieces to create the GUI and get the TTS to work, but I can't get the form factor right. I'm trying to mimic the form factor of Dragon Naturally Speaking's text-to-speech because, well it's simple and what I'm used to.

Here are the missing steps in the user story I can't get to work, in order:

1) user highlights text in an application (word, chrome, notepad, whatever) with the mouse and presses the gui button

2) data from the external application is pulled in as UTF-8 and stored in a variable called "text"

I know there's a problem in that several windows can have selected text. My solution is to pull the selected text from the most recently previously selected window.

Right now the kludgy work around is to Ctrl-C whatever text I want read and then press the button, because I can pull the data from the clipboard, but this is a really terrible user experience and confusing, as well. I tried using pyperclip to get the button to put the text in the clipboard, but it doesn't seem to work, so I'm not sure if the clipboard idea is a dead end.

def select_text(self):
    #copy
    pyperclip.copy() # doesn't work        

    #get text
    win32clipboard.OpenClipboard()
    text = win32clipboard.GetClipboardData()
    win32clipboard.CloseClipboard()

    #say it!
    self.say_text(text)

I can't seem to find anything like this anywhere, and I have no idea where to start. Any help would be appreciated.

Nicodemus
  • 31
  • 1
  • tried looking for in these libraries suggested https://stackoverflow.com/questions/165495/detecting-mouse-clicks-in-windows-using-python ? – Ishan Srivastava Jul 18 '18 at 22:14
  • I would recommend instead listening for some hotkey combo like `ctrl+T` (using something like `pynput`) when you detect the hotkey get the current top level window ... save current clipboard to tmp var, copy whatever might be highlighted into the clipboard (maybe by sending `ctrl+c`), read in whatever is now in the clip board and "speak" it, restore clipboard back to its original value – Joran Beasley Jul 18 '18 at 22:27
  • I like the idea of the 'ctrl+t' short cut, but... I still have no idea how to get the mouse-selected text into the clipboard. This is the key step I can't seem to figure out. – Nicodemus Jul 19 '18 at 14:15
  • Let me correct my statement: I have no idea how to get the text into the clipboard pythonically. Ctr+C works, but then you have to hit the button or the Ctr+T shortcut too. It would be better as a one-click experience. I'm not sure why this is so hard to accomplish in Python. – Nicodemus Jul 19 '18 at 18:18
  • I think I figured it out – Nicodemus Jul 19 '18 at 20:28

1 Answers1

0

I think I figured it out. It's pretty ugly, but it works; I'm a beginner at coding, and this is just a hobby for me. Honestly, I'm just happy to have it working.

I've bound the command to Alt_L+Q; I'll probably add the Alt_R+Q later, but I'll never use them. For some reason using Alt alone without the L or R doesn't work.

Also, Without the first sleep statement, it doesn't work at all. The second sleep statement is precautionary.

# set clipboard data
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText('No text is currently selected.')
win32clipboard.CloseClipboard()

text = None #this is what will be read
chg_rate = 0 #reading speed +/- 0.0 to 1.0
chg_vol = 0 #volume +/- 0.0 to 1.0

def get_text():
    #current_window = win32gui.GetForegroundWindow()
    import pythoncom
    pythoncom.CoInitialize()
    cw = win32com.client.Dispatch('WScript.Shell')
    time.sleep(.7)
    cw.SendKeys('^c') #copies text
    time.sleep(.2)

    #get text
    win32clipboard.OpenClipboard()
    text = win32clipboard.GetClipboardData()
    win32clipboard.CloseClipboard()

    return text


#define the hot key
def create_hotkey():
    # The key combination to check
    COMBINATIONS = [
        {keyboard.Key.alt_l, keyboard.KeyCode(char='Q')},
        {keyboard.Key.alt_l, keyboard.KeyCode(char='q')}
    ]

    # The currently active modifiers
    current = set()

    def execute():
            text = get_text()
            if not isinstance(text, str):
                text = "Selected input is not UTF-8 text."
            say_text(text)

    def on_press(key):
        if any([key in COMBO for COMBO in COMBINATIONS]):
            current.add(key)
            if any(all(k in current for k in COMBO) for COMBO in COMBINATIONS):
                execute()

    def on_release(key):
        if any([key in COMBO for COMBO in COMBINATIONS]):
            current.remove(key)

    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
        listener.join()

#run hotkey
create_hotkey()
Nicodemus
  • 31
  • 1