Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multitouch touchscreen support #8041

Closed
wants to merge 1 commit into from

Conversation

GregDMeyer
Copy link

@GregDMeyer GregDMeyer commented Feb 7, 2017

These changes add support for intuitive touchscreen interaction with Matplotlib, specifically touch-drag to pan and two-finger pinch to zoom.

When dragging, the plot is panned so that the same point in data coords stays under one's finger. When zooming, the plot is zoomed and panned simultaneously such that the same two points in data coords stay under the two touch points (except in cases such as holding down the 'x' key, locking motion to the x axis). These behaviors are meant to mimic what one would imagine physically sliding and/or stretching something.

Touch support is similarly added for the Axes3D class, such that a drag rotates the axes, and two-finger pinch scales the plot (zooms).

I only set up the Qt4 and Qt5 backends to send touch events to the figures, mostly because I don't know enough about gtk, javascript, etc to do it for the other backends. But it shouldn't be that hard for people who know those things, because NavigationToolbar2 does the heavy lifting. All you have to do is send it the touches' coordinates and a unique ID for each touch.

Summary of changes:

  • NavigationToolbar2 in backend_bases.py has new functions for handling touches, implementing touch-drag to pan and two-finger pinch to zoom.
  • New classes Touch and TouchEvent to store touch event data.
  • FigureCanvasBase has new functions to trigger the callbacks for touch events.
  • matplotlibrc.template and the other rc files are set up for the flag backends.touch, which defines whether to accept touch input. If it is false, Qt turns touches into regular mouse events.
  • Qt4 and Qt5 backends are set up to accept touch events and call the FigureCanvasBase functions
  • Axes3D provides callbacks for the touch functions.

I tested on Python 2.7 and 3.5, with PyQt4 and PySide (both on python 2.7) and PyQt5 (python 2.7 and 3.5), on a Dell XPS 13 running Ubuntu 16.04.

I mentioned that I was working on this in #7789.

@QuLogic QuLogic added the GUI: Qt label Feb 7, 2017
@tacaswell tacaswell added this to the 2.1 (next point release) milestone Feb 7, 2017
@GregDMeyer
Copy link
Author

just realized I have some misses on the pep8 compliance--will fix and amend my commit.

 - support for touch-to-drag and pinch-to-zoom in NavigationToolbar2
 - pass touchscreen events from Qt4 and Qt5 backends
 - add option in matplotlibrc to pass touches as mouse events if desired
@GregDMeyer
Copy link
Author

fixed the pep8 issues and it should be good to go! (I think the appveyor tests failed due to some issue on their side... it seems there was a problem at runtime?)

Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I don't have a touch screen, so I can't really say whether this does or does not work. It looks sane, anyway.


"""
x = None # x position - pixels from left of canvas
y = None # y position - pixels from right of canvas
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right -> bottom

"""
x = None # x position - pixels from left of canvas
y = None # y position - pixels from right of canvas
inaxes = None # the Axes instance if mouse us over axes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

us -> is (but there's no mouse in this event, is there?)


def __init__(self, name, canvas, x, y, ID, guiEvent=None):
"""
x, y in figure coords, 0,0 = bottom, left
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bottom, left -> left, bottom

'touch_update_event',
'touch_end_event')

In addition to the :class:`Event` and :class:`LocationEvent`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't derive from LocationEvent?


touches : None, or list
A list of the touches (possibly several), which will be of class Touch.
They are passed to the class as a list of triples of the form (ID,x,y),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How they are passed to the class has nothing to do with how the attribute is accessed. This would be something to go in docs for __init__, maybe.

elif twiny and not twinx:
a._set_view((xlims[0], xlims[1], ymin, ymax))
elif not twinx and not twiny:
a._set_view(list(xlims)+list(ylims))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is lims.T.flatten().

orig_IDs = {t.ID for t in self._touches}
e_IDs = {t.ID for t in event.touches}
if (len(event.touches) != len(self._touches) or
not orig_IDs == e_IDs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orig_IDs != e_IDs

@@ -66,6 +66,12 @@ backend : $TEMPLATE_BACKEND
# you if backend_fallback is True
#backend_fallback: True

# if you are using Qt4 or Qt5 backend and have a touchscreen or touchpad,
# you can interact with the plot using touch gestures (pinch to zoom,
# touch and drag). if this option is set to false, touch events will
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capitalize 'If' on both sentences.

if event.type() in TOUCH_EVENTS:
etype = TOUCH_EVENTS[event.type()]

touches = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These above two lines can go after the quick-return below.

orig_IDs = {t.ID for t in self._touches}
e_IDs = {t.ID for t in event.touches}
if (len(event.touches) != len(self._touches) or
not orig_IDs == e_IDs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not orig_IDs == eIDs -> orig_IDs != eIDs

@GregDMeyer
Copy link
Author

GregDMeyer commented Feb 14, 2017

Thanks for the comments!

Re: testing, yes, I thought it might be an obstacle that people don't have an easy way to test it. a couple ideas, if it isn't too much of a hassle to set up:

  1. VNC from a smartphone or tablet can support passing multitouch events, I believe. (this maybe would be a neat application for this functionality, too, so that if you own a tablet you could hand someone a plot to interact with. I don't have such a device so I don't know how well that would work).
  2. There seem to be some ways to simulate a touchscreen, e.g. this.

Will go through your comments when I have a chance.

Oh, and it shouldn't be too hard to write some unit tests at least for the general stuff in backend_bases.py etc. I can work on that.

cheers

@tacaswell tacaswell modified the milestones: 2.1 (next point release), 2.2 (next next feature release) Aug 29, 2017
@thomasaarholt
Copy link

As a heads-up, over at hyperspy/hyperspy-demos#44, we've just realised that using Binder (or public jupyter servers) we can now access jupyter notebooks on our phones. This provides another avenue for testing and especially using multitouch!

I'd love to see this implemented and expanded on!

@tacaswell tacaswell modified the milestones: needs sorting, v3.0 Feb 28, 2018
@tacaswell tacaswell modified the milestones: v3.0, v3.1 Jul 7, 2018
@jklymak jklymak modified the milestones: v3.1.0, v3.2.0, unassigned Feb 7, 2019
@jklymak
Copy link
Member

jklymak commented Jul 16, 2020

@GregDMeyer were you planning to come back to this? The rebase has gotten large, but maybe its straight forward. Is there a reason you didn't follow up because this was apparently significant work.

In general this would probably be good to get in. I would bet 50/50 that macs will have touchscreens in the next few months. And some PCs have touch.

@GregDMeyer
Copy link
Author

Unfortunately what happened was that I started grad school and lost track of most of my side projects ;) But I certainly think this would still be a valuable feature, and I'm happy to take another look in the next couple weeks to fix the pull request. I do have a feeling that fixing it up probably won't be that much work. (Also, I have a lot more experience with Python now, so I will definitely want to clean a few things up from my original code).

@story645
Copy link
Member

My laptop is multi-touch & I'd be happy for the feature/to test it.

@jklymak
Copy link
Member

jklymak commented Jul 21, 2020

@story645 is it OK to assign you to keep an eye on this? If not, please feel free to unassigned yourself!

@jklymak jklymak removed this from the unassigned milestone Jul 21, 2020
@jklymak jklymak added this to the v3.4.0 milestone Jul 21, 2020
@timhoffm
Copy link
Member

I can test touch screen support as well.

@timhoffm timhoffm self-assigned this Jul 21, 2020
@jklymak jklymak marked this pull request as draft September 8, 2020 01:59
@QuLogic QuLogic modified the milestones: v3.4.0, unassigned Jan 21, 2021
@jklymak
Copy link
Member

jklymak commented Jun 14, 2021

Closing as abandoned. Thanks for the PR, and sorry it didn't get in. If you do come back to this, please feel free to request this be re-opened, or open a fresh PR!

@jklymak jklymak closed this Jun 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants