Error when running a simple pynput program - python

I'm making a project and a part of it is making a program that when activated, automatically right clicks whenever I left click. But when I launch the program and left click, it returns an error:
TypeError: combatModules.on_click() takes 4 positional arguments but 5 were given
My code: (I'm using threads so i can have multiple programs running at once from the same program.)
import pydirectinput as pa
import time as t
import pynput
import threading
class combatModules:
def __init__(self) -> None:
pass
def on_click(x, y, button, pressed):
if button == pynput.mouse.Button.left:
print('{} at {}'.format('Pressed Left Click' if pressed else 'Released Left Click', (x, y)))
pa.rightClick()
else:
print('{} at {}'.format('Pressed Right Click' if pressed else 'Released Right Click', (x, y)))
def blockHitCode(self):
for i in range(100):
listener = pynput.mouse.Listener(on_click=self.on_click)
listener.start()
listener.join()
def blockHit(self):
blockHitThread = threading.Thread(target=self.blockHitCode)
blockHitThread.start()
A little explanation: blockHit() is meant to be executed from the main program.

Your method is missing the self argument. This is required for methods in a class like you have with the blockHitCode and blockHit.
This should fix the error you are having:
def on_click(self, x, y, button, pressed):

Related

Pynput sent mouse only press and won't release

Hi I wanted to bind extra buttons on my mouse using pynput for playing a game
Button.x1 for spam spacebar key
Button.x2 for spam left mouse
and all of them is trigger
for x1 button is worked. but the x2 is more likely holding the left mouse button, when I activate it
I've tried run the script on terminal (as admin). but still won't work
here is my code
from pynput.mouse import Listener, Button, Controller as MController
from pynput.keyboard import Key, Controller as KController
from time import sleep
from threading import Thread
from random import uniform
C_keyboard = KController()
C_mouse = MController()
state = [False, False]
def bind(i, key, c):
global state
while state[i]:
c.press(key)
sleep(round(uniform(0.5, 1.0), 10))
c.release(key)
def onClick(x, y, button, pressed):
global state
global C_keyboard
global C_mouse
if pressed:
if button == Button.x1:
state[0] = not state[0]
Thread(target=bind, args=(0, Key.space, C_keyboard)).start()
elif button == Button.x2:
state[1] = not state[1]
Thread(target=bind, args=(1, Button.left, C_mouse)).start()
print("%s is %s (%s, %s)" % (button, ("Pressed" if pressed else "Released"), state[0],state[1]))
def main():
with Listener(on_click=onClick) as listener:
listener.join()
Thread(target=main).start()
here is the output of my onClick function when I pressed x2 once

python - fastest way to click mouse

I'm trying to make an auto click bot in python using pyautogui but this takes too much time (it runs in a loop, and xPos/yPos changes every time). What faster ways are there? Thanks for helping.
pyautogui.PAUSE = 0.001
pyautogui.click(xPos,yPos,button = 'left')
You can use pynput:
from pynput import mouse
from pynput.mouse import Controller, Button
import time
mouse = Controller()
one = time.time_ns()
for i in range(1000):
mouse.click(Button.left)
two = time.time_ns()
print(two-one)
With this setup im able to execute 1000 clicks in .53 seconds.
I'm testing three library: Pyautogui, Mouse and Pynput.
Links to libs:
Pyautogui last upd. OCT 2021
Mouse last upd. APR 2021
Pynput last upd. OCT 2021
Below code and result:
PYAUTOGUI version:
import pyautogui
def click_pyautogui(x, y, button):
pyautogui.moveTo(x, y)
pyautogui.click(button=button)
MOUSE version:
import mouse
def click_mouse(x, y, button):
mouse.move(x, y, absolute=True)
mouse.click(button=button)
PYNPUT version:
from pynput.mouse import Button, Controller
def click_pynput(x, y, button):
mouse = Controller()
mouse.position = (x, y)
button = Button.left if button=='left' else Button.right
mouse.click(Button.left)
wrapper:
def click(x, y, button):
# pyautogui
# click_pyautogui(x, y, button)
# mouse
# click_mouse(x, y, button)
# pynput
click_pynput(x, y, button)
import timeit
if __name__ == '__main__':
print(timeit.timeit("click(random.randrange(100), random.randrange(100), 'left')", number=100, globals=locals()))
RESULTS time for 100 cycles (average of 3, very small variations):
Pyautogui: 22.07 sec
Mouse : 0.16 sec
Pynput : 0.20 sec
Mouse look as fastest library!
I can't comment so I have to make a post, I just wanted to say that #swasher 's speed test is incorrect.
from pynput.mouse import Button, Controller
def click_pynput(x, y, button):
mouse = Controller()
mouse.position = (x, y)
button = Button.left if button=='left' else Button.right
mouse.click(Button.left)
This creates a new Controller object every time click_pynput is called, which is unnecessary and slow.
Creating the Controller object once before the function declaration is much better:
from pynput.mouse import Button, Controller
_mouse = Controller()
def click_pynput(x, y, button):
_mouse.position = (x, y)
button = Button.left if button=='left' else Button.right
_mouse.click(Button.left)
This in fact showed (on my pc) that pynput and mouse have the same speed.
Real RESULTS time for 100 cycles (average of 3, very small variations):
Mouse : 0.1317509999498725 sec
Pynput : 0.1323150999378413 sec
Also I tested just move speed and pyinput is slightly faster (1000000 iterations):
Mouse : 24.206686099991202 sec
Pynput : 20.718958700075746 sec

Disable click events from queuing on a widget while another function runs

I have a function (A) that is bound to a click event on a Canvas. At some point, function A calls another function (B). The canvas contents may change in function B, so I want it to ignore canvas click events while it's running. I've tried setting a bool variable that is checked when function A is called to False before the function B is called and then resetting it to True when the function B is done, but the event still queues with bad mouse coordinates for the new contents. I've tried re-binding canvas click events to a function (C) that just returns "break" then calling the function B then re-binding canvas click events back to function A. Still queues with bad mouse coordinates for the new contents. I've tried setting the canvas state to "disabled" calling function B then setting the state to "normal". I've tried returning "break" from function A, function B, and from both. I get the same result it still queues with bad mouse coordinates for the new contents. Any suggestions?
the code I'm using for testing options in its current state:
import time
from tkinter import *
class test_form(object):
def __init__(self, master):
self.master = master
self._CreateGUI()
def _CreateGUI(self):
geom = "200x175+0+0"
self.master.geometry(geom)
self.xx=Canvas(bg='#ff0000',width=100,height=75)
self.xx.bind('<ButtonRelease>',self.test_button)
self.xx.pack()
def waste_time(self):
print('time wasted')
time.sleep(10)
return "break"
def test_button(self,event=None):
self.waste_time()
return "break"
def start_gui():
root = Tk()
form_load = test_form(root)
root.mainloop()
if __name__ == "__main__":
start_gui()
The following approach seems to fix your problem. It's likely similar to one you've already tried but with an after() thrown in:
class test_form(object):
def __init__(self, master):
self.master = master
self._CreateGUI()
def _CreateGUI(self):
geom = "200x175+0+0"
self.master.geometry(geom)
self.xx = Canvas(bg='#ff0000', width=100, height=75)
self.xx.pack()
self.notice()
def waste_time(self):
print('time wasted')
time.sleep(10)
def notice(self):
self.xx.bind('<ButtonRelease>', self.test_button)
def ignore(self):
self.xx.bind('<ButtonRelease>', lambda event: "break")
def test_button(self, event):
self.ignore()
self.waste_time()
self.master.after(500, self.notice)

Print while mouse pressed

i am using PyMouse(Event) for detecting if mouse button is pressed:
from pymouse import PyMouseEvent
class DetectMouseClick(PyMouseEvent):
def __init__(self):
PyMouseEvent.__init__(self)
def click(self, x, y, button, press):
if button == 1:
if press:
print("click")
else:
self.stop()
O = DetectMouseClick()
O.run()
This works so far, but now i want to loop print("click") until mouse isnt pressed anymore ... i tried:
def click(self, x, y, button, press):
if button == 1:
if press:
do = 1
while do == 1:
print("down")
if not press:
do = 0
And also smth. like:
while press:
print("click")
Someone can help me? Thanks!
I think as Oli points out in his comment there isn't a constant stream of clicks when the mouse button is held down so you'll have to have the print in a loop. Having the while loop running on the same thread prevents the click event firing when the mouse is released so the only way I can think of to achieve what you are after is to print("click") from a separate thread.
I'm not a Python programmer but I've had a stab which works on my machine (Python 2.7 on Windows 8.1):
from pymouse import PyMouseEvent
from threading import Thread
class DetectMouseClick(PyMouseEvent):
def __init__(self):
PyMouseEvent.__init__(self)
def print_message(self):
while self.do == 1:
print("click")
def click(self, x, y, button, press):
if button == 1:
if press:
print("click")
self.do = 1
self.thread = Thread(target = self.print_message)
self.thread.start()
else:
self.do = 0
print("end")
else:
self.do = 0
self.stop()
O = DetectMouseClick()
O.run()

Python: How to distinguish between long, short button press and click

I want to have a simple GUI with 4 buttons. If you just click the button, function A should be executed, for short button press (e.g.1sec) function B should be executed and finally a long press (e.g. > 2s) function C should be executed.
Imagine a counter.
If you click the button, it will be reset to 0
If you short press the button, counter will be increased by 1 for e.g t=1sec
If you long press the button, counter will be increased by 10 until button is released.
Is somebody haveing an idea. I was trying this to accomplish it with a 2nd thread but I haven't found a possibility to stop the thread like you can start it
This is easy to do in PyQt if you use a widget which inherits QAbstractButton. No need for any timers or separate threads. Just use the built-in auto-repeat functionality, and keep a record of the current state.
Here's a simple demo:
from PyQt4 import QtGui, QtCore
class Button(QtGui.QPushButton):
def __init__(self, *args, **kwargs):
QtGui.QPushButton.__init__(self, *args, **kwargs)
self.setAutoRepeat(True)
self.setAutoRepeatDelay(1000)
self.setAutoRepeatInterval(1000)
self.clicked.connect(self.handleClicked)
self._state = 0
def handleClicked(self):
if self.isDown():
if self._state == 0:
self._state = 1
self.setAutoRepeatInterval(50)
print 'press'
else:
print 'repeat'
elif self._state == 1:
self._state = 0
self.setAutoRepeatInterval(1000)
print 'release'
else:
print 'click'
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
button = Button('Test Button')
button.show()
sys.exit(app.exec_())
in wxPython I would do it like this ... however you may need to adjust it for your GUI library ...
start_time = None
def onLeftDown(e):
global running
running = True
ct =0
while running:
ct += 1
do_something(ct)
def onLeftUp(e):
print "You Pressed For %s Seconds!"%(time.time()-start_time)
my_btn = wx.Button(parent,-1,"Click Me!")
my_btn.Bind(wx.EVT_LEFT_DOWN,onLeftDown)
my_btn.Bind(wx.EVT_LEFT_UP,onLeftUp)
Im not very familliar with QT but maybe you can modify this wx code to do what you want...
import wx
ct = 0
def counting():
global running
global ct
if running:
ct +=1
print ct
wx.CallLater(1,counting)
else:
print "OK DONE COUNTING AT:",ct
def onLeftDown(evt):
global running
running = True
counting()
def onLeftUp(evt):
print "STOP NOW!!"
global running
running = False
a = wx.App(redirect=False)
f = wx.Frame(None,-1,"asdasd")
b = wx.Button(f,-1,"Click Me")
b.Bind(wx.EVT_LEFT_DOWN,onLeftDown)
b.Bind(wx.EVT_LEFT_UP,onLeftUp)
f.Show()
a.MainLoop()

Categories