moses-palmer / pynput

Sends virtual input commands
GNU Lesser General Public License v3.0
1.79k stars 248 forks source link

'HOTKEYS' referenced before assignment #368

Closed SpecialCharacter closed 3 years ago

SpecialCharacter commented 3 years ago

I want to toggle my script, i.e. it runs in the background but has to be activated by a key combination. However, I run into this:

Unhandled exception in listener callback [...]
NameError: free variable 'HOTKEYS' referenced before assignment in enclosing scope

When I switch the setting, i.e. de-activated upon key combination, everything works fine.

Is this a programming mistake on my side or a bug?

moses-palmer commented 3 years ago

Thank you for your report.

To be frank, this is a programming mistake on your part :-) You have probably forgotten a global statement on the callback function, since Python thinks that HOTKEYS should be a local variable and you have not yet bound it.

If this is correct, you will also have an assignment in the callback (HOTKEYS = ...). You might want to move this out to another scope, as pynput gives you no guarantees about in which thread the callback will execute, and you might introduce race conditions if you modify global state.

I will close this issue, but if your investigation reveals that this is indeed a bug in this library, please reopen.

SpecialCharacter commented 3 years ago

So he does not like this:

    def release_callback(key):
        global HOTKEYS # test
        if Transkription == False:
            for hotkey in HOTKEYS:
                hotkey.release(listener.canonical(key)) # Handle released keys
            pass

You notice that I added the global statement, but he is still not happy.

My HOTKEYS = [...] are defined in the keyboard listener. So you suggest that I move them over to the release callback?

SpecialCharacter commented 3 years ago

Did not work (neither did press callback). Now he complains about my variables referenced before assignment...

keyboard.HotKey(keyboard.HotKey.parse('a'), on_hotkey_a),
NameError: free variable 'on_hotkey_a' referenced before assignment in enclosing scope

What do you mean by "another scope"?

moses-palmer commented 3 years ago

You may want to read up on scoping rules in Python. Here's a link with some information: https://realpython.com/python-scope-legb-rule/

SpecialCharacter commented 3 years ago

I tried many things, but nothing worked so far... My structure is this:

Transkription = False
def keyboard_listener():
        global listener
    global Transkription
    global Hotkeys
              if Transkription == True:
                   def on_hotkey_a():
...
                   HOTKEYS = [keyboard.HotKey(keyboard.HotKey.parse('a'), on_hotkey_a)]
            else:
                 pass

    def press_callback(key):
        global Transkription
        global Hotkeys
        if Transkription == True:
            for hotkey in HOTKEYS:
                hotkey.press(listener.canonical(key)) # Handle pressed keys
        else:
            pass

The program works initially, but as soon as I make Transkription == True, I get free variable 'HOTKEYS' referenced before assignment in enclosing scope.

SpecialCharacter commented 3 years ago

Oh, I just realized it should probably be global HOTKEYS, not Hotkeys. Now the error message is NameError: name 'HOTKEYS' is not defined.

SpecialCharacter commented 3 years ago

Put the HOTKEYS list on the start, but with no hotkeys inside. Program works (yahoo!), but doesn't print hotkeys when activated...

SpecialCharacter commented 3 years ago

Had to change True into False. Not sure why, but now it works as intended :) Programming mistake finally solved!