0

How can I return to a function after exiting a previous one? My code is this:

import pyHook, pythoncom, sys
import msvcrt

def test(keyLogging):
    key = msvcrt.getwche()
    if key == 'x':
        print("Worked")
        sys.exit(0)
    return None

def test2(keyLogging):
    key = msvcrt.getwche()
    if key == 'c':
        print("Worked, again")
        sys.exit(0)
    return None

def keyLogging():
    key = msvcrt.getwche()
    if key == 'z':
        hm = pyHook.HookManager()
        hm.KeyDown = test
        hm.HookKeyboard()
        pythoncom.PumpMessages()

    elif key == 'v':
        hm = pyHook.HookManager()
        hm.KeyDown = test2
        hm.HookKeyboard()
        pythoncom.PumpMessages()


keyLogging()

After calling a function from keyLogging test or test2 and the called one finishes, I want to return back to keylogging to give an option again which function to run. Something like an infinite loop but with functions. sys.exit() just terminates everything, I have also tried threading inside the two function and also returning the keyLogging on return

  • Code execution *does* return to the function `keyLogging`. Try it: put a `print('hi')` at the end of that function. Do you want a loop somewhere? – Jongware Dec 14 '18 at 15:18

2 Answers2

1

Just put keylogging() at the end of each function call that you want to return to keylogging(). ex:

def test(keyLogging):
    key = msvcrt.getwche()
    if key == 'x':
        print("Worked")
    keyLogging()        
    return None
  • `return None` is redundant as without it it returns `None` by default. – Sergey Pugach Dec 14 '18 at 15:23
  • True, but this is how the code was formatted by the op. I don't know that the return statement will always be none. It could be a placeholder. I just kept the code the same as what I received except for the change I pointed out in the answer. The question wasn't about return statements so I didn't presume to know what the op was thinking with regard to his. –  Dec 14 '18 at 15:25
  • This is what I get `TypeError: 'KeyboardEvent' object is not callable` –  Dec 14 '18 at 15:27
  • I did just notice that the function shouldn't be embedded in that if statement, since it will only call keylogging when the statement evaluates as "`True`. The type error may have been due to a typo on my part. Your function is `keyLogging`, not `keylogging`. I can update that in the answer now but, before I do, is there a specific reason that you need the `sys.exit` at the end of the function? Without that, the `keyLogging` function can just be called at the end, before the return statement, rather than calling keylogging before finishing the `test` functions. –  Dec 14 '18 at 15:32
  • I removed `sys.exit()` and added keyLogging at the end and I get this error. I don't `sys.exit()` for any reason It was just for testing if it woks this way. –  Dec 14 '18 at 15:35
  • What version of python are you using? I can't install pyHook to test this code in my base env, but if I create a virtual environment running your python version I should be able to see what's going on. –  Dec 14 '18 at 15:42
  • I am using visual studio 2017 which has 3.6 version, so I run it on 3.6 –  Dec 14 '18 at 15:46
  • Hmm, weird. I'm on 3.6.7, running out of VSCode. I'll try running it in 3.6.0 and see if I can install your dependencies. Just a sec. –  Dec 14 '18 at 15:47
0

EDIT: Based on the comments it looks like pyhook module may be somewhat broken.
You may want to use pywin32 to access windows hooking API another way.
I don't have experience with this project my self, but it appears to be actively maintained, unlike pyhook, and thats always a good sign.

Your question is not really about "returning to function".
It appears that you are setting a keyboard hook under windows, and you want to reset it, every time the hooked event fires.

To do this, you need to restructure your program, because if you simply call keyLogging again the way it is written in your code, you will be stuck with multiple attempts to pump the message queue and multiple hooks installed.

The pyhook library documentation is pretty bad, so I am not sure what kind of errors you would get, but this is all the more reason to restructure your program for clean operation.

First - have a main function, like this:

def main():
    keyLogging(False) # call with False parameter means no old hooks to clean up
    pythoncom.PumpMessages()

Notice, I added a parameter to your keyLogging function, this is so the function will know if it needs to perform cleanup.

Second - change your key logging setup function to enable cleanup of old hooks, and also, make sure it exits when done:

def keyLogging(cleanup):
    hm = pyHook.HookManager()

    # if you called this function before, uninstall old hook before installing new one!
    if cleanup:
        hm.UnhookKeyboard()

    key = msvcrt.getwche() # ask user for new hook as you did originaly

    # in the if block, only select what hook you want, clean up redundant code!
    if key == 'z':
        hm.KeyDown = test
    elif key == 'v':
        hm.KeyDown = test2

    hm.HookKeyboard() # finally, install the new hook

Third - now that you have a suitable version of keyLogging function, you can call it from your hook functions to allow the user to choose a new hook.
Note that you must not give your hook function a parameter name that is the same as another name in your code!
This is called 'shadowing' and it means you will not be able to access the other thing from within the function!

def test(event):
    key = msvcrt.getwche()
    if key == 'x':
        print("Worked")

    keyLogging(True) # call with True parameter means old hook needs cleanup

And now your program will do what you wanted cleanly.

One more thing to consider, is that if you want to know what key the user pressed to trigger your test function, you don't need to use msvcrt.getwche().
It is already passed in the function parameter event so you can check it like this:

def test(event):        
    if event.getKey() == 'x' or event.Ascii == 'x':
        print("Worked")

    keyLogging(True)

This is documented here.

Lev M.
  • 6,088
  • 1
  • 10
  • 23
  • I am using pyHook because after I solve this I want to make work in the background, to catch the shortcuts while running as background process. But anyway thanks I will check it as soon as I find some time. –  Dec 14 '18 at 17:26
  • If I use the test function with `if event.getKey() == 'x' or event.Ascii == 'x':` I get `AttributeError: 'KeyboardEvent' object has no attribute 'getKey'` , if I use the other function with `key = msvcrt.getwche()` I get `AttributeError: 'HookManager' object has no attribute 'keyboard_hook'` –  Dec 14 '18 at 18:31
  • Have you tried using only `event.Ascii`? Like I said, the documentation for pyhook looks garbage, it may be out of date. Unfotunatly, I don't have a windows machine to test this on. What line are you getting the error `AttributeError: 'HookManager' object has no attribute 'keyboard_hook'`? – Lev M. Dec 14 '18 at 20:39
  • Here is the code https://pastebin.com/MXf7e5Nz. I found a solution for the first error here https://stackoverflow.com/questions/39806560/no-attribute-hookmanager with what @Raqun Bob says, but if I do this I can only run this one time. It keeps running but it doesn't get inputs. I press key but not doing anything. –  Dec 14 '18 at 21:04
  • As far as I can tell from the code you posted, pyhook, or at least the specific version you are using is broken. The UnhookKeyboard method does not work, even though it is documented. There is one thing you might want to try: put `HookManager` in a global var and always use the same one. Perhaps that is what the library maker intended. Another option is removing the unhook code. Not sure what that will do, but if it is broken anyway, couldn't hurt to try... – Lev M. Dec 14 '18 at 22:39