... | ... | @@ -21,11 +21,11 @@ Exemple [d'événements ](https://matplotlib.org/stable/users/event_handling.htm |
|
|
| 'pick_event' | PickEvent | artist in the canvas is selected |
|
|
|
| 'resize_event' | ResizeEvent | figure canvas is resized |
|
|
|
|
|
|
## Avec un "MouseEvent"
|
|
|
## Comprendre et manipuler un "MouseEvent"
|
|
|
|
|
|
On travaille d'abord avec une figure ne comportant qu'une seule courbe.
|
|
|
### Avec une figure ne comportant qu'une seule courbe.
|
|
|
|
|
|
### pour commencer, le "clic" écrit juste les coordonnées dans la console
|
|
|
#### pour commencer, le "clic" écrit juste les coordonnées dans la console
|
|
|
|
|
|
```python
|
|
|
import matplotlib.pyplot as plt
|
... | ... | @@ -96,16 +96,137 @@ selection, = ax.plot([0],[0], marker='*', c='red', markersize=20) |
|
|
|
|
|
fig.canvas.mpl_connect('button_press_event', onclick_proche)
|
|
|
plt.show()
|
|
|
|
|
|
### Le tableau ci-dessous montre le résultat de l'identification du point le plus proche à partir des coordonnées de la souris ou de Picker, pour ce deuxième cas en montrant la différence de calculs distance "unités courbes" et distances "écran"
|
|
|
```
|
|
|
| code ci-dessus, basé sur mouseclick | code avec 2 calculs de distance et annotations, utilisant Picker (voir ci-dessous)|
|
|
|
|
|
|
Le code "fait le travail", vous pouvez tester et essayer avec d'autres jeux de données. Vous pourrez trouvez des données pour lesquels le calcul des distances proposé ici n'est pas satisfaisant car il n'identifie pas le point le plus proche "visuellement".
|
|
|
|
|
|
### Le tableau ci-dessous montre le résultat de l'identification du point le plus proche à partir des coordonnées de la souris ou de Picker, pour ce deuxième cas en montrant la différence de calculs distance "unités courbes" et distances "écran"
|
|
|
|
|
|
```plaintext
|
|
|
| code ci-dessus, basé sur MouseEvent | code basé sur Picker (voir ci-dessous)|
|
|
|
|-----------------|------------------------------|
|
|
|
| ![Figure initiale, 10points alignés](uploads/467300d845b9a2e75b1b8ecca19328d8/selection_point_mouseclick.png) | ![selection_picker2](uploads/36f24ebb707baac8a9af0c29c562d76c/selection_picker2.png) |
|
|
|
|<i> une étoile rouge matérialise le point identifié comme le plus proche du clic de souris </i>|résultats en distance "unités courbes" et distances "écran"|
|
|
|
|<i> une étoile rouge matérialise le point identifié comme le plus proche du clic de souris </i>|<i>résultats en distance "unités courbes" et distances "écran", avec affichage dans des annotations</i>|
|
|
|
|
|
|
### Avec une figure comportant 2 courbes ou plus.
|
|
|
|
|
|
Dans ce nouveau code, on va introduire deux difficultés (toutes relatives !):
|
|
|
- calculer la **distance aux points en "coordonnées écran"**, en normant les composantes de la distance par la largeur et la hauteur respectivement en coordonnées données, puis en dénormant par les largeur et la hauteur respectivement en "unité écran" (pixels ou unités proportionnelles). On donne la possibilité de passer d'une distance à l'autre en passant à l'argument ratio_w_sur_h soit le ratio w/h de la vignette ce qui conduira à un calcul en distances "écran", soit rien, ce qui conduira à un calcul en distances "données"
|
|
|
- répéter l'opération pour 2 courbes (ou plus) : le minimum de chacune est repérée par un carré et une annotation ; le plus proche des deux est repéré par une étoile inratable.
|
|
|
|
|
|
|
|
|
On a "factorisé" le code en définissant dans la fonction onclick_proche_2courbes une sous-fonction qui fera l'opération sur une courbe, son annotation associée et son "carré" fluo associé.
|
|
|
|
|
|
|
|
|
``` python
|
|
|
import matplotlib.pyplot as plt
|
|
|
import numpy as np
|
|
|
|
|
|
fig = None
|
|
|
adnotacja_ecran = None
|
|
|
adnotacja_ecran_2 = None
|
|
|
une_croix = None
|
|
|
carre_jaune=None
|
|
|
carre_orange=None
|
|
|
curseur = None
|
|
|
|
|
|
|
|
|
def onclick_proche_2courbes(event):
|
|
|
def operations_sur_une_courbe(courbe, annotation, carre, ratio_w_sur_h = None):
|
|
|
"""
|
|
|
# si on ne passe pas comme argument ratio_w_sur_h, sa valeur par défaut sera None : on raisonne en "distance données"
|
|
|
# pour ça il faut aussi REASSIGNER LOCALEMENT DANS CETTE SOUS-FONCTION 1 à ratio_w_sur_h, delta_x et delta_y
|
|
|
# sinon, on passe le ratio largeur/hauteur de la vignette et on raisonne bien en "distance écran"
|
|
|
"""
|
|
|
|
|
|
if ratio_w_sur_h is None:
|
|
|
# on raisonne en "distances en unité des données"
|
|
|
distances_ecran = [((x - x_souris) ** 2 + (y - y_souris)**2) ** (1 / 2) for (x, y) in zip(courbe.get_xdata(), courbe.get_ydata())]
|
|
|
else:
|
|
|
# on raisonne en "unités écran", donc on norme par w/h unité données et on dénorme par le ratio w/h de la vignette
|
|
|
distances_ecran = [((ratio_w_sur_h *(x - x_souris) / delta_x ) ** 2 +
|
|
|
((y - y_souris) / delta_y)**2) ** (1 / 2) for (x, y) in zip(courbe.get_xdata(), courbe.get_ydata())]
|
|
|
# print("liste des distances écran", distances_ecran)
|
|
|
idx_ecran = np.argmin(distances_ecran)
|
|
|
xpt, ypt = courbe.get_xdata()[idx_ecran], courbe.get_ydata()[idx_ecran] # tuple
|
|
|
distance_min = min(distances_ecran)
|
|
|
|
|
|
carre.set_data(xpt,ypt)
|
|
|
|
|
|
annotation.xy = (xpt, ypt)
|
|
|
annotation.set_visible(True)
|
|
|
|
|
|
return xpt, ypt, distance_min
|
|
|
|
|
|
|
|
|
x_souris, y_souris = event.xdata, event.ydata
|
|
|
curseur.set_data(x_souris, y_souris)
|
|
|
ax.set_title(f"détermination du point de la courbe le plus proche de {x_souris:.2f}, {y_souris:.2f}")
|
|
|
|
|
|
# unités écran
|
|
|
xdroite, xgauche = ax.get_xlim()
|
|
|
ybas, yhaut = ax.get_ylim()
|
|
|
delta_x = abs(xdroite - xgauche)
|
|
|
delta_y = abs(ybas - yhaut)
|
|
|
bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
|
|
|
ratio_w_sur_h = bbox.width / bbox.height # le graphique est "ratio_w_sur_h fois plus large que haut"
|
|
|
print("delta_x, delta_y, ratio_w_sur_h)", delta_x, delta_y, ratio_w_sur_h)
|
|
|
|
|
|
#print("type :", type(line_pk6700.get_data()), " => ", line_pk6700.get_data())
|
|
|
|
|
|
|
|
|
# première courbe
|
|
|
x1, y1, distance_min1 = operations_sur_une_courbe(line_pk6700, adnotacja_ecran, carre_jaune, ratio_w_sur_h)
|
|
|
adnotacja_ecran.set_text ( f"courbe Pk6700 : {x1:.2f}, {y1:.2f}")
|
|
|
adnotacja_ecran.set_position( (x1+100, y1+2.5))
|
|
|
adnotacja_ecran.set_color("blue")
|
|
|
|
|
|
|
|
|
# deuxième courbe
|
|
|
x2, y2, distance_min2 = operations_sur_une_courbe(line_decalee, adnotacja_ecran_2, carre_orange, ratio_w_sur_h)
|
|
|
adnotacja_ecran_2.set_text ( f"courbe Pk6700 : {x1:.2f}, {y1:.2f}")
|
|
|
adnotacja_ecran_2.set_position( (x1-250, y1+1.5))
|
|
|
adnotacja_ecran_2.set_color("green")
|
|
|
|
|
|
if distance_min2 < distance_min1 :
|
|
|
selection.set_data([x2], [y2])
|
|
|
selection.set_color("blue")
|
|
|
else:
|
|
|
selection.set_data([x1], [y1])
|
|
|
selection.set_color("green")
|
|
|
|
|
|
fig.canvas.draw_idle()
|
|
|
|
|
|
# CORPS DU PROGRAMME
|
|
|
|
|
|
fig,ax = plt.subplots()
|
|
|
ax.set_title("détermination du point de la courbe le plus proche")
|
|
|
x_sc = [-240.838,-80.39,-78.15,-34,-20.91,-17.27,0,4.51,6.76,8.76,14.76,42.76,46.76,57.76,74.76,78.62,83.32,86.85,104,136.22,143.85,262.877]
|
|
|
z_sc=[142.75,139,140,139.17,139.17,138.47,137.8,136.73,135.21,135.01,134.71,134.51,134.61,134.29,134.71,135.83,138.48,137.77,139.53,140.68,141.5,141.91]
|
|
|
|
|
|
carre_jaune, = ax.plot(x_sc[0], z_sc[0], 's', c='yellow',alpha=0.5, markersize =20)
|
|
|
line_pk6700, = ax.plot(x_sc, z_sc, '*', c='red', label="pk6700", ls='-')
|
|
|
|
|
|
x_2 = [x+25 for x in x_sc]
|
|
|
z_2 = [z+1 for z in z_sc]
|
|
|
|
|
|
minimum_des_x = min(min(x_sc), min(x_2))
|
|
|
minimum_des_y = min(min(z_sc), min(z_2))
|
|
|
|
|
|
curseur, = ax.plot(x_2[0], z_2[0], '+', c='blue', markersize =20)
|
|
|
carre_orange, = ax.plot(x_2[0], z_2[0], 's', c='orange',alpha=0.5, markersize =20)
|
|
|
line_decalee, = ax.plot(x_2, z_2, '^', c='sienna', label="pk6700 décalé", ls=":")
|
|
|
selection, = ax.plot(x_2[0], z_2[0], marker='*', c='red', markersize=20)
|
|
|
|
|
|
adnotacja_ecran = ax.annotate("X", xytext=(minimum_des_x,minimum_des_y), xy=(minimum_des_x, minimum_des_y), bbox = dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.75), arrowprops = dict(arrowstyle='->', connectionstyle='arc3,rad=0', color='grey'))
|
|
|
adnotacja_ecran.set_visible(False)
|
|
|
adnotacja_ecran_2 = ax.annotate("X", xytext=(minimum_des_x,minimum_des_y), xy=(minimum_des_x, minimum_des_y), bbox = dict(boxstyle='round,pad=0.5', fc='lightblue', alpha=0.75), arrowprops = dict(arrowstyle='->', connectionstyle='arc3,rad=0', color='grey'))
|
|
|
adnotacja_ecran_2.set_visible(False)
|
|
|
|
|
|
fig.canvas.mpl_connect('button_press_event', onclick_proche_2courbes)
|
|
|
plt.show()
|
|
|
|
|
|
```
|
|
|
|
|
|
## Avec un "PickEvent" :
|
|
|
|
... | ... | @@ -157,8 +278,6 @@ largeur_en_pixels, hauteur_en_pixels = bbox.width* fig.dpi , bbox.height * fig.d |
|
|
|
|
|
Dans ce code, on commence par tracer une courbe, en la nommant, et des objets supplémentaire comme des courbes réduites à un point et des annotations, toujours en les nommant, mais sans les afficher dans un premier temps grâce à set_visible(False). On rappelle que dans une fonction, on ne pourrait pas redéfinir ces objets (courbe = plt.plot(nouveaux_x, nouveaux_y)) mais par contre on peut parfaitement appliquer des méthodes sur des objets définis dans le programme principal, ou en changer des attributs : courbe = set_data(nouveaux_x, nouveaux_y). Ainsi, dans la fonction on_pick_cp on va pouvoir modifier certains arguments de ces objets. On peut donc déplacer la croix bleue vers les coordonnées de la souris, et déplacer également les annotations en mettant à jour leur texte, et bien sûr changer leur statut avec set_visible(True). On pourrait également modifier le titre de la figure, avec ax.set_text()
|
|
|
|
|
|
|
|
|
|
|
|
## Exemple 2: comprendre l'argument pickradius
|
|
|
|
|
|
Le "Picker" sélectionne plus ou moins de points "autour" du clic de souris, en fonction d'un argument correspondant à un rayon.
|
... | ... | @@ -223,3 +342,5 @@ def submit_radius(val): |
|
|
nuage, = ax.plot(x, y, '*', c='blue', label="nuage", picker=init_radius, ls='None')
|
|
|
textbox_radius.on_submit(submit_radius)
|
|
|
```
|
|
|
|
|
|
![selection_point_mouseclick_2courbes](uploads/5e59b3773c9d83f464047deb72e4d5ff/selection_point_mouseclick_2courbes.png) |
|
|
\ No newline at end of file |