spyoungtech / ahk

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

cannot assign values to global variables in callbacks #249

Closed aaaddd4747 closed 10 months ago

aaaddd4747 commented 11 months ago

im not sure if this is intended behaviour or not, but it is very inconvenient.

here is an example of a failing case:

import time
from ahk import AHK
ahk = AHK()
mousepos = (0,0)

def update_mouse():
    print("attempting to assign")
    mousepos=ahk.get_mouse_position()

ahk.add_hotkey('^+y',callback=update_mouse)

ahk.start_hotkeys()
while True:
    time.sleep(0.1)
    print(*mousepos)

apologies in advance if im just forgetting how to use python

spyoungtech commented 11 months ago

This is more of a facet of Python than anything. There's a few ways you might overcome this situation.

You might use the global keyword.

def update_mouse():
    global mousepos
    # ...
    mousepos = ...

You could also use a mutable object as your global variable instead, like a dictionary.

data = {'mousepos': None}
def update_mouse():
    data['mousepos'] = ...

But keep in mind that these callbacks run in their own threads, so considerations for shared state, race conditions, etc. may apply if you have callbacks that potentially fire at the same time or if your main thread also changes data that is changed by your callbacks.

aaaddd4747 commented 10 months ago

for the time being i used the registry commands as a workaround, storing anything that needs to share state across what eventually dawned on me would be different threads and/or processes (this also makes the state of the script persistent, which is both a blessing and a curse depending on the application). this idea of using an object sounds interesting though, il have to check that out. fortunately the mode of most of the programs im making are just hotkeys setting variables that then get read by a main loop, so any race conditions are unlikely to occu. transient misalignments of state always correct themselves for the next loop of main. though, if that does ever come up, does python have any mutex like facility?

spyoungtech commented 10 months ago

does python have any mutex like facility?

Yep! You could do something like this:

from threading import Lock
mouse_pos = (0, 0)
position_lock = Lock()

def update_position():
    global mouse_pos
    with position_lock:
        # acquires the lock before this block executes
        mouse_pos = ...
    # when the block ends, the lock is released

There's probably better ways to encapsulate this, but that's just the basic idea for locks.

Sounds like you have a handle on the situation. Good Luck!