spyoungtech / ahk

Python wrapper for AutoHotkey with full type support. Harness the automation power of AutoHotkey with the beauty of Python.
MIT License
858 stars 65 forks source link

Return variable from ahk script into python #337

Closed Apprisco closed 1 month ago

Apprisco commented 1 month ago

Checked the documentation

describe your feature request

I have a script that ends up running and returning the following variable:

    username :=  ReadMemory_Str(U_A, ,pid)

I want to be able to run this script in python and get the username back out as a variable I can use in python. Is this possible?

spyoungtech commented 1 month ago

There's a couple ways you can do this. The most robust approach would be to write an extension.

Another quick and dirty way may be to simply write the result out to stdout (e.g. with FileAppend or File.Write), though this requires the data be a UTF-8 string, so you may need to encode if the underlying data is not represented in this way.

I haven't tested this, but I believe this example works and should work with both versions of AutoHotkey:

# https://github.com/spyoungtech/ahk/blob/46bce5c3e19aaa9fe49f1a90816a80a96a39a2c2/ahk/_utils.py#L137-L146
script = '''\
#NoTrayIcon
version := Format("{}", A_AhkVersion)
filename := "*"
encoding := "UTF-8"
mode := "w"
stdout := FileOpen(filename, mode, encoding)
stdout.Write(version)
stdout.Read(0)
'''
result = ahk.run_script(script)
print(result)

In the above example, the variable version contains the data being returned to Python. It is written to stdout with UTF-8 encoding. This is ultimately the return value of the run_script call.

Apprisco commented 1 month ago
    stdout := FileOpen("*","w", "UTF-8")
    stdout.Write(R_P)
    stdout.Read(0)

Unfortunately, that is not working.

Traceback (most recent call last):
  File "C:\Users\Andrew\Desktop\restartTrove\main.py", line 22, in startAllFunctions
    await art.startAllAccounts()
  File "C:\Users\Andrew\Desktop\restartTrove\main.py", line 90, in startAllAccounts
    await self.glyph.loginAccount(account)
  File "C:\Users\Andrew\Desktop\restartTrove\glyph.py", line 87, in loginAccount
    trove_window=Trove(trove_hwnd,account.username)
  File "C:\Users\Andrew\Desktop\restartTrove\trove.py", line 123, in __init__
    self.r_p=Trove.ahk.run_script(rpscript)
  File "C:\Users\Andrew\Anaconda3\envs\Trove\lib\site-packages\ahk\_sync\engine.py", line 1128, in run_script
    return self._transport.run_script(script_text_or_path, blocking=blocking, timeout=timeout)
  File "C:\Users\Andrew\Anaconda3\envs\Trove\lib\site-packages\ahk\_sync\transport.py", line 779, in run_script
    raise subprocess.CalledProcessError(proc.returncode, proc.runargs, stdout, stderr)
subprocess.CalledProcessError: Command '['C:\\Users\\Andrew\\Anaconda3\\envs\\Trove\\Scripts\\AutoHotkey.exe', '/CP65001', '/ErrorStdOut', '*']' returned non-zero exit status 2.
Apprisco commented 1 month ago

Update: Got it slightly working. How can i do the same when i'm using blocking=False? Not using asyncahk yet, should I be?

Apprisco commented 1 month ago

<ahk._async.transport.AsyncFutureResult object at 0x000001D13BF6B130> how do I get the value out of this class lol.

 username=await asyncahk.run_script(script,blocking=False)
print(username.result())
await asyncio.sleep(self.delay)

This is not working either.

spyoungtech commented 1 month ago

Not using asyncahk yet, should I be?

It's up to you. If your program is using asyncio, it's beneficial to avoid blocking the event loop -- while waiting on the AutoHotkey process to return a response, other tasks in the event loop can run asynchronously. Though most function calls return very quickly, some can take time (such as those that wait, like win_wait). Using AsyncAHK would probably be less cumbersome (and more performant) than using blocking=False to avoid the same problem if that's the intent.

To answer in a word: yes, probably.

<ahk._async.transport.AsyncFutureResult object at 0x000001D13BF6B130>
how do I get the value out of this class

When using the async API, you should await the result method.

future_username = await asyncahk.run_script(script, blocking=False)
username = await future_username.result()
print(username)

Though, if you are immediately getting the result from the future object, there's probably no point to using blocking=False in the first place. Just get the result directly:

username = await asyncahk.run_script(script)
print(username)
Apprisco commented 1 month ago

Works, I need concurrent requests so I have no choice. Thanks!