When using mouse click event on python, Multiple clicks happen at once. This makes loops to have problem working with a variable that is constantly changing.
I want to single thread it. Stop the click event until all the functionalities in that thread is done, then listen for more.
I am also not sure why I am getting multiple clicks at once. Every time I click, it fires 3 times. That's why I want to ask this question overall to solve this issue.
Here is my code
import matplotlib.pyplot as plt
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
import numpy as np
from mpl_point_clicker import clicker
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def isAround(self, anotherPoint):
if anotherPoint.x == self.x + 1 or anotherPoint.x == self.x - 1 or anotherPoint.x == self.y + 1 or anotherPoint.x == self.y - 1:
return True
return False
def roundPoints(self):
self.x = round(self.x, 2)
self.y = round(self.y, 2)
mouseClicks = []
slopeAndLengths = []
count = 0
def on_pick(event):
global count
artist = event.artist
xmouse, ymouse = event.mouseevent.xdata, event.mouseevent.ydata
x, y = artist.get_xdata(), artist.get_ydata()
ind = event.ind
# print ('Artist picked:', event.artist)
# print ('{} vertices picked'.format(len(ind)))
print ('Pick between vertices {} and {}'.format(min(ind), max(ind)+1))
# print ('x, y of mouse: {:.2f},{:.2f}'.format(xmouse, ymouse))
# print ('Data point:', x[ind[0]], y[ind[0]])
# print(" ------------------- ")
thisPoint = Point(x[ind[0]], y[ind[0]])
thisPoint.roundPoints()
mouseClicks.append(thisPoint)
for i in range(len(mouseClicks)):
for j in list(range(len(mouseClicks))):
if mouseClicks[i].isAround(mouseClicks[j]):
if not mouseClicks[i] == mouseClicks[j]:
mouseClicks.pop(i)
print(mouseClicks)
# mouseClicks.append((round(x[ind[0]],2),round(y[ind[0]],2)))
# count += 1
# if count % 2 == 0:
# mouseClicks.pop()
# print(mouseClicks)
if len(mouseClicks)==2:
length = mouseClicks[1].x - mouseClicks[0].x
slope = (mouseClicks[1].y - mouseClicks[0].y)/(mouseClicks[1].x - mouseClicks[0].x)
slopeAndLengths.append((round(slope,2),round(length,2)))
mouseClicks.pop(0)
# print(slopeAndLengths)
# print(mouseClicks)
# addPoint(round(x[ind[0]],2),round(y[ind[0]],2))
# ax.plot(round(x[ind[0]],2),round(y[ind[0]],2),'bo-', zorder=2)
# plt.draw()
arr_lena = mpimg.imread('picture.png')
imageBox = OffsetImage(arr_lena, zoom=2)
ab = AnnotationBbox(imageBox, (7.4, 13.6), zorder=1)
fig, ax = plt.subplots()
ax.add_artist(ab)
for row in range(1,30):
tolerance = 30 # points
ax.plot(np.arange(0,15,0.5),[i*row/i for i in range(1,15*2+1)], 'ro-', picker=tolerance, zorder=0)
fig.canvas.callbacks.connect('pick_event', on_pick)
klicker = clicker(ax, ["event"], markers=["x"], **{"linestyle": "--"})
plt.draw()
plt.savefig('add_picture_matplotlib_figure.png',bbox_inches='tight')
plt.show()
I have tried this idea to remove points that are near the first received point, but because multiple threads happen at once, this doesn't help.
for i in range(len(mouseClicks)):
for j in list(range(len(mouseClicks))):
if mouseClicks[i].isAround(mouseClicks[j]):
if not mouseClicks[i] == mouseClicks[j]:
mouseClicks.pop(i)
How I fix it was like this:
before I go to the answer, we know that every time a click is received, on_pick function will run. So using time I could dismiss the event fires that were happening at the same time, leaving only one of them.
Answer:
Before = datetime.now()
def on_pick(event):
global Before
now = datetime.now()
if (now < Before + timedelta(seconds=1)):
return
Before = datetime.now()
...