1 |
import pygtk |
2 |
pygtk.require('2.0') |
3 |
import gtk |
4 |
import gtk.glade |
5 |
import pango |
6 |
|
7 |
OBSERVER_INITIAL_COLS = 3 # how many cells are at the start of the table? |
8 |
OBSERVER_ICON, OBSERVER_WEIGHT, OBSERVER_EDIT = range(0,OBSERVER_INITIAL_COLS) # column indices for the start of the TreeStore |
9 |
OBSERVER_NULL = 0 # value that gets added to empty cells in a new column |
10 |
|
11 |
# This is messy code since it doesn't observe the convention of keeping your model |
12 |
# separate from your view. It's all mixed up together. Yuck. Part of the |
13 |
# difficulty with that was the fact that TreeStores don't support the adding of |
14 |
# columns. |
15 |
|
16 |
# Update: there is a technique for doing this, in fact: |
17 |
# http://www.daa.com.au/pipermail/pygtk/2006-February/011777.html |
18 |
|
19 |
OBSERVER_NUM=0 |
20 |
|
21 |
class ObserverColumn: |
22 |
""" |
23 |
A class to identify the instance that relates to a specify column |
24 |
and the units of measurement and column title, etc. |
25 |
""" |
26 |
def __init__(self,instance,index,name=None,units=None,browser=None): |
27 |
self.instance = instance |
28 |
self.name = name |
29 |
self.index = index |
30 |
|
31 |
if name==None: |
32 |
if browser == None: |
33 |
name = "UNNAMED" |
34 |
else: |
35 |
name = browser.sim.getInstanceName(instance) |
36 |
|
37 |
if units == None: |
38 |
units = instance.getType().getPreferredUnits() |
39 |
if units == None: |
40 |
units = instance.getType().getDimensions().getDefaultUnits() |
41 |
|
42 |
uname = str(units.getName()) |
43 |
if uname.find("/")!=-1: |
44 |
uname = "["+uname+"]" |
45 |
|
46 |
if uname == "": |
47 |
_title = "%s" % (name) |
48 |
else: |
49 |
_title = "%s / %s" % (name, uname) |
50 |
|
51 |
self.title = _title |
52 |
self.units = units |
53 |
self.uname = uname |
54 |
self.name = name |
55 |
|
56 |
def __repr__(self): |
57 |
return "ObserverColumn(name="+self.name+")" |
58 |
|
59 |
def cellvalue(self, column, cell, model, iter): |
60 |
print "RENDERING COLUMN",self.index |
61 |
_rowobject = model.get_value(iter,0) |
62 |
|
63 |
try: |
64 |
if _rowobject.active: |
65 |
_rawval = self.instance.getRealValue() |
66 |
else: |
67 |
_rawval = _rowobject.values[self.index] |
68 |
_dataval = _rawval / self.units.getConversion() |
69 |
except KeyError: |
70 |
_dataval = "N/A" |
71 |
|
72 |
cell.set_property('text', _dataval) |
73 |
|
74 |
class ObserverRow: |
75 |
""" |
76 |
Just a container for a vector of values, but with columns that |
77 |
should correspond to those in the Observer object's vector of |
78 |
ObserverColumn objects. |
79 |
""" |
80 |
def __init__(self,values=None,active=False): |
81 |
if values==None: |
82 |
values={} |
83 |
|
84 |
self.values = values |
85 |
self.active = active |
86 |
|
87 |
def make_static(self,table): |
88 |
self.active = False |
89 |
print "TABLE COLS:",table.cols |
90 |
print "ROW VALUES:",self.values |
91 |
for index,col in table.cols.iteritems(): |
92 |
print "INDEX: ",index,"; COL: ",col |
93 |
self.values[index] = col.instance.getRealValue() |
94 |
print "Made static, values:",self.values |
95 |
|
96 |
class ObserverTab: |
97 |
|
98 |
def __init__(self,xml,browser,tab,name=None,alive=True): |
99 |
global OBSERVER_NUM |
100 |
self.colindex = 0 |
101 |
if name==None: |
102 |
OBSERVER_NUM=OBSERVER_NUM+1 |
103 |
name = "Observer %d" % OBSERVER_NUM |
104 |
self.name = name |
105 |
self.browser=browser |
106 |
xml.signal_autoconnect(self) |
107 |
self.view = xml.get_widget('observerview') |
108 |
self.tab = tab |
109 |
self.alive=alive |
110 |
if self.alive: |
111 |
self.browser.reporter.reportNote("New observer is 'alive'") |
112 |
|
113 |
self.keptimg = gtk.Image() |
114 |
self.activeimg = gtk.Image() |
115 |
self.activeimg.set_from_file("glade/active.png") |
116 |
# create PixBuf objects from these? |
117 |
self.rows = [] |
118 |
_store = gtk.TreeStore(object,int) |
119 |
self.cols = {} |
120 |
|
121 |
# create the 'active' pixbuf column |
122 |
_renderer = gtk.CellRendererPixbuf() |
123 |
_col = gtk.TreeViewColumn() |
124 |
_col.set_title("") |
125 |
_col.pack_start(_renderer,False) |
126 |
_col.set_cell_data_func(_renderer, self.activepixbufvalue) |
127 |
self.view.append_column(_col); |
128 |
|
129 |
# initially there will not be any other columns |
130 |
|
131 |
if self.alive: |
132 |
# for a 'live' Observer, create the 'active' bottom row |
133 |
self.browser.reporter.reportNote("Adding empty row to store") |
134 |
self.activeiter = _store.append(None, [ObserverRow(active=True),0] ) |
135 |
|
136 |
self.view.set_model(_store) |
137 |
self.browser.reporter.reportNote("Created observer '%s'" % self.name) |
138 |
|
139 |
def activepixbufvalue(self,column,cell,model,iter): |
140 |
_rowobject = model.get_value(iter,0) |
141 |
if _rowobject.active: |
142 |
cell.set_property('pixbuf',self.activeimg.get_pixbuf()) |
143 |
else: |
144 |
cell.set_property('pixbuf',self.keptimg.get_pixbuf()) |
145 |
|
146 |
def on_add_clicked(self,*args): |
147 |
self.do_add_row() |
148 |
|
149 |
def on_clear_clicked(self,*args): |
150 |
self.browser.reporter.reportError("CLEAR not implemented") |
151 |
|
152 |
def do_add_row(self): |
153 |
if self.alive: |
154 |
_rowobject = ObserverRow(active=True) |
155 |
_store = self.view.get_model() |
156 |
_oldrow = _store.get_value(self.activeiter,0) |
157 |
_oldrow.make_static(self) |
158 |
self.activeiter = _store.append(None,[ObserverRow([],True),0]) |
159 |
else: |
160 |
self.browser.reporter.reportError("Can't add row: incorrect observer type") |
161 |
|
162 |
def on_view_cell_edited(self, renderer, path, newtext, datacolumn): |
163 |
self.browser.reporter.reportError("EDIT not implemented") |
164 |
|
165 |
def sync(self): |
166 |
_store = self.view.get_model() |
167 |
_activerow = _store.get_value(self.activeiter,0) |
168 |
_store.set(self.activeiter,1,0 ) |
169 |
self.browser.reporter.reportNote("SYNC performed") |
170 |
|
171 |
def add_instance(self,instance): |
172 |
_col = ObserverColumn(instance,self.colindex,browser=self.browser) |
173 |
self.cols[self.colindex] = _col |
174 |
self.colindex = self.colindex + 1 |
175 |
|
176 |
# create a new column |
177 |
_renderer = gtk.CellRendererText() |
178 |
_tvcol = gtk.TreeViewColumn() |
179 |
_tvcol.set_title(_col.title) |
180 |
_tvcol.pack_start(_renderer,False) |
181 |
_tvcol.set_cell_data_func(_renderer, _col.cellvalue) |
182 |
self.view.append_column(_tvcol); |
183 |
|
184 |
self.browser.reporter.reportError("cols = "+str(self.cols)) |
185 |
|
186 |
#------------------------------------------------------------------------------- |
187 |
# OLD STUFF |
188 |
|
189 |
class ObserverTab1: |
190 |
""" |
191 |
An 'Observer' tab in the Browser interface. Multiple tabs should be |
192 |
possible. |
193 |
""" |
194 |
def __init__(self,xml,name,browser,tab): |
195 |
xml.signal_autoconnect(self); |
196 |
|
197 |
self.view = xml.get_widget('observerview') |
198 |
self.tab = tab |
199 |
|
200 |
self.activeimg = None |
201 |
self.keptimg = None |
202 |
|
203 |
# no instances yet in the observer: |
204 |
self.columninstances = [] |
205 |
|
206 |
self.columns = [gtk.gdk.Pixbuf,int,bool] |
207 |
|
208 |
# units for each data column |
209 |
self.units = [] |
210 |
self.titles = [] |
211 |
|
212 |
_store = gtk.TreeStore(*self.columns) |
213 |
self.rows = [] |
214 |
|
215 |
# add an empty first row |
216 |
self.rows.append([]) |
217 |
|
218 |
# work towards having multiple observers for multiple simulations |
219 |
self.name = nameself.view.set_model(_store) |
220 |
self.browser = browser |
221 |
|
222 |
# create the 'active' pixbuf columns |
223 |
_renderer = gtk.CellRendererPixbuf() |
224 |
_col = gtk.TreeViewColumn() |
225 |
_col.set_title("") |
226 |
_col.pack_start(_renderer,False) |
227 |
_col.add_attribute(_renderer, 'pixbuf', OBSERVER_ICON) |
228 |
self.view.append_column(_col); |
229 |
|
230 |
# create the first row |
231 |
print "Adding row",self.rows[0],"to store" |
232 |
_store.append(None, self.make_row(True, self.rows[0]) ) |
233 |
|
234 |
self.activerow = 0 |
235 |
|
236 |
self.view.set_model(_store) |
237 |
|
238 |
self.browser.reporter.reportNote("Created observer '%s'" % self.name) |
239 |
|
240 |
def on_add_clicked(self,*args): |
241 |
self.do_add_row() |
242 |
|
243 |
def do_add_row(self): |
244 |
_rownum = len(self.rows) |
245 |
|
246 |
# add a copy of the last row |
247 |
self.rows.append(self.rows[_rownum-1]) |
248 |
self.activerow = _rownum |
249 |
|
250 |
_m = self.view.get_model() |
251 |
_m.set(self.activeiter,OBSERVER_ICON,self.keptimg,OBSERVER_WEIGHT,pango.WEIGHT_NORMAL,OBSERVER_EDIT,False) |
252 |
self.activeiter = _m.append(None,self.make_row(True,self.rows[_rownum])) |
253 |
self.browser.reporter.reportNote("Kept current values"); |
254 |
|
255 |
# if the observer is the active tab, move the cursor to the new row. |
256 |
if self.browser.maintabs.get_current_page() == self.tab: |
257 |
self.view.set_cursor(_m.get_path(self.activeiter)) |
258 |
|
259 |
def on_clear_clicked(self,*args): |
260 |
self.rows = [] |
261 |
_r = [_i.getRealValue() for _i in self.columninstances] |
262 |
self.rows.append(_r) |
263 |
self.view.get_model().clear(); |
264 |
self.view.get_model().append(None,self.make_row(True,_r)) |
265 |
self.browser.reporter.reportNote("Observer '%s' cleared" % self.name) |
266 |
|
267 |
def on_view_cell_edited(self, renderer, path, newtext, datacolumn): |
268 |
# we can assume it's always the self.activeiter that is edited... |
269 |
if self.columninstances[datacolumn].isFixed(): |
270 |
self.columninstances[datacolumn].setRealValue( float(newtext) * self.units[datacolumn].getConversion() ) |
271 |
else: |
272 |
self.browser.reporter.reportError("Can't set a FIXED variable from the Observer") |
273 |
return |
274 |
self.browser.do_solve_if_auto() |
275 |
|
276 |
def sync(self): |
277 |
#new row data |
278 |
_r = [self.columninstances[_i].getRealValue() / self.units[_i].getConversion() for _i in range(0,len(self.columninstances)) ] |
279 |
|
280 |
_r1 = self.make_row(True,_r) |
281 |
|
282 |
# stick the row data into the TreeStore |
283 |
_m = self.view.get_model() |
284 |
_i = 0 |
285 |
for _c in _r1: |
286 |
_m.set(self.activeiter, _i, _c) |
287 |
_i = _i + 1 |
288 |
|
289 |
# keep the data in self.rows as well |
290 |
self.rows[self.activerow] = _r; |
291 |
|
292 |
def copy_to_clipboard(self,clip): |
293 |
_s = [] |
294 |
_s.append('\t'.join(self.titles)) |
295 |
for _r in self.rows: |
296 |
_s.append( '\t'.join([`_c` for _c in _r]) ) |
297 |
|
298 |
clip.set_text('\n'.join(_s),-1) |
299 |
|
300 |
self.browser.reporter.reportNote("Observer '%s' data copied to clipboard" % self.name) |
301 |
|
302 |
def make_row(self,isactive,row): |
303 |
# add the initial OBSERVER_INITIAL_COLS fields: |
304 |
if isactive: |
305 |
_r = [self.activeimg, pango.WEIGHT_BOLD, True] |
306 |
else: |
307 |
_r = [self.keptimg, pango.WEIGHT_NORMAL, False] |
308 |
|
309 |
for _c in row: |
310 |
_r.append(_c) |
311 |
|
312 |
return _r |
313 |
|
314 |
def add_instance(self, inst): |
315 |
# TODO big changes here.... |
316 |
if not inst.getType().isRefinedSolverVar(): |
317 |
self.browser.reporter.reportError("Instance is not a refined solver variable: can't 'observe'."); |
318 |
return |
319 |
|
320 |
_colnum = len(self.columns) |
321 |
_colname = self.browser.sim.getInstanceName(inst) |
322 |
_rownum = len(self.rows)-1 |
323 |
|
324 |
# store the instances in self.columninstances for sync purposes |
325 |
self.columninstances.append(inst) |
326 |
|
327 |
# create new TreeStore, copy of old, plus one columm |
328 |
self.columns.append(float) |
329 |
_units = inst.getType().getPreferredUnits() |
330 |
if _units == None: |
331 |
_units = inst.getType().getDimensions().getDefaultUnits() |
332 |
|
333 |
_uname = str(_units.getName()) |
334 |
if _uname.find("/")!=-1: |
335 |
_uname = "["+_uname+"]" |
336 |
|
337 |
if _uname == "": |
338 |
_title = "%s" % (_colname) |
339 |
else: |
340 |
_title = "%s / %s" % (_colname, _uname) |
341 |
|
342 |
self.titles.append(_title); |
343 |
self.units.append(_units) # we keep a track of the preferred units for the column at the time of the column creation |
344 |
|
345 |
_store = gtk.TreeStore(*(self.columns)) |
346 |
|
347 |
_iter = None |
348 |
_i = 0 |
349 |
_active = False |
350 |
for _r in self.rows: |
351 |
_r.append(OBSERVER_NULL) |
352 |
if _i == _rownum: |
353 |
_active = True |
354 |
_iter = _store.append(None, self.make_row(_active,_r) ) |
355 |
_i = _i + 1 |
356 |
|
357 |
self.activeiter = _iter |
358 |
|
359 |
# add newest data point in bottom-right |
360 |
_datacol = _colnum - OBSERVER_INITIAL_COLS |
361 |
_dataval = inst.getRealValue() / self.units[_datacol].getConversion() # convert value to units specified when col created |
362 |
self.rows[_rownum][_datacol] = _dataval |
363 |
_store.set_value(self.activeiter, _colnum, _dataval) |
364 |
|
365 |
# re-assign store to TreeView |
366 |
self.view.set_model(_store) |
367 |
|
368 |
_renderer = gtk.CellRendererText() |
369 |
_renderer.connect('edited',self.on_view_cell_edited, _datacol) |
370 |
_col = gtk.TreeViewColumn(_title, _renderer) |
371 |
_col.add_attribute(_renderer, 'text', _colnum) |
372 |
_col.add_attribute(_renderer, 'weight', OBSERVER_WEIGHT) |
373 |
_col.add_attribute(_renderer, 'editable', OBSERVER_EDIT) |
374 |
_col.set_alignment(0.0) |
375 |
_col.set_reorderable(True) |
376 |
_col.set_sort_column_id(_colnum) |
377 |
|
378 |
self.view.append_column(_col); |
379 |
|
380 |
self.browser.reporter.reportNote("Added variable '%s' to observer '%s'" % (_colname,self.name)) |
381 |
|
382 |
|