Python Matplotlib MouseEvent xy vs. xydata - python

I am working on a project to make a mouse click interactive pixel map (created with pyplot) using Python 3.4 and Matplotlib 1.4.3. I am using the matplotlib.backend_bases.MouseEvent class to make my figure interactive. I'm just getting started, and so far I have:
# Define an on_click() function that will print event data upon mouseclick
def on_click(event):
"""Print event data on mouseclick"""
print(event)
When I click on the figure, here's the kind of output I get:
MPL MouseEvent: xy=(289,265) xydata=(24.5956632653,21.9489795918) button=1 dblclick=False inaxes=Axes(0.141923,0.1;0.603077x0.8)
Can anyone tell me what the xy part means? It's a 50x50 pixel map, so the xydata is the pixel position of the mouse click.

It is the position from left and bottom of canvas, respectively.
Here is the documentation:
the following attributes
x- x position - pixels from left of canvas
y- y position - pixels from bottom of canvas
canvas- the FigureCanvas instance generating the event

Related

Convert from plotter coordinates to world coordinates in PyVista

I am new to PyVista and vtk. I am implementing a mesh editing tool (Python=3.10, pyvista=0.37,vtk=9.1 ) When a user clicks all points within a given radius of the mouse cursor's world coordinates (e.g. projected point on the surface) should be selected. I have implemented this much through callbacks to mouse clicks using the pyvista plotters track_click_position method.
My problem is that I also want for a user to be able to preview the selection (highlight the vertices that will be selected) before they click. For this it is necessary to track the mouse location in world coordinates and to attach a callback function to the movement of the mouse that will highlight the relevant nodes.
The pyvista plotter's 'track_mouse_position' method doesn't support attaching callbacks but I figured out a work around for that. In the minimal example below I have managed to track changes to the mouse cursor location in pixels in the plotter's coordinate system. I am stuck now as to how to convert these into world coordinates. When the mouse hovers over the sphere these 'world coordinates' this should be the projected location on the sphere. When the mouse hovers off the sphere then it should return nothing or inf or some other useless value.
import pyvista as pv
def myCallback(src,evt):
C = src.GetEventPosition() # appears to be in pixels of the viewer
print(C)
# how to convert C into world coordinates on the sphere
sp = pv.Sphere()
p = pv.Plotter()
p.add_mesh(sp)
p.iren.add_observer("MouseMoveEvent",myCallback)
p.show()
Thank you very much for your help.
Harry
I figured this one out. They key was to use 'pick_mouse_position' after calling 'track_mouse_position'.
import pyvista as pv
def myCallback(src,evt):
out = p.pick_mouse_position()
print(out)
sp = pv.Sphere()
p = pv.Plotter()
p.add_mesh(sp)
p.track_mouse_position()
p.iren.add_observer("MouseMoveEvent",myCallback)
p.show()

Restricting mouse coordinates to matplotlib figure window instead of using the coordinates of the whole computer screen?

If there a way to restricting the mouse clicking event coordinates to just detect the coordinates into a matplotlb figure window instead of detecting the coordinates of the whole computer screen. You can see from the image that in my figure window it is at coordinates x=348, y= -149.2 but it prints the coordinates of the whole window screen at x=-1136 and y = 510.
I am using this
listener = mouse.Listener(on_click=onMouseClick)
listener.start() # start thread
and
cid = fig.canvas.mpl_connect('button', on_click)
You can add a check in your on_click function:
def on_click(event):
if event.inaxes:
# your current function body here

Matplotlib - remove RectangleSelector widget from the plot

I am creating a visual PyQt5 application which creates and shows Matplotlib plots. I want to be able to select some points on the plot with a mouse. I use RectangleSelector widget for it. The problem is, the drawn rectangle stays on plot after I release the mouse. Using interactive=False makes the rectangle invisible, but it is still there and breaks plot auto scale when I try to change the data to display on the plot. Here is the example with interactive=True :
After I load some other data to plot:
Is there any way to remove the RectangleSelector from the plot? Or at least to make ax.autoscale() ignore it?
You can use the "escape" key to clear the current shape. This can be redefined in the argument state_modifier_keys. For details see here.
A other way of doing it would be to this:
rectangle = RectangleSelector(...)
for artist in rectangle :
artist.set_visible(False)
rectangle.update()
this is what is done when the escape button is pressed and it will remove the current rectangle selector from your graph.
Not sure if you've resolved this yet.
I use this
def line_select_callback(self, eclick, erelease):
# At the beginning
toggle_selector.RS.set_active(False)
toggle_selector.RS.to_draw.set_visible(False)
toggle_selector.RS.update()
#....line_select_callback code....
# Last line of method/function
toggle_selector.RS.set_active(True)

Tkinter - Using multiple key-binds

I've made a program which draws an oval on click (mouse click=start point, mouse release= end point) as shown in code below, and I'd like to add if condition: when the shift key is pressed midst drawing, it would equalize the coordinates and therefore as a result, a circle (or perfect oval if you wish) will be drawn.
from tkinter import *
def draw(event):
if str(event.type)=='ButtonPress':
canvas.old_coords=event.x,event.y
elif str(event.type)=='ButtonRelease':
x,y=event.x,event.y
x1,y1=canvas.old_coords
canvas.create_oval(x,y,x1,y1)
canvas=Canvas()
canvas.pack()
canvas.bind('<B1-Motion>',draw)
canvas.bind('<ButtonPress-1>',draw)
canvas.bind('<ButtonRelease-1>',draw)
How could I possibly take in account pressed shift and then draw a circle?
So, I found a Python module called keyboard and I solved my problem using it, adding this condition:
if keyboard.is_pressed('shift'):
if y>y1: y=y1+abs(x-x1)
else: y=y1-abs(x-x1)
which changes the end coordinates and later draws circle accordingly

Pygame Surface Mechanics

I'm currently writing up some GUI code for a small project I'm working on and I've come to the point where I need to implement scroll bars and their associated containers. For ease of execution, I would love to be able to draw all elements within the "scroll box" (the window that the scroll bar will affect) to a separate surface from my main display surface. The separate surface would then be cropped as need be and then drawn to the display surface in the render loop. I'm having trouble getting this to work, however.
In the draw() method of my ScrollBox class, I have the following code.
def draw(self):
self.subSurface.blit(self.image, (x, y))
#subSurface is, naturally, a Surface, and image is a pygame.Image; x and y are whatever
self.displaySurface.blit(self.subSurface, (x,y))
As with all drawable GUI elements in my code, draw() is called every pass through the main render loop. What the above code gives me is the default filled-in black Rect and self.image is not displayed in any capacity. I tried replacing the first line with
pygame.draw.rect(self.subSurface, color, rect)
but it yielded the same results. From my reading up on other Pygame GUI libraries, it seems what I want to do is possible but I don't think I'm executing it properly. How do I attach other sources/surfaces to subSurface and then have subSurface be drawn (with the sources attached) by displaySurface?
Any help would be much appreciated. Thanks.
For people visiting this question in the future:
Remember that the dest argument for Surface.blit() is relative to the upper-left corner of the destination surface. So if you're assembling an image on a subsurface, remember to use coordinates relative to the top-left corner of the object you're assembling, rather than absolute display coordinates.
So to assemble a scrollbar and draw it somewhere:
class ScrollBar:
# ... code ...
def render(self, display, x, y):
self.subSurface.blit(self.handle_image, (0, self.handle_pos))
self.subSurface.blit(self.upbtn_image, (0, 0))
self.subSurface.blit(self.dnbtn_image, (0, self.height - self.btn_height))
# ... other rendering operations
display.blit(self.subSurface, (x, y))
Adjust all numbers and variable names to taste, but you get the idea. Notice that all the scrollbar elements are positioned in "scrollbar-local" coordinates, with only the final blit to the display surface positioned in screen/application coordinates.

Categories

Resources