1

I have a python script called "server.py" and inside it I have a function def calcFunction(arg1): ... return output How can I call the function calcFunction with arguments and use the return value in autohotkey? This is what I want to do in autohotkey:

ToSend = someString ; a string
output = Run server.py, calcFunction(ToSend) ; get the returned value from the function with ToSend as argument
Send, output ; use the returned value in autohotkey

I have looked online but nothing seems to fully answer my question. Can it even be done?

Secozzi
  • 252
  • 3
  • 10
  • 1
    I think you may need to make your Python script **`print()`** it's output. – martineau Oct 29 '19 at 00:23
  • I have solved similar problems by writing the results to a TXT file if that interests you. – ViktorMS Oct 30 '19 at 16:26
  • Can you just use StdOut in the python function? Then run the python script in a wscript shell returning the StdOut value to ahk . . . Easy, peasy. & no need to write to files or clipboard, either. – PGilm Oct 30 '19 at 20:09

2 Answers2

2

In order to send your parameters to Python, you could use arguments from within your Python script. You can do this with the sys library:

import sys
print(sys.argv[0]) # name of file
print(sys.argv[1]) # first argument
print(sys.argv[2]) # second argument...

From within your AutoHotKey script, you can send parameters to the Python script by adding them as arguments right after specifying the file name:

RunWait, server.py "This will be printed as the first argument!" "This is the second!"

Then, to get the output of the function back to AHK, you could use sys again by utilizing it's exit() function:

sys.exit(EXIT_NUMBER)

And back in AHK, you recieve the EXIT_NUMBER inside the variable ErrorLevel. Put all together, your code should look something like this:

; AHK
RunWait, server.py "%ToSend%"

# Python
sys.exit(calcFunction(sys.argv[1]))

; AHK
MsgBox %ErrorLevel%
Xiddoc
  • 3,369
  • 3
  • 11
  • 37
  • Getting the errorcode (python's EXIT_NUMBER) is not quite the same as getting the result of the server.py function operating on "ToSend." – PGilm Oct 31 '19 at 16:16
  • That is true, an alternative would be to have python dump the return value into a file, and when the program exits, AHK can read that file. In my answer, I was assuming that the return statement is an integer, which I guess makes it partially invalid. – Xiddoc Nov 01 '19 at 10:19
2

using python COM server, ahk can really calls python functions. directly.
you use it like this: MsgBox % pythonComServer.toUppercase("hello world")

simple example: return uppercased string
use the python part from How to program hotstrings in python like in autohotkey and use this for ahk part:

call python function uppercase.ahk

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, force
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines, -1
#KeyHistory 0
ListLines Off

pythonComServer:=ComObjCreate("Python.stringUppercaser")
;or
; pythonComServer:=ComObjCreate("{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}") ;use your own CLSID

MsgBox % pythonComServer.toUppercase("hello world")
Exitapp

f3::Exitapp

customized version: (math) use SymPy to simplify Expression
read this first to understand: How to program hotstrings in python like in autohotkey

sympy com server.py

from sympy import simplify, Number, N
from sympy.parsing.sympy_parser import standard_transformations, implicit_multiplication_application, convert_xor
from sympy.parsing.sympy_parser import parse_expr

from decimal import Decimal

from winsound import MessageBeep

transformations = standard_transformations + (implicit_multiplication_application, convert_xor)

def removeTrailingZerosFromNum(num):
    dec = Decimal(str(num))

    tup = dec.as_tuple()
    delta = len(tup.digits) + tup.exponent
    digits = ''.join(str(d) for d in tup.digits)
    if delta <= 0:
        zeros = abs(tup.exponent) - len(tup.digits)
        val = '0.' + ('0' * zeros) + digits
    else:
        val = digits[:delta] + ('0' * tup.exponent) + '.' + digits[delta:]
    val = val.rstrip('0')
    if val[-1] == '.':
        val = val[:-1]
    if tup.sign:
        return '-' + val
    return val


def removeTrailingZerosFromExpr(operatorObject):
    if operatorObject.args:
        return type(operatorObject)(*[removeTrailingZerosFromExpr(i) for i in operatorObject.args])
    else:
        try:
            return Number(removeTrailingZerosFromNum(operatorObject))
        except:
            return operatorObject

def removeTrailingZerosFromExprOrNumber(operatorObject):
    try:
        return removeTrailingZerosFromNum(operatorObject)
    except:
        return removeTrailingZerosFromExpr(operatorObject)

class BasicServer:
    # list of all method names exposed to COM
    _public_methods_ = ["parExprN"]

    @staticmethod
    def parExprN(clipBak):
        parsed = parse_expr(clipBak, transformations=transformations)
        simplified = simplify(N(parsed))
        finalStr = str(removeTrailingZerosFromExprOrNumber(simplified))
        MessageBeep(-1)
        return finalStr.replace("**", "^")

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("Error: need to supply arg (""--register"" or ""--unregister"")")
        sys.exit(1)
    else:
        import win32com.server.register
        import win32com.server.exception

        # this server's CLSID
        # NEVER copy the following ID 
        # Use "print(pythoncom.CreateGuid())" to make a new one.
        myClsid="{4530C817-6C66-46C8-8FB0-E606970A8DF6}"
        # this server's (user-friendly) program ID, can be anything you want
        myProgID="Python.SimplifyExpr"
        
        import ctypes
        def make_sure_is_admin():
            try:
                if ctypes.windll.shell32.IsUserAnAdmin():
                    return
            except:
                pass
            exit("YOU MUST RUN THIS AS ADMIN")

        if sys.argv[1] == "--register":
            make_sure_is_admin()

            import pythoncom
            import os.path
            realPath = os.path.realpath(__file__)
            dirName = os.path.dirname(realPath)
            nameOfThisFile = os.path.basename(realPath)
            nameNoExt = os.path.splitext(nameOfThisFile)[0]
            # stuff will be written here
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\${myClsid}
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{c2467d33-71c5-4057-977c-e847c2286882}
            # and here
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${myProgID}
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Python.SimplifyExpr
            win32com.server.register.RegisterServer(
                clsid=myClsid,
                # I guess this is {fileNameNoExt}.{className}
                pythonInstString=nameNoExt + ".BasicServer", #sympy com server.BasicServer
                progID=myProgID,
                # optional description
                desc="(math) SymPy simplify Expression",
                #we only want the registry key LocalServer32
                #we DO NOT WANT InProcServer32: pythoncom39.dll, NO NO NO
                clsctx=pythoncom.CLSCTX_LOCAL_SERVER,
                #this is needed if this file isn't in PYTHONPATH: it tells regedit which directory this file is located
                #this will write HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{4530C817-6C66-46C8-8FB0-E606970A8DF6}\PythonCOMPath : dirName
                addnPath=dirName,
            )
            print("Registered COM server.")
            # don't use UseCommandLine(), as it will write InProcServer32: pythoncom39.dll
            # win32com.server.register.UseCommandLine(BasicServer)
        elif sys.argv[1] == "--unregister":
            make_sure_is_admin()

            print("Starting to unregister...")

            win32com.server.register.UnregisterServer(myClsid, myProgID)

            print("Unregistered COM server.")
        else:
            print("Error: arg not recognized")

to register:
python "sympy com server.py" --register


sympy com client.ahk

#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance, force
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetBatchLines, -1
#KeyHistory 0
ListLines Off

sympyComServer:=ComObjCreate("Python.SimplifyExpr")
;or
; pythonComServer:=ComObjCreate("{4530C817-6C66-46C8-8FB0-E606970A8DF6}") ;use your own CLSID

; clipboard:=sympyComServer.parExprN("1+3*7")
clipboard:=sympyComServer.parExprN("1/3 + 1/2")

$#s::
clipboard:=sympyComServer.parExprN(clipboard)
return

f3::Exitapp
Mr. Doge
  • 796
  • 5
  • 11