#!/usr/bin/env python """ As of matplotlib-0.70, there is GUI neutral object picking. See for example the picker_demo.py. Show how to use the mouse to select objects and a build dialog to set line properties. The approach here can be readily extended to include all artists and properties. Volunteers welcome! """ from __future__ import division from matplotlib.numerix import sin, pi, arange, absolute, sqrt from matplotlib.numerix.mlab import amin, amax import matplotlib matplotlib.use('GTKAgg') from matplotlib.backends.backend_gtk import NavigationToolbar, \ error_msg_gtk from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas from matplotlib.figure import Figure from matplotlib.lines import Line2D, lineStyles, lineMarkers from matplotlib.transforms import Bbox, lbwh_to_bbox from matplotlib.patches import draw_bbox from matplotlib.cbook import is_string_like from matplotlib.colors import colorConverter import gtk def get_color(rgb): def rgb_to_gdk_color(rgb): r,g,b = rgb color = gtk.gdk.Color(int(r*65535), int(g*65535), int(b*65535)) return color def gdk_color_to_rgb(color): return color.red/65535.0, color.green/65535.0, color.blue/65535.0 dialog = gtk.ColorSelectionDialog('Choose color') colorsel = dialog.colorsel color = rgb_to_gdk_color(rgb) colorsel.set_previous_color(color) colorsel.set_current_color(color) colorsel.set_has_palette(True) response = dialog.run() if response == gtk.RESPONSE_OK: rgb = gdk_color_to_rgb(colorsel.get_current_color()) else: rgb = None dialog.destroy() return rgb def make_option_menu( names, func=None ): """ Make an option menu with list of names in names. Return value is a optMenu, itemDict tuple, where optMenu is the option menu and itemDict is a dictionary mapping menu items to labels. Eg optmenu, menud = make_option_menu( ('Bill', 'Ted', 'Fred') ) ...set up dialog ... if response==gtk.RESPONSE_OK: item = optmenu.get_menu().get_active() print menud[item] # this is the selected name if func is not None, call func with label when selected """ optmenu = gtk.OptionMenu() optmenu.show() menu = gtk.Menu() menu.show() d = {} for label in names: if not is_string_like(label): continue item = gtk.MenuItem(label) menu.append(item) item.show() d[item] = label if func is not None: item.connect("activate", func, label) optmenu.set_menu(menu) return optmenu, d class LineDialog(gtk.Dialog): def __init__(self, line, fig): gtk.Dialog.__init__(self, 'Line Properties') self.fig = fig self.line = line table = gtk.Table(3,2) table.show() table.set_row_spacings(4) table.set_col_spacings(4) table.set_homogeneous(True) self.vbox.pack_start(table, True, True) row = 0 label = gtk.Label('linewidth') label.show() entry = gtk.Entry() entry.show() entry.set_text(str(line.get_linewidth())) self.entryLineWidth = entry table.attach(label, 0, 1, row, row+1, xoptions=False, yoptions=False) table.attach(entry, 1, 2, row, row+1, xoptions=True, yoptions=False) row += 1 self.rgbLine = colorConverter.to_rgb(self.line.get_color()) def set_color(button): rgb = get_color(self.rgbLine) if rgb is not None: self.rgbLine = rgb label = gtk.Label('color') label.show() button = gtk.Button(stock=gtk.STOCK_SELECT_COLOR) button.show() button.connect('clicked', set_color) table.attach(label, 0, 1, row, row+1, xoptions=False, yoptions=False) table.attach(button, 1, 2, row, row+1, xoptions=True, yoptions=False) row += 1 ## line styles label = gtk.Label('linestyle') label.show() thisStyle = line.get_linestyle() styles = [thisStyle] for key in lineStyles.keys(): if key == thisStyle: continue styles.append(key) self.menuLineStyle, self.menuLineStyleItemd = make_option_menu(styles) table.attach(label, 0, 1, row, row+1, xoptions=False, yoptions=False) table.attach(self.menuLineStyle, 1, 2, row, row+1, xoptions=True, yoptions=False) row += 1 ## marker label = gtk.Label('marker') label.show() keys = lineMarkers.keys() keys.append('None') marker = line.get_marker() if marker is None: marker = 'None' styles = [marker] for key in keys: if key == marker: continue styles.append(key) self.menuMarker, self.menuMarkerItemd = make_option_menu(styles) table.attach(label, 0, 1, row, row+1, xoptions=False, yoptions=False) table.attach(self.menuMarker, 1, 2, row, row+1, xoptions=True, yoptions=False) row += 1 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY) self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) def update_line(self): s = self.entryLineWidth.get_text() try: lw = float(s) except ValueError: error_msg_gtk('Line width must be a float. You entered %s' % s) else: self.line.set_linewidth(lw) item = self.menuLineStyle.get_menu().get_active() style = self.menuLineStyleItemd[item] self.line.set_linestyle(style) item = self.menuMarker.get_menu().get_active() style = self.menuMarkerItemd[item] if style is 'None': style = None self.line.set_marker(style) self.line.set_color(self.rgbLine) self.fig.draw() def run(self): while 1: response = gtk.Dialog.run(self) if response==gtk.RESPONSE_APPLY: self.update_line() elif response==gtk.RESPONSE_OK: self.update_line() break elif response==gtk.RESPONSE_CANCEL: break self.destroy() class PickerCanvas(FigureCanvas): def button_press_event(self, widget, event): width = self.figure.bbox.width() height = self.figure.bbox.height() self.pick(event.x, height-event.y) def select_line(self, line): dlg = LineDialog(line, self) dlg.show() dlg.run() def select_text(self, text): print 'select text', text.get_text() def pick(self, x, y, epsilon=5): """ Return the artist at location x,y with an error tolerance epsilon (in pixels) """ clickBBox = lbwh_to_bbox(x-epsilon/2, y-epsilon/2, epsilon, epsilon) draw_bbox(clickBBox, self.renderer) def over_text(t): bbox = t.get_window_extent(self.renderer) return clickBBox.overlaps(bbox) def over_line(line): # can't use the line bbox because it covers the entire extent # of the line trans = line.get_transform() xdata, ydata = trans.numerix_x_y(line.get_xdata(valid_only = True), line.get_ydata(valid_only = True)) distances = sqrt((x-xdata)**2 + (y-ydata)**2) return amin(distances)