1

I have an interactive plot that listens for certain keypresses and clicks but I want the user to be able to add a comment. I know that artist events don't typically allow this (they're listening for individual presses! but can I have matplotlib open a new window that has a small "insert comment" area? Ideally the window exits and returns to the main (original) window when the user hits return.

import numpy as np
import matplotlib.pyplot as plt

def onpick(event):
    ''' '''
    if event.mouseevent.button == 1: #only want lmb clicks
        selection = event.artist
        xdata = selection.get_xdata()
        ydata = selection.get_ydata()
        ind = event.ind
        point = tuple(zip(xdata[ind], ydata[ind]))
        xclick,yclick = point[0] 

        print('[x,y]=',xclick,yclick)   

def on_key(event):
    ''' 
        Handles predefined key-press events 
    ''' 
    print('Key press:\'%s\'' %(event.key))
    if event.key == ' ': #spacebar
        print 'Space'
        #do a thing
    if event.key == 'e': 
        print 'eeeeee'
        #do another thing
    if event.key == 'C':
        print 'How do make a comment. ...'
        comment = 'Whatever the user entered'
        return comment

        # when done return

fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, facecolor='#FFFFCC')

x, y = 4*(np.random.rand(2, 100) - .5)
ax.plot(x, y, 'o', picker = 6)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)

keyID = fig.canvas.mpl_connect('key_press_event', on_key)
clickID = fig.canvas.mpl_connect('pick_event', onpick)

plt.show()
FriskyGrub
  • 979
  • 3
  • 14
  • 25

1 Answers1

1

Matplotlib will soon has now introduced a TextBox Widget. See it's usage in this example.

Alternatively, you can use Tkinter's tkSimpleDialog to ask the user for a comment.

w = tkSimpleDialog.askstring("Title", "Please type comment")

You may then annotate the last picked point with the comment.

Complete example (which runs in python 2.7):

import numpy as np
import matplotlib.pyplot as plt
import Tkinter, tkSimpleDialog

xy = [(0,0)]

def onpick(event):
    ''' '''
    if event.mouseevent.button == 1: #only want lmb clicks
        selection = event.artist
        xdata = selection.get_xdata()
        ydata = selection.get_ydata()
        ind = event.ind
        point = tuple(zip(xdata[ind], ydata[ind]))
        xclick,yclick = point[0] 
        xy[0] = (xclick,yclick)
        print('[x,y]=',xclick,yclick)   

def on_key(event):
    print('Key press:\'%s\'' %(event.key))
    if event.key == 'c':
        root = Tkinter.Tk()
        root.withdraw()
        w = tkSimpleDialog.askstring("Title", "Please type comment")
        if w != None:
            ax.annotate(w, xy=xy[0], xytext=(20,-20), 
                    arrowprops=dict(facecolor='black', width=2, headwidth=6),
                    textcoords='offset points')
            ax.figure.canvas.draw_idle()

fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, facecolor='#FFFFCC')

x, y = 4*(np.random.rand(2, 100) - .5)
ax.plot(x, y, 'o', picker = 6)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)

keyID = fig.canvas.mpl_connect('key_press_event', on_key)
clickID = fig.canvas.mpl_connect('pick_event', onpick)

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks, this is exactly what I had visualised! – FriskyGrub May 16 '17 at 02:39
  • Hmm, seems to be getting a few strange errors `Python[50938:397346] -[NSApplication _setup:]: unrecognized selector sent to instance 0x7f836c97e400` ... the internet seems to be of the opinion that Tkinter isobsolete, I'll use your solution though and modify it to work with [gtkPython](http://python-gtk-3-tutorial.readthedocs.io/en/latest/entry.html) – FriskyGrub May 16 '17 at 02:57
  • Tkinter is not obsolete. It's the standard GUI interface for python 2.x. In python 3.x you need tkinter instead. Of course there are alternatives available like PyQt, GTK, wx, etc, but T(t)kinter has the advantage that it already ships with python. Concerning the error, one would need to have a complete problem description to help you. – ImportanceOfBeingErnest May 16 '17 at 08:37
  • You're right, looks like pyqt and gtk aren't ideal since i'll have colleagues using this software who may not want to screw around with the installs. Should I ask a new question for the error then, or are you happy to give some insight into [this mess](http://dumptext.com/juw4IIth)? That is running the exact code you have given above from a file (jnk.py). – FriskyGrub May 19 '17 at 03:20
  • So the above was fixed by using the TKagg backend instead of osx (pretty typical). However, the script now runs but while your annotation works it doesn't return keyboard input back to the event listener. That is, only one annotation can be made, and any other `'key_press_event'`s aren't registered. – FriskyGrub May 19 '17 at 04:10
  • Hard to say what's going wrong, since it's working as expected for me. And I don't have macOS to test. Could it be that you're loosing the focus of the window, such that key boad events are not within the plotting window after the comments dialog is closed? – ImportanceOfBeingErnest May 19 '17 at 07:10
  • Damn, I thought it might be an OS thing. I imagine that losing the focus is likely the problem, I had that issue with these key press events testing my software on a friends anaconda distro. Unfortunately, the symptom of the characters appearing intthe terminal input isn't present here. Thanks for confirming it's working as intended for you, and for your solution above. I'll have a go at working this out! :) – FriskyGrub May 19 '17 at 07:17