SavinaRoja / PyUserInput

A module for cross-platform control of the mouse and keyboard in python that is simple to install and use.
GNU General Public License v3.0
1.07k stars 244 forks source link

Pymouse / PyMouseEvent .run() doesn't resume function after .stop() is called #117

Open joaomamede opened 5 years ago

joaomamede commented 5 years ago

This is a linux machine. This was working 3/4 years ago when I wrote it and now it's buggy.

from pymouse import PyMouse
from pymouse import PyMouseEvent
from pykeyboard import PyKeyboard
import pyperclip
import numpy as np
m = PyMouse()
k = PyKeyboard()

class set_position(PyMouseEvent):
    def __init__(self):
        PyMouseEvent.__init__(self)

    def click(self,x,y,button,press):
        if button == 1:
            if press:
               self.value = m.position()
               print self.value
        else:
            print "aborted"
            self.stop()

Then in my main program:

This is how it was and it was running fine in previous versions.

    print "Please Click on a section without text"
    scan_button = set_position()
    scan_button.run()
    scan_x, scan_y = scan_button.value

When I clicked with button 1 it would set the value, correctly and when I clicked middle or button 2, it would resume the python script or, go back to prompt.

Now it gets stuck. if I put prints on the "if button ==1" then and else. It does capture mouse events until I press any other button. But then it gets stuck until ctrl+c is pressed many many times.

if I switch to

    print "Please Click on a section without text"
    scan_button = set_position()
    scan_button.start()
    scan_x, scan_y = scan_button.value

It comes back to the python shell but value was never defined because it runs in the background and proceeds right away.

Am I doing something wrong that doesn't match most recent versions? Thanks

The example with clickonacci has the exact same problem, anything I can do?

vkresch commented 3 years ago

Same issue! Maybe xlib had some updates which broke pymouse. (Here the related issue) For some reason it gets stuck in the context manager. I'm currently not sure what display2 is needed for but commenting:

with display_manager(self.display2) as d:
    d.ungrab_pointer(X.CurrentTime)
    d.record_disable_context(self.ctx)

works. But if you are using a loop which reruns an already created PyMouseEvent object (when initializing from scratch then it works) I get the following error thrown:

  self.display2.record_enable_context(self.ctx, self.handler)
  File "python/lib/python3.8/site-packages/Xlib/ext/record.py", line 252, in enable_context
    EnableContext(
  File "python/lib/python3.8/site-packages/Xlib/ext/record.py", line 232, in __init__
    rq.ReplyRequest.__init__(self, *args, **keys)
  File "python/lib/python3.8/site-packages/Xlib/protocol/rq.py", line 1420, in __init__
    self.reply()
  File "python/lib/python3.8/site-packages/Xlib/protocol/rq.py", line 1440, in reply
    raise self._error
Xlib.error.XError: <class 'Xlib.error.XError'>: code = 154, resource_id = 98566144, sequence_number = 16, major_opcode = 146, minor_opcode = 5

Has maybe something to do with not closing display2 correctly. Any help would be appreciated. Thanks!

mikeabbott10 commented 3 years ago

I faced a cpu-hungry behavior when stop() is called. This is due to being stucked in the while loop in reply function (line 1483 in Xlib/protocol/rq.py). The reason here is that we're not updating _data from the send_and_recv function we call at line 1493. This problem disappear if we don't call display_manager(self.display2) and we call

   d.ungrab_pointer(X.CurrentTime)
   d.record_disable_context(self.ctx)

with self.display2 in place of d (the file here is pymouse/x11.py), but I actually don't know if it's safe to do this.

Btw the issue seems the display_manager(self.display2) call. If you look at this function you can see that the display.sync() function takes us straight to the loop we were talking about. But, the first time ( with display_manager(self.display) call ) I got the answer {'sequence_number': 11, 'accel_num': 2, 'accel_denom': 1, 'threshold': 4} to the request. The second time ( with display_manager(self.display2) call ) I don't get any answer and that's why we're stucked inside the loop. I don't know how this request-and-response system works here. I just hope that these information can help someone who already knows pymouse and Xlib better than me.

EDIT: From client side I can avoid this behavior overriding stop() like this:

class example(PyMouseEvent):
   def __init__(self):
       PyMouseEvent.__init__(self)

   def click(self,x,y,button,press):
       # do something
       pass

   def stop(self):
       self.state = False
       with x11.display_manager(self.display) as d:
           d.ungrab_pointer(X.CurrentTime)
           d.record_disable_context(self.ctx)
       #with display_manager(self.display2) as d:
           self.display2.ungrab_pointer(X.CurrentTime)
           self.display2.record_disable_context(self.ctx)

I have obviously to import the modules i need:

from pymouse import PyMouseEvent, x11
from Xlib import X
mikeabbott10 commented 3 years ago

Same issue! Maybe xlib had some updates which broke pymouse. (Here the related issue) For some reason it gets stuck in the context manager. I'm currently not sure what display2 is needed for but commenting:

with display_manager(self.display2) as d:
    d.ungrab_pointer(X.CurrentTime)
    d.record_disable_context(self.ctx)

works. But if you are using a loop which reruns an already created PyMouseEvent object (when initializing from scratch then it works) I get the following error thrown:

  self.display2.record_enable_context(self.ctx, self.handler)
  File "python/lib/python3.8/site-packages/Xlib/ext/record.py", line 252, in enable_context
    EnableContext(
  File "python/lib/python3.8/site-packages/Xlib/ext/record.py", line 232, in __init__
    rq.ReplyRequest.__init__(self, *args, **keys)
  File "python/lib/python3.8/site-packages/Xlib/protocol/rq.py", line 1420, in __init__
    self.reply()
  File "python/lib/python3.8/site-packages/Xlib/protocol/rq.py", line 1440, in reply
    raise self._error
Xlib.error.XError: <class 'Xlib.error.XError'>: code = 154, resource_id = 98566144, sequence_number = 16, major_opcode = 146, minor_opcode = 5

Has maybe something to do with not closing display2 correctly. Any help would be appreciated. Thanks!

This project is no longer mainteined btw. Use https://github.com/moses-palmer/pynput instead

EDIT: just faced the same issue with pynput because this is a platform dependent problem.