/[ascend]/branches/adrian/pygtk/diagnose.py
ViewVC logotype

Contents of /branches/adrian/pygtk/diagnose.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3074 - (show annotations) (download) (as text)
Wed Aug 19 10:47:38 2015 UTC (2 years, 3 months ago) by adrian
File MIME type: text/x-python
File size: 16706 byte(s)
Fixed pixbuf issue in diagnose window
1 import array
2 from itertools import groupby
3 from operator import itemgetter
4 import math
5
6 import cairo
7 from gi.repository import GdkPixbuf, Gdk
8
9 import config
10 from infodialog import *
11 from preferences import *
12
13 ZOOM_RE = re.compile(r"([0-9]+)\s*%?")
14 MAX_ZOOM_SIZE = float(2000) # float
15 MAX_ZOOM_RATIO = float(16) # float
16 AT_BOUND_TOL = 0.0001
17
18 class DiagnoseWindow:
19 def __init__(self,browser,block=0):
20 self.browser=browser
21 self.browser.builder.add_objects_from_file(self.browser.glade_file, ["diagnosewin"])
22 self.browser.builder.connect_signals(self)
23 self.window = self.browser.builder.get_object("diagnosewin")
24 self.window.grab_focus()
25 self.window.set_transient_for(self.browser.window)
26
27 self.prefs = Preferences()
28
29 try:
30 _icon = Gtk.Image()
31 _iconpath = os.path.join(browser.assets_dir, 'diagnose' + config.ICON_EXTENSION)
32 print "ICON PATH =",_iconpath
33 _icon.set_from_file(_iconpath)
34 print "ICON = ",_icon
35 self.window.set_icon(_icon.get_pixbuf())
36 except:
37 pass
38
39 self.blockstatus = self.browser.builder.get_object("blockstatustext")
40
41 self.imagescroll = self.browser.builder.get_object("imagescroll")
42 self.image = self.browser.builder.get_object("image")
43 self.blockentry = self.browser.builder.get_object("blockentry")
44 self.zoomentry = self.browser.builder.get_object("zoomentry")
45
46 self.var = None; self.rel = None
47 self.varname = self.browser.builder.get_object("varname1")
48 self.varval = self.browser.builder.get_object("varval")
49 self.varinfobutton = self.browser.builder.get_object("varinfobutton")
50 self.relname = self.browser.builder.get_object("relname1")
51 self.relresid = self.browser.builder.get_object("relresid")
52 self.relinfobutton = self.browser.builder.get_object("relinfobutton")
53 self.preferred_units_check = self.browser.builder.get_object("preferred_units_check")
54 if self.prefs.getBoolPref("Diagnose","show_preferred_units")==True:
55 self.preferred_units_check.set_active(True)
56 else:
57 self.preferred_units_check.set_active(False)
58
59 self.varview = self.browser.builder.get_object("varview")
60 self.varbuf = Gtk.TextBuffer()
61 self.varview.set_buffer(self.varbuf)
62 self.varcollapsed = self.browser.builder.get_object("varcollapsed")
63 self.relview = self.browser.builder.get_object("relview")
64 self.relcollapsed = self.browser.builder.get_object("relcollapsed")
65 self.relvalues = self.browser.builder.get_object("relvalues")
66 self.rellabels = self.browser.builder.get_object("rellabels")
67 self.relrels = self.browser.builder.get_object("relrels")
68 self.relresids = self.browser.builder.get_object("relresids")
69 self.relbuf = Gtk.TextBuffer()
70 self.relview.set_buffer(self.relbuf)
71 self.im = None
72 self.block = 0
73 self.apply_prefs()
74
75 self.prepare_data()
76 self.fill_values(block) # block zero
77
78 def run(self):
79 self.window.run()
80 self.window.hide()
81
82 def apply_prefs(self):
83 vc = self.browser.prefs.getBoolPref("Diagnose","varcollapsed",True)
84
85 print "VARCOLLAPSED =",vc
86 self.varcollapsed.set_active(vc)
87 self.relcollapsed.set_active(self.browser.prefs.getBoolPref("Diagnose","relcollapsed",True))
88
89 def prepare_data(self):
90 # convert incidence map to pylab numarray type:
91 print "PREPARING DATA to be loaded"
92 self.im = self.browser.sim.getIncidenceMatrix()
93 self.data = self.im.getIncidenceData()
94 print "DATA LOADED"
95
96 self.zoom=1;
97
98 def fill_values(self, block):
99
100 try:
101 if self.im.getNumBlocks()==0:
102 print "NO BLOCKS!"
103 self.image.set_from_stock(Gtk.STOCK_DIALOG_ERROR
104 ,Gtk.IconSize.DIALOG
105 )
106 self.browser.reporter.reportError(
107 "Can't 'Diagnose blocks' until solver has been used."
108 )
109 return;
110 rl,cl,rh,ch = self.im.getBlockLocation(block)
111 except IndexError:
112 if block >= self.im.getNumBlocks():
113 block = self.im.getNumBlocks() - 1
114 rl,cl,rh,ch = self.im.getBlockLocation(block)
115 else:
116 print "BLOCK INDEX ERROR: block =",block
117 self.blockentry.set_text(str(self.block))
118 return
119 except RuntimeError,e:
120 print "ERROR GETTING BLOCK LOCATION:",str(e)
121 self.blockentry.set_text(str(self.block))
122 return
123
124 self.block = block
125 self.blockentry.set_text(str(block))
126
127 self.rl = rl
128 self.cl = cl
129 self.rh = rh
130 self.ch = ch
131
132 nr = int(rh-rl+1);
133 nc = int(ch-cl+1);
134
135 print "STARTING IMAGE CREATION"
136 # refer http://pyGtk.org/pygtk2tutorial/sec-DrawingMethods.html
137 c = chr(255)
138 b = nr * nc * 4 * [c]
139 rowstride = 4 * nc
140
141 blackdot = [chr(0)]*3;
142 reddot = [chr(255), chr(0), chr(0)]
143 pinkdot = [chr(255), chr(127), chr(127)]
144 skydot = [chr(127), chr(127), chr(255)]
145 bluedot = [chr(0), chr(0), chr(255)]
146 hotpinkdot = [chr(255), chr(47), chr(179)] # very big (+/-)
147 brightbluedot = [chr(71), chr(157), chr(255)] # very small (+/-)
148 greendot = [chr(87), chr(193), chr(70)] # close to 1
149 orangedot = [chr(255), chr(207), chr(61)] # 10-1000
150 bluegreendot = [chr(70), chr(221), chr(181)] # 0.001 - 0.1
151 for i in self.data:
152 if i.row < rl or i.row > rh or i.col < cl or i.col > ch:
153 continue
154 r = i.row - rl
155 c = i.col - cl
156 pos = rowstride * r + 4 * c
157 dot = blackdot
158 var = self.im.getVariable(i.col)
159 if abs( (var.getValue()-var.getUpperBound())/ var.getNominal() ) < AT_BOUND_TOL:
160 dot = reddot
161 elif abs( var.getValue() - var.getLowerBound() ) / var.getNominal() < AT_BOUND_TOL:
162 dot = reddot
163 else:
164 rat = var.getValue() / var.getNominal()
165 if rat!=0:
166 try:
167 val = abs(rat)
168 if abs(rat) > 1000:
169 dot = hotpinkdot
170 elif abs(rat) > 10:
171 dot = orangedot
172 elif abs(rat) < 0.001:
173 dot = brightbluedot
174 elif abs(rat) < 10 and abs(rat) > 0.1:
175 dot = greendot
176 elif abs(rat) > 0.001 and abs(rat) < 0.1:
177 dot = bluegreendot
178 else:
179 dot = blackdot
180 except ValueError, e:
181 pass
182 b[pos + 2], b[pos + 1], b[pos] = dot
183
184 d = ''.join(b)
185
186 print "DONE IMAGE CREATION"
187 buff = array.array('c', d)
188 surface = cairo.ImageSurface.create_for_data(buff, cairo.FORMAT_ARGB32, nc, nr)
189 self.pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height())
190
191 self.nr = nr
192 self.nc = nc
193 self.zoom = -1 # to fit, up to max 16x
194 self.do_zoom()
195
196 print "DONE IMAGE TRANSFER TO SERVER"
197
198 self.fill_var_names()
199 self.fill_rel_names()
200 self.fill_block_status()
201
202 self.fill_selection_info()
203
204 print "DONE FILL VALUES"
205
206 def fill_selection_info(self):
207 if self.var:
208 self.varname.set_text(self.var.getName())
209 default_units = self.var.getInstance().getType().getDimensions().getDefaultUnits().getName().toString()
210 pref_units = self.var.getInstance().getType().getPreferredUnits()
211 if pref_units and self.prefs.getBoolPref("Diagnose","show_preferred_units",True):
212 varval = str(self.var.getValue())+" "+pref_units.getName().toString()
213 else:
214 if default_units=="?":
215 varval = str(self.var.getValue())
216 else:
217 varval = str(self.var.getValue())+" "+default_units
218 self.varval.set_text(varval)
219 self.varinfobutton.set_sensitive(True)
220 else:
221 self.varname.set_text("")
222 self.varval.set_text("")
223 self.varinfobutton.set_sensitive(False)
224
225 if self.rel:
226 self.relname.set_text(self.rel.getName())
227 self.relresid.set_text(str(self.rel.getResidual()))
228 self.relinfobutton.set_sensitive(True)
229 else:
230 self.relname.set_text("")
231 self.relresid.set_text("")
232 self.relinfobutton.set_sensitive(False)
233
234 def do_zoom(self):
235 if self.zoom == -1:
236 w, h = self.imagescroll.get_size_request()
237 #print "SCALE TO FIX, w=%d, h=%d" % (w,h)
238 if self.nc/self.nr > w/h:
239 # a 'wide' image
240 self.zoom = float(w) / self.nc
241 else:
242 self.zoom = float(h) / self.nr
243
244 #self.browser.reporter.reportNote("Diagnose window: preliminary calculated zoom = %f (nr = %d, nc = %d)" % (self.zoom, self.nr, self.nc))
245
246
247 if self.zoom > MAX_ZOOM_RATIO:
248 self.zoom = MAX_ZOOM_RATIO
249
250 if self.zoom * self.nc > MAX_ZOOM_SIZE or self.zoom * self.nr > MAX_ZOOM_SIZE:
251 self.browser.reporter.reportNote("image is too big, reducing to MAX_ZOOM_SIZE = %f" % MAX_ZOOM_SIZE);
252 self.zoom = MAX_ZOOM_SIZE / max(self.nc,self.nr)
253
254 #self.browser.reporter.reportNote("Diagnose window: matrix zoom = %f" % self.zoom)
255 w = int(self.zoom * self.nc)
256 h = int(self.zoom * self.nr)
257
258 self.zoomentry.set_text("%d %%" % (int(self.zoom*100)) )
259
260 if self.zoom < 2:
261 pb1 = self.pixbuf.scale_simple(w,h,GdkPixbuf.InterpType.BILINEAR)
262 else:
263 pb1 = self.pixbuf.scale_simple(w,h,GdkPixbuf.InterpType.NEAREST)
264
265 self.image.set_from_pixbuf(pb1)
266
267 def fill_block_status(self):
268 print "FILL BLOCK STATUS"
269 s = self.im.getBlockStatus(self.block)
270 ss = "Failed"
271 if s == ascpy.IM_CONVERGED:
272 ss = "Converged"
273 elif s == ascpy.IM_NOT_YET_ATTEMPTED:
274 ss = "Not attempted yet"
275 elif s == ascpy.IM_OVER_TIME:
276 ss += " (time limit)"
277 elif s == ascpy.IM_OVER_ITER:
278 ss += " (iter limit)"
279 self.blockstatus.set_text(ss);
280
281
282 def fill_var_names(self):
283 print "FILL VAR NAMES"
284
285 names = [str(i) for i in self.im.getBlockVars(self.block)]
286
287 #print "NAMES:",names
288
289 if self.varcollapsed.get_active():
290 res = reduce(names)
291 rows = []
292 for k in res:
293 if k=="":
294 for r in res[k]:
295 rows.append(r)
296 else:
297 rows.append( '%s:\n\t%s' % (k, "\n\t".join(res[k])) )
298 text = "\n".join(rows)
299 else:
300 text = "\n".join(names)
301 self.varbuf.set_text(text)
302
303 print "DONE VAR NAMES"
304
305 def fill_rel_names(self):
306 print "REL NAMES"
307
308 rels = self.im.getBlockRels(self.block)
309
310 print "GOT RELS, NOW GETTING NAMES"
311
312 names = [str(i) for i in rels]
313
314 #print "NAMES =",names
315
316 if self.relcollapsed.get_active():
317 res = reduce(names)
318 rows = []
319 for k in res:
320 if k=="":
321 for r in res[k]:
322 rows.append(r)
323 else:
324 rows.append( '%s:\n\t%s' % (k, "\n\t".join(res[k])) )
325 text = "\n".join(rows)
326 else:
327 text = "\n".join(names)
328 self.relbuf.set_text(text)
329
330 print "DONE REL NAMES"
331
332 def set_block(self, block):
333 self.fill_values(block)
334
335 def set_zoom(self,zoom):
336 self.zoom = zoom
337 self.do_zoom()
338
339 def show_cursor(self,x,y):
340 c = self.cl + int(x/self.zoom)
341 r = self.rl + int(y / self.zoom)
342 if c > self.ch or r > self.rh:
343 #print "OUT OF RANGE"
344 return
345 self.var = self.im.getVariable(c)
346 self.rel = self.im.getRelation(r)
347 self.fill_selection_info()
348
349 # GUI EVENT HOOKS-----------------------------------------------------------
350
351 def on_diagnosewin_close(self,*args):
352 self.window.response(Gtk.ResponseType.CLOSE);
353
354 def on_preferred_units_toggle(self,widget):
355 _v = widget.get_active()
356 self.prefs.setBoolPref("Diagnose","show_preferred_units",_v)
357 self.fill_selection_info()
358
359 # incidence data view
360
361 def on_varcollapsed_toggled(self,*args):
362 vc = self.varcollapsed.get_active()
363 self.browser.prefs.setBoolPref("Diagnose","varcollapsed",vc)
364 if self.im:
365 self.fill_var_names()
366
367 def on_relcollapsed_toggled(self,*args):
368 rc = self.varcollapsed.get_active()
369 self.browser.prefs.setBoolPref("Diagnose","relcollapsed",rc)
370 if self.im:
371 self.fill_rel_names()
372
373 # detailed information about vars and rels (solver-side information!)
374
375 def on_varinfobutton_clicked(self,*args):
376 title = "Variable '%s'" % self.var
377 text = "%s\n%s\n" % (title,"(from the solver's view)")
378 units = " "
379 default_units = self.var.getInstance().getType().getDimensions().getDefaultUnits().getName().toString()
380 pref_units = self.var.getInstance().getType().getPreferredUnits()
381 if pref_units and self.prefs.getBoolPref("Diagnose","show_preferred_units",True):
382 units += pref_units.getName().toString()
383 else:
384 if default_units!="?":
385 units += default_units
386 _rows = {
387 "Value": self.var.getValue()
388 ,"Nominal": self.var.getNominal()
389 ,"Lower bound": self.var.getLowerBound()
390 ,"Upper bound": self.var.getUpperBound()
391 }
392 for k,v in _rows.iteritems():
393 text += "\n %s\t%s" % (k,value_human(v)+units)
394
395 text += "\n\nIncident with %d relations:" % self.var.getNumIncidentRelations()
396 for r in self.var.getIncidentRelations():
397 text += "\n %s" % r.getName()
398
399 _dialog = InfoDialog(self.browser,self.window,text,title,tabs=(150,300))
400 _dialog.run()
401
402 def on_relinfobutton_clicked(self,*args):
403 title = "Relation '%s'" % self.rel
404 text = "%s\n%s\n" % (title,"(from the solver's view)")
405 text += "\n %s\t%15f" % ("Residual", self.rel.getResidual())
406
407 text += "\n\nRelation expression:\n"
408 text += self.rel.getRelationAsString()
409
410 text += "\n\nIncident with %d variables:" % self.rel.getNumIncidentVariables()
411 for v in self.rel.getIncidentVariables():
412 units = " "
413 default_units = v.getInstance().getType().getDimensions().getDefaultUnits().getName().toString()
414 pref_units = v.getInstance().getType().getPreferredUnits()
415 if pref_units and self.prefs.getBoolPref("Diagnose","show_preferred_units",True):
416 units += pref_units.getName().toString()
417 else:
418 if default_units != "?" :
419 units += default_units
420 text += "\n %s\t= %s" % ( v.getName(),value_human(v.getValue())+units )
421
422 _dialog = InfoDialog(self.browser,self.window,text,title,tabs=(150,300))
423 _dialog.run()
424
425
426 # block navigation
427
428 def on_nextbutton_clicked(self,*args):
429 self.set_block(self.block + 1)
430
431 def on_prevbutton_clicked(self,*args):
432 self.set_block(self.block - 1)
433
434 def on_prevbigbutton_clicked(self,*args):
435 b = self.block - 1
436 while b >= 0:
437 rl,cl,rh,ch = self.im.getBlockLocation(b)
438 if rh-rl > 0 or ch-cl>0:
439 self.set_block(b)
440 return
441 b = b - 1
442 print "NO PRECEDING 'BIG' BLOCKS"
443
444 def on_nextbigbutton_clicked(self,*args):
445 b = self.block + 1
446 n = self.im.getNumBlocks()
447 while b < n:
448 rl,cl,rh,ch = self.im.getBlockLocation(b)
449 if rh-rl > 0 or ch-cl>0:
450 self.set_block(b)
451 return
452 b = b + 1
453 print "NO FOLLOWING 'BIG' BLOCKS"
454
455 def on_blockentry_key_press_event(self,widget,event):
456 keyname = Gdk.keyval_name(event.keyval)
457 print "KEY ",keyname
458 if keyname=="Return":
459 self.set_block( int(self.blockentry.get_text()) )
460
461 # zoom in and out
462
463 def on_zoominbutton_clicked(self,*args):
464 z = int( math.log(self.zoom)/math.log(2) )
465 z = pow(2,z + 1);
466 self.set_zoom(z)
467
468 def on_zoomoutbutton_clicked(self,*args):
469 z = int( math.log(self.zoom)/math.log(2) + 0.999)
470 z = pow(2,z - 1);
471 self.set_zoom(z)
472
473 def on_zoomentry_key_press_event(self,widget,event):
474 keyname = Gdk.keyval_name(event.keyval)
475 print "KEY ",keyname
476 if keyname=="Return":
477 t = self.zoomentry.get_text()
478 m = ZOOM_RE.match(t)
479 if not m:
480 self.zoomentry.set_text("%d %%" % int(self.zoom*100))
481 for mm in m:
482 print m
483 self.set_zoom( int(self.zoomentry.get_text()) )
484
485 # clicking in incidence matrix to get updated information at RHS
486
487 def on_imageevent_motion_notify_event(self,widget,event):
488 self.show_cursor(event.x, event.y)
489
490 def on_imageevent_button_press_event(self,widget,event):
491 self.show_cursor(event.x, event.y)
492
493
494 def value_human(v):
495 if v==0 or abs( math.log10(abs(v)) )<8:
496 return "%f" % v
497 return "%e" % v
498
499 #---------------------------------------
500 # Procedures to 'fold' a list of items from a hierarchy
501 # http://www.experts-exchange.com/Programming/Programming_Languages/Python/Q_21719649.html
502 # It's still buggy, I think
503
504 def fold(data):
505 """ fold sorted numeric sequence data into ranged representation:
506 >>> fold([1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28])
507 '[1,4-6,10,15-18,22,25-28]'
508 """
509 folded = []
510 for k, g in groupby(enumerate(sorted(data)), lambda (i,x):i-x):
511 seq = map(itemgetter(1), g)
512 if len(seq) > 1:
513 x = '%s-%s' % (seq[0], seq[-1])
514 else:
515 x = str(seq[0])
516 folded.append(x)
517 return folded and '[%s]' % ','.join(folded) or ''
518
519 def reduce(names):
520 """reduce a list of items nto something more readable:
521 >>> data = 'C.x C.p C.T C.delta[1] C.delta[2] C.delta[3] C.sat.x C.sat.p C.h C.delta[5]'.split()
522 >>> res = reduce(data)
523 >>> for k in sorted(res):
524 ... print '%s: %s' % (k, res[k])
525 C: T, delta[1-3,5], h, p, x
526 C.sat: p, x
527 """
528 data = sorted([n.split('.') for n in sorted(names)], key=len)
529 res = {}
530 for k, g in groupby(data, lambda x: len(x)):
531 if k == 1:
532 indexed = {}
533 seq = set([get(indexed, item) for item in g])
534 res['[global]'] = [ i+fold(indexed.get(i, [])) for i in sorted(seq) ]
535 else:
536 for key, g1 in groupby(g, lambda x: '.'.join(x[:-1])):
537 indexed = {}
538 seq = set(get(indexed, item) for item in g1)
539 res[key] = [ i+fold(indexed.get(i, [])) for i in sorted(seq) ]
540 return res
541
542 def get(indexed, item):
543 item = item[-1]
544 if item.endswith(']'):
545 item, idx = item[:-1].split('[')
546 indexed.setdefault(item, []).append(int(idx))
547 return item

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22