1

I'm trying to make a python script that starts counting from 0 when the mouse button is pressed. My idea is to use pyHook to go into a function when left mouse button is pressed and exit the function when left mouse is released. I'm pretty new to python so sorry for bad explanations. Some pseudocode:

import pyHook
import pythoncom

def termin():
    return None
def counter(tell):
    a=0
    while True:
        print a
        a+=1
        hm = pyHook.HookManager()
        hm.SubscribeMouseLeftUp(termin)

hm = pyHook.HookManager()
hm.SubscribeMouseLeftDown(counter)
hm.HookMouse()
pythoncom.PumpMessages()
hm.UnhookMouse()

This code is my general idea, however I don't think it will work because SubscribeMouseLeftUp happens at a discrete time. What i'm looking for is maybe running the counter function and termin function in some kind of threading or multiprocessing module and use conditions in one function to terminate the other running function. But I'm not sure how to make this work.

Okay, so I tried this script after willpower's comment:

import pyHook,time,pythoncom

def counter(go):
    for a in range(5):
        time.sleep(1)
        print a
    return True

hm=pyHook.HookManager()
hm.SubscribeMouseLeftDown(counter)
hm.HookMouse()
pythoncom.PumpMessages()
hm.UnhookMouse()

The accepted answer from willpower2727 is the best solution I've seen so far. Before he posted his solution using threading I made the following code:

from multiprocessing import Process,Queue
import pyHook
import time
import pythoncom
import ctypes

def counter(tellerstate,q):
    while True:
        a=0
        tellerstate=q.get()
        if tellerstate==1:
            while True:
                a+=1
                print a
                tellerstate=q.get()
                if tellerstate==0:
                    break
        time.sleep(0.1)

def mousesignal(q):
    def OnDown(go):
        tellstate=1
        q.put(tellstate)
        return None

    def OnUp(go):
        tellstate=0
        q.put(tellstate)
        return None

    def terminate(go):
        if chr(go.Ascii)=='q' or chr(go.Ascii)=='Q':
            ctypes.windll.user32.PostQuitMessage(0)
            hm.UnhookKeyboard()
            hm.UnhookMouse()
            q.close()
            q.join_thread()
            process_counter.join()
            process_mousesignal.join() 
        return None

    hm=pyHook.HookManager()
    hm.KeyDown = terminate
    hm.MouseLeftDown = OnDown
    hm.MouseLeftUp = OnUp
    hm.HookMouse()
    hm.HookKeyboard()
    pythoncom.PumpMessages()

if __name__ == '__main__':
    tellerstate=0
    q=Queue()
    process_counter = Process(target=counter,args=(tellerstate,q))
    process_mousesignal = Process(target=mousesignal,args=(q,))
    process_mousesignal.start()
    process_counter.start()

My expected behaviour of this code is that the counter and mousesignal functions should run as separate processes. In the mousesignal process I'm putting either a 0 or 1 to a Queue based on mouse input. The counter function runs continuously and reads the Queue and uses if statements to enter and quit the loop in this function. This code doesn't work at all, but I can't understand why.

mathiasxx94
  • 23
  • 1
  • 8
  • Just a suggestion, you might try getting a working example of a pyhook program and then try and tweak to suit your needs. Once you have a working example it will be much easier to help you. I'd suggest this example as a possible place to start: https://gordoncluster.wordpress.com/2013/09/12/logging-all-keyboard-input-with-python-pyhook/ – willpower2727 Nov 21 '16 at 19:13
  • Added working counter code. – mathiasxx94 Nov 25 '16 at 15:42
  • Are you interested in calculating the cumulative time the mouse button is down? Or some other measure of counting? – willpower2727 Nov 28 '16 at 14:52

2 Answers2

1

Based on the example you provided it looks like you want to count the number of seconds the mouse button is down? You can do this without using multiple threads. The following example will print the amount of time the mouse button was pressed down for:

import pyHook
import time
import pythoncom

global starttime
starttime = time.time()
global endtime
endtime = time.time()

def OnDown(go):
    global starttime
    starttime = time.time()
    return True

def OnUp(go):
    global starttime
    global endtime
    endtime = time.time()
    etime = endtime-starttime
    print(etime)
    return True

hm = pyHook.HookManager()
hm.MouseLeftDown = OnDown
hm.MouseLeftUp = OnUp
hm.HookMouse()
pythoncom.PumpMessages()
willpower2727
  • 769
  • 2
  • 8
  • 23
  • Thank you very much for the answer willpower. Unfortunately I may have worded my request with some ambiguity. The program is supposed to do an operation in a loop as long as the mouse button is pressed and I just used counting as an example. The reason I can't think of a way without multiprocessing or threading is that as far as I know the program stops listening for mouse input when it goes into a function. @willpower – mathiasxx94 Nov 28 '16 at 22:25
1

Ok I have an example of using threading to do something while the mouse button is held down. This example breaks/gets stuck if the user double-clicks the mouse very quickly. It uses a lock and a thread that only executes certain code when the lock is released (triggered by mouse down).

import time
import threading
import pyHook
import pythoncom

def DoThis(Cond):
    while True:
        with Cond: #calling "with" automatically calls acquire() on the lock
            print(time.time())
    print('stopping...')

global Cond
Cond = threading.Lock()#create a threading lock
Cond.acquire() #give the lock to the main thread

global t1
t1 = threading.Thread(target=DoThis,args=(Cond, )) #initialize the thread that does stuff while mouse button is down
t1.start() #start the thread, it won't do anything until mouse button is down

def OnDown(go):
    global Cond
    Cond.release() #allow the thread to acquire the lock
    print('Lock released')
    return True

def OnUp(go):
    global Cond
    Cond.acquire() #take the lock away from the thread
    print('Lock acquired')
    return True

hm = pyHook.HookManager()
hm.MouseLeftDown = OnDown
hm.MouseLeftUp = OnUp
hm.HookMouse()
pythoncom.PumpMessages()

The challenge with using threads is that they can only be started once so you couldn't call thread.start() everytime the mouse is pressed, it would only work the first time. By allowing the thread to stay alive but not do anything (except constantly check if it should) enables the possibility to only execute some code when the mouse is down. There are some more sophisticated ways to do this that might improve the processing load on the computer (maybe using a thread condition instead of a regular lock) but this is the general idea I had.

willpower2727
  • 769
  • 2
  • 8
  • 23
  • This seems very promising, I really appreciate your help, time to fully understand the threading module then. I've updated the main post with my try by using multiprocessing, however it doesn't work. If you want to take a look at it you are very welcome. – mathiasxx94 Nov 30 '16 at 00:57