1

I want to make hotstrings in python that converts one word when typed into another after some processing, since AHK is very limiting when it comes to determining which word to type. Right now, I am using a hotstring in ahk that runs code on the command line that runs a python script with the word that I typed as arguments. Then I use pyautogui to type the word. However, this is very slow and does not work when typing at speed. I'm looking for a way to do this all with python and without ahk, but I have not found a way to do hotstrings in python. For example, every time I type the word "test" it replaces it with "testing." Thanks for your help. I'm running the latest version of Python and Windows 10 if that is useful to anyone by the way.

Jack N
  • 324
  • 2
  • 14
  • 1
    *"since AHK is very limiting when it comes to determining which word to type"* I'd assume you just don't know how to do it. I doubt you could think of hotstring replacement that's possible in Python, but not in AHK. Could you maybe share your AHK code of what you tried to do? – 0x464e Jan 18 '21 at 18:44
  • In order to choose which word to type it will use a machine learning algorithm and choices from very complex matrices that are not possible to do in AHK. – Jack N Jan 18 '21 at 18:59
  • Ok, well that's new. I guess I can't comment on how possible it would be to do that since I don't know what this machine learning algorithm is. Maybe an option worth noting would be communicating between your AHK script and whatever you have that'll take care of the machine learning algorithm. The [documentation](https://www.autohotkey.com/docs/commands/OnMessage.htm#ExCustom) for `OnMessage()` shows a *simple* way of receiving a custom message. – 0x464e Jan 18 '21 at 19:11
  • Thanks for your help. I'll take a look at that. Ideally though, it would all be done in Python though, since python is much better for the NLP and then there would be less of a delay. – Jack N Jan 18 '21 at 19:14

1 Answers1

2

(if you want to process it as each letter is typed(t,te,tes,test), you should edit your question)

I call my SymPy functions using ahk hotkeys. I register the python script as a COM server and load it using ahk.
I do not notice any latency.

you'll need pywin32, but don't download using pip install pywin32
download from https://github.com/mhammond/pywin32/releases
OR ELSE IT WON'T WORK for AutoHotkeyU64.exe, it will only work for AutoHotkeyU32.exe.
make sure to download amd64, (I downloaded pywin32-300.win-amd64-py3.8.exe)
here's why: how to register a 64bit python COM server

toUppercase COM server.py

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

    @staticmethod
    def toUppercase(string):
        return string.upper()
        
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="{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}"
        # this server's (user-friendly) program ID
        myProgID="Python.stringUppercaser"
        
        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\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}
            # and here
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\${myProgID}
            # HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Python.stringUppercaser
            win32com.server.register.RegisterServer(
                clsid=myClsid,
                # I guess this is {fileNameNoExt}.{className}
                pythonInstString=nameNoExt + ".BasicServer", #toUppercase COM server.BasicServer
                progID=myProgID,
                # optional description
                desc="return uppercased string",
                #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\{C70F3BF7-2947-4F87-B31E-9F5B8B13D24F}\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")

you first need to register the python COM server:
first, get your own CLSID: just use a python shell.

import pythoncom
print(pythoncom.CreateGuid())

then, set myClsid to that output

to register:
python "toUppercase COM server.py" --register
to unregister:
python "toUppercase COM server.py" --unregister

hotstring python toUppercase.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
#Persistent
#MaxThreadsPerHotkey 4

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


; * do not wait for string to end
; C case sensitive
:*:hello world::

savedHotstring:=A_ThisHotkey

;theActualHotstring=savedHotstring[second colon:end of string]
theActualHotstring:=SubStr(savedHotstring, InStr(savedHotstring, ":",, 2) + 1)
send, % pythonComServer.toUppercase(theActualHotstring)


return



f3::Exitapp

you can test the speed of hotstring hello world, it's very fast for me.
Edit def toUppercase(string): to your liking

Mr. Doge
  • 796
  • 5
  • 11
  • Thank you for your in depth suggestion. However, I am having an issue with lines 11 and 13 (the ones that have ComObjCreate). When I execute the code I get the error, "the specified module could not be found." I found a bit of info on the topic on the following ahk forum https://www.autohotkey.com/boards/viewtopic.php?t=29394 (ctrl+f "My server code for a test server is" to get to the right spot in the forum). However, I'm not familiar enough with ComObjCreate to make sense of the solution given there. Please let me know if you know a solution to this error. Thanks. – Jack N Jan 19 '21 at 06:25
  • you need to create a file named `python COM server.py`, though you can change the filename, but you must change it everywhere
    and copy paste the code inside
    then INSIDE the file `python COM server.py`, change the value of `_reg_clsid_` to your own CLSID that I told you how to generate
    then run USING COMMAND LINE (I used cmd) `python "python COM server.py" --register`
    did you do that ? was it successful ? (cmd should say `Registered: Python.stringUppercaser`)
    – Mr. Doge Jan 19 '21 at 23:26
  • I won't edit the answer, please I want you to edit it, so others like you will understand. Edit the parts that weren't clear/explicit enough. I don't know how to explain it, you'd know best
    I got my code from [this exact place][1], I had to edit the code a bit. I should post this code on that thread to help people like me... [1]: http://autohotkey.com/boards/viewtopic.php?t=29394
    – Mr. Doge Jan 19 '21 at 23:26
  • Thanks for your help. Your answer was clear. I followed the instructions twice and still got the same error. I named the programs as stated, generated and changed the clsid's in both the python and AHK program, registered the python program successfully with cmd (cd'd to the proper folder location), but I still get that error. I think that it is likely an issue with the AHK script itself rather than having to do with the python program because I get the error message immediately when I run the AHK program. Perhaps you could try the exact code that you sent to verify that it works. Thanks – Jack N Jan 20 '21 at 00:12
  • Perhaps since it is a module not found error some sort of an import statement is missing from the AHK script, although that is just a guess. Also, I just tried it using the most recent and older versions of AHK and I still had that same issue. – Jack N Jan 20 '21 at 00:15
  • OH, an ahk error, I've got that error when using AutoHotkeyU64.exe instead of AutoHotkeyU32.exe, for it to work with AutoHotkeyU64.exe. download from https://github.com/mhammond/pywin32/releases instead of `pip install pywin32`. download amd64, (I downloaded pywin32-300.win-amd64-py3.8.exe) – Mr. Doge Jan 20 '21 at 01:40
  • I just modified my version of AHK to the 32 bit version from the 64 bit version and now the program works. Thank you very much for your help and patience. – Jack N Jan 20 '21 at 02:04
  • 1
    Thank you very much, this works for me! The only thing is that I had to comment out `import admin` and other references to admin. Apparently, it is not part of Python by default? – Cerberus Oct 10 '21 at 03:44
  • I got it here : https://stackoverflow.com/questions/19672352/how-to-run-script-with-elevated-privilege-on-windows#answer-19719292 ;; I've realized it creates a new process : so you can't see the output.. `"Registered COM server."` I'll remove it and tell user instead if it's not elevated. – Mr. Doge Oct 12 '21 at 00:40