Copy Text from Previously selected External application using button in python gui - python

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.

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()

Related

Python - Why win32clipboard is unable to paste to Google unless pasting to GUI first

Part of a project I'm working on requires I copy a generated "password" variable to the clipboard.
I'm using PySimpleGUI and when the password is generated, I click a "Copy" button and it should save the password to the clipboard, however, trying to paste this to an external program (Google Chrome for example), nothing happens, but if I press Ctrl+V in a box on the PySimpleGUI interface, the password appears and I can then paste it to an external program (Google Chrome).
if event == "Copy" and password is not None:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(password)
Is there a way I can make it so the password can be pasted straight from clipboard to google without having to paste it inside the program first?
One additional line of code needed for this to function properly.
win32clipboard.CloseClipboard()
I'm not sure why it would allow me to paste in google if I had first pasted it inside the GUI, so if anyone knows the answer then please say, but the line of code above will solve similar issues.
There's no code provided by you for the GUI.
Following code work well.
import win32clipboard
import PySimpleGUI as sg
layout = [
[sg.Input(key='INPUT')],
[sg.Button('COPY')],
]
window = sg.Window('Title', layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'COPY':
text = values['INPUT']
if text:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text)
win32clipboard.CloseClipboard()
window.close()

Tkinter Canvas text substitution intermittent

I have a method that is called from a button and it allows the user to select a file and pump out an analysis on the other side. There is a canvas outside the class and method that has a text widget that tells the user what is happening, with an original text='Ready'. This is the method that gets it done:
def process_solution(self):
canvas.itemconfig(infobox_text, text='Please Select a File.')
self.import_file_path = filedialog.askopenfilename()
canvas.itemconfig(infobox_text, text='Analysing Solution. Please Wait.')
if self.import_file_path:
# UnGzip
with gzip.open(self.import_file_path, 'rt') as decompressed_buffer:
decompressed_data = decompressed_buffer.read()
# Parse to XML
parsed_data = ElementTree.fromstring(decompressed_data)
# Now extract metrics
self.analysis_results = ExtractMetrics.start(parsed_data)
# Display Parsed Results in Window
canvas.itemconfig(infobox_text, text=self.analysis_results)
else:
canvas.itemconfig(infobox_text, text='Upload Cancelled.')
It changes from the original 'Ready' message to 'Please Select a File' with no problem, then it seems to skip 'Analysing Solution. Please Wait.' (even when I put the thread to sleep for a few seconds). And finally it displays self.analysis_results. Even if the file selection is cancelled it displays the right message, but not when a file is selected.
My question is why, if in all cases I'm using canvas.itemconfig(infobox_text, text=...) the first, second and fourth changes to the text work, but it skips the third. No errors anywhere either.

Psychopy in python : having two answers once when the key is pressed and the second when is released

I need to modify a script in psychopy. The thing I need is the subject to press and hold a buttom in the keyboard, but I'd like to have two responses: one when he/her presses it (and the windows with text has to change), and the second when he/her releases it (and the windows with text has to change again).
I am using event.getKeys function of psychopy, and then I'm using keyboard library.
after inizializing the window screen and the text and importing psychopy and the functions I need and keyboard library as key.
intro.draw()
win.flip() # reset the clock to record a response time and clear events
event.clearEvents('all')
event.globalKeys.clear() #to avoid problems with the keys you pressed before
intro_loop = True
while intro_loop:
intro_keys = kb.getKeys(["space"], waitRelease=True)
if key.is_pressed('space'):
intro.text = 'Hi, please follow these instructions :) '
intro.draw()
win.flip() # reset the clock to record a response time and clear events
if len(intro_keys):
win.close()
intro_loop = False
I did the same in another script (just to try) but instead of having a window text screen I printed two different things, and it worked. in this script, instead, it completely ignores the function "key.is_pressed".
do you have any ideas?

Python guizero: textBox = [textBox]

OK, I been using guizero for a couple of days but I found a problem that has been perplexing me. Here a simplified version of the code.
from guizero import App, Text, PushButton, Picture, Drawing, TextBox
app = App("Testing")
def test():
global testing
print(testing)
button = PushButton(app,test, text = "press to test")
button.bg = "red"
button.text_size= 35
tittle = Text(app, "Testing input here")
testing = TextBox(app, text= "" )
app.display()
No matter what the users input into the Textbox, it is always print:
[TextBox] object with text ""
I tried putting testing into an argument in the function test, it comes up with the same thing.
[TextBox] object with text ""
If I don't make an argument in the function or global testing, it makes the same thing, and if I make the textbox before the button, I have the same problem.
Can anyone find a way to work around the problem or to fix this, I'm new to guizero so I have little to an idea what I'm doing
If you want to print the contents of the Text widget then you need to do
def test():
global testing
print(testing.value)
This will get the value of the testing widget rather than the "repr" of the widget.
Seems like a bit of "bug" in guizero that the description text that is output doesn't update when the value of the widget is updated.
Issue has been accepted by the developer and a fix has been pushed to a development branch. https://github.com/lawsie/guizero/issues/392

Psychopy builder expt. - how to add live updating text on screen

I am creating an experiment using Psychopy builder.
The participant is presented with an image containing numbers, e.g. 10 and 20.
They enter what they think is the mean of the numbers, 15 in this case, and then press the spacebar to move on to the next image.
I am trying to have it so there is a display/box on screen that shows them their entry, as with larger numbers in the hundreds of thousands and millions I think they might lose track of what they have pressed.
The ability to change their entry would be excellent also, but really I am most interested in them being able to see it on screen.
In builder I can't find a way to do this, and the ratings scale is not appropriate for huge numbers.
I found these solutions in code to do something that sounds like it:
http://www.psychopy.org/wiki/home.php/Snippets/LiveUpdatingText
However when I try to add them using the code insert function , or just adding them to the compiled script the screen locks up when I try to run the experiment. I am a novice at python, and am not sure where to start fixing this. Is what I'm trying to do possible?
I'm happy to provide some example code from the compiled builder experiment.
Thanks in advance!
Those code snippets are designed for Coder, where you control everything that is happening and when. The same thing can be done in Builder, but you will have to amend the code to fit in with Builder's event loop cycle. i.e. Builder does certain things at the start of an experiment, on every trial, on every screen refresh and so on. So you can't just insert this sort of code without modification, because, for example, it attempts to wait indefinitely for a keypress. Builder meanwhile, is checking the keyboard every screen refresh (typically at 60 Hz), so if you try to wait indefinitely for a keypress in code, you'll be halting Builder from doing everything else it needs to do.
In essence, you just need to break up the code into snippets that go in the appropriate tab in a Builder Code Component (for code to be executed at experiment start, on each frame, and so on), and avoid indefinite functions like event.waitKeys() in favour of instantaneous checking via event.getKeys()
e.g. to adapt the second example from Jonas Lindeløv, in the "Begin Routine" tab, put:
chars = list('0123456789.') # the valid characters
meanText = '' # start with an empty answer on each trial
In the "Each Frame" tab, put something like:
response = event.getKeys() # get a list of keys pressed at this instant
if len(response) > 0: # if there was one,
key = response[0] # just convenient shorthand
if key in chars:
meanText = meanText + response[0]
elif key == 'space':
meanText = meanText + ' '
elif key == 'backspace' and len(meanText) > 0:
meanText = meanText[:-1]
elif key == 'return':
thisExp.addData('Answer', meanText) # save the response
continueRoutine = False # finish this trial
# update the appropriate text stimulus with the current response value:
insertNameOfYourTextStimulusComponent.text = meanText

Categories

Resources