Menu

[r3358]: / trunk / htdocs / examples / object_picker.py  Maximize  Restore  History

Download this file

299 lines (230 with data), 8.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
#!/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)<epsilon
for ax in self.figure.axes:
for line in ax.get_lines():
if over_line(line):
self.select_line(line)
return
text = ax.get_xticklabels()
text.extend( ax.get_yticklabels() )
for t in text:
if over_text(t):
self.select_text(t)
return
win = gtk.Window()
win.set_default_size(400,300)
win.set_name("Object Picker")
win.connect("destroy", lambda x: gtk.main_quit())
vbox = gtk.VBox()
win.add(vbox)
vbox.show()
fig = Figure(figsize=(5,4), dpi=100)
ax = fig.add_subplot(111)
t = arange(0.0,3.0,0.01)
s = sin(2*pi*t)
ax.plot(t,s)
ax.set_title('click on line or text')
canvas = PickerCanvas(fig)
canvas.show()
vbox.pack_start(canvas)
toolbar = NavigationToolbar(canvas, win)
toolbar.show()
vbox.pack_start(toolbar, False, False)
win.show()
gtk.main()
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.