1 |
import pygtk |
2 |
pygtk.require('2.0') |
3 |
import gtk |
4 |
import gtk.glade |
5 |
import pango |
6 |
import os.path |
7 |
|
8 |
OBSERVER_EDIT_COLOR = "#008800" |
9 |
OBSERVER_NOEDIT_COLOR = "#000088" |
10 |
OBSERVER_NORMAL_COLOR = "black" |
11 |
|
12 |
# This code uses the techniques described in |
13 |
# http://www.daa.com.au/pipermail/pygtk/2006-February/011777.html |
14 |
# http://piman.livejournal.com/361173.html |
15 |
|
16 |
OBSERVER_NUM=0 |
17 |
|
18 |
class ClickableTreeColumn(gtk.TreeViewColumn): |
19 |
def __init__(self, title="", *args, **kwargs): |
20 |
super(ClickableTreeColumn, self).__init__(None, *args, **kwargs) |
21 |
self.label = gtk.Label("%s" % title) |
22 |
self.label.show() |
23 |
self.set_widget(self.label) |
24 |
|
25 |
def do_connect(self): |
26 |
""" Connect the defined 'on_click' method. Note: must be called after |
27 |
this object (ClickableTreeColumn) has been added to the TreeView, |
28 |
eg mytreeview.append_column(col). """ |
29 |
button = self.label.get_ancestor(gtk.Button) |
30 |
h = button.connect("clicked",self.on_click) |
31 |
#button.clicked() |
32 |
|
33 |
def on_click(self,widget,*args): |
34 |
print "RECEIVED EVENT" |
35 |
|
36 |
class ObserverColumn: |
37 |
""" |
38 |
A class to identify the instance that relates to a specify column |
39 |
and the units of measurement and column title, etc. |
40 |
""" |
41 |
def __init__(self,instance,index,name=None,units=None,browser=None): |
42 |
self.instance = instance |
43 |
self.name = name |
44 |
self.index = index |
45 |
|
46 |
if name==None: |
47 |
if browser == None: |
48 |
name = "UNNAMED" |
49 |
else: |
50 |
name = browser.sim.getInstanceName(instance) |
51 |
|
52 |
if units == None: |
53 |
units = instance.getType().getPreferredUnits() |
54 |
if units == None: |
55 |
units = instance.getType().getDimensions().getDefaultUnits() |
56 |
|
57 |
uname = str(units.getName()) |
58 |
if len(uname) or uname.find("/")!=-1: |
59 |
uname = "["+uname+"]" |
60 |
|
61 |
if uname == "": |
62 |
_title = "%s" % (name) |
63 |
else: |
64 |
_title = "%s / %s" % (name, uname) |
65 |
|
66 |
self.title = _title |
67 |
self.units = units |
68 |
self.uname = uname |
69 |
self.name = name |
70 |
|
71 |
def __repr__(self): |
72 |
return "ObserverColumn(name="+self.name+")" |
73 |
|
74 |
def cellvalue(self, column, cell, model, iter): |
75 |
#print "RENDERING COLUMN",self.index |
76 |
_rowobject = model.get_value(iter,0) |
77 |
|
78 |
cell.set_property('editable',False) |
79 |
cell.set_property('weight',400) |
80 |
try: |
81 |
if _rowobject.active: |
82 |
_rawval = self.instance.getRealValue() |
83 |
if self.instance.getType().isRefinedSolverVar(): |
84 |
if self.instance.isFixed(): |
85 |
cell.set_property('editable',True) |
86 |
cell.set_property('weight',700) |
87 |
cell.set_property('foreground',OBSERVER_EDIT_COLOR) |
88 |
else: |
89 |
cell.set_property('foreground',OBSERVER_NOEDIT_COLOR) |
90 |
else: |
91 |
cell.set_property('foreground',OBSERVER_NORMAL_COLOR) |
92 |
_rawval = _rowobject.values[self.index] |
93 |
_dataval = _rawval / self.units.getConversion() |
94 |
except IndexError: |
95 |
_dataval = "" |
96 |
|
97 |
cell.set_property('text', _dataval) |
98 |
|
99 |
class ObserverRow: |
100 |
""" |
101 |
Just a container for a vector of values, but with columns that |
102 |
should correspond to those in the Observer object's vector of |
103 |
ObserverColumn objects. |
104 |
""" |
105 |
def __init__(self,values=None,active=True): |
106 |
if values==None: |
107 |
values=[] |
108 |
|
109 |
self.values = values |
110 |
self.active = active |
111 |
|
112 |
def make_static(self,table): |
113 |
self.active = False |
114 |
print "TABLE COLS:",table.cols |
115 |
print "ROW VALUES:",self.values |
116 |
_v = [] |
117 |
for col in table.cols.values(): |
118 |
_v.append( col.instance.getRealValue() ) |
119 |
self.values = _v |
120 |
print "Made static, values:",self.values |
121 |
|
122 |
def get_values(self,table): |
123 |
if not self.active: |
124 |
vv = [] |
125 |
for k,v in table.cols.iteritems(): |
126 |
if k<len(self.values): |
127 |
vv.append(self.values[k]/v.units.getConversion()) |
128 |
return vv |
129 |
else: |
130 |
return [col.instance.getRealValue()/col.units.getConversion() \ |
131 |
for index,col in table.cols.iteritems() \ |
132 |
] |
133 |
|
134 |
class ObserverTab: |
135 |
|
136 |
def __init__(self,xml,browser,tab,name=None,alive=True): |
137 |
global OBSERVER_NUM |
138 |
self.colindex = 0 |
139 |
if name==None: |
140 |
OBSERVER_NUM=OBSERVER_NUM+1 |
141 |
name = "Observer %d" % OBSERVER_NUM |
142 |
self.name = name |
143 |
self.browser=browser |
144 |
xml.signal_autoconnect(self) |
145 |
self.view = xml.get_widget('observerview') |
146 |
self.tab = tab |
147 |
self.alive=alive |
148 |
if self.alive: |
149 |
self.browser.reporter.reportNote("New observer is 'alive'") |
150 |
|
151 |
self.keptimg = gtk.Image() |
152 |
self.activeimg = gtk.Image() |
153 |
self.activeimg.set_from_file(os.path.join(browser.options.assets_dir,"active.png")) |
154 |
# create PixBuf objects from these? |
155 |
self.rows = [] |
156 |
_store = gtk.TreeStore(object) |
157 |
self.cols = {} |
158 |
|
159 |
# create the 'active' pixbuf column |
160 |
_renderer = gtk.CellRendererPixbuf() |
161 |
_col = ClickableTreeColumn("") |
162 |
_col.pack_start(_renderer,False) |
163 |
_col.set_cell_data_func(_renderer, self.activepixbufvalue) |
164 |
self.view.append_column(_col) |
165 |
_col.do_connect() |
166 |
|
167 |
# initially there will not be any other columns |
168 |
|
169 |
if self.alive: |
170 |
# for a 'live' Observer, create the 'active' bottom row |
171 |
self.browser.reporter.reportNote("Adding empty row to store") |
172 |
_row = ObserverRow() |
173 |
self.activeiter = _store.append(None, [_row] ) |
174 |
self.rows.append(_row) |
175 |
|
176 |
self.view.set_model(_store) |
177 |
self.browser.reporter.reportNote("Created observer '%s'" % self.name) |
178 |
|
179 |
def activepixbufvalue(self,column,cell,model,iter): |
180 |
_rowobject = model.get_value(iter,0) |
181 |
if _rowobject.active: |
182 |
cell.set_property('pixbuf',self.activeimg.get_pixbuf()) |
183 |
else: |
184 |
cell.set_property('pixbuf',self.keptimg.get_pixbuf()) |
185 |
|
186 |
def on_add_clicked(self,*args): |
187 |
self.do_add_row() |
188 |
|
189 |
def on_clear_clicked(self,*args): |
190 |
_store = self.view.get_model() |
191 |
_store.clear(); |
192 |
self.rows = [] |
193 |
self.activeiter = _store.append(None, [ObserverRow()] ) |
194 |
|
195 |
def plot(self,x=None,y=None,y2=None): |
196 |
"""create a plot from two columns in the ObserverTable""" |
197 |
import platform |
198 |
import matplotlib |
199 |
matplotlib.use('GTKAgg') |
200 |
import pylab |
201 |
pylab.ioff() |
202 |
if x is None or y is None: |
203 |
if len(self.cols)<2: |
204 |
raise Exception("Not enough columns to plot (need 2+)") |
205 |
if x is None: |
206 |
x=self.cols[0] |
207 |
if y is None: |
208 |
y=self.cols[1] |
209 |
|
210 |
if x.__class__ is int and x>=0 and x<len(self.cols): |
211 |
x=self.cols[x] |
212 |
if y.__class__ is int and y>=0 and y<len(self.cols): |
213 |
y=self.cols[y] |
214 |
if y2.__class__ is int and y2>=0 and y2<len(self.cols): |
215 |
y2=self.cols[y2] |
216 |
|
217 |
ncols = 2 |
218 |
if y2 is not None: |
219 |
ncols+=1 |
220 |
|
221 |
A = pylab.zeros((len(self.rows),ncols),'f') |
222 |
for i in range(len(self.rows)): |
223 |
r = self.rows[i].get_values(self) |
224 |
A[i,0]=r[x.index] |
225 |
A[i,1]=r[y.index] |
226 |
if y2 is not None: |
227 |
A[i,2]=r[y2.index] |
228 |
print A |
229 |
pylab.figure() |
230 |
p1 = pylab.plot(A[:,0],A[:,1],'b-') |
231 |
pylab.xlabel(x.title) |
232 |
pylab.ylabel(y.title) |
233 |
|
234 |
if y2 is not None: |
235 |
ax2 = pylab.twinx() |
236 |
p2 = pylab.plot(A[:,0],A[:,2],'r-') |
237 |
pylab.ylabel(y2.title) |
238 |
ax2.yaxis.tick_right() |
239 |
pylab.legend([y.name,y2.name]) |
240 |
|
241 |
pylab.ion() |
242 |
if platform.system()=="Windows": |
243 |
pylab.show() |
244 |
else: |
245 |
pylab.show(False) |
246 |
|
247 |
def on_plot_clicked(self,*args): |
248 |
try: |
249 |
self.plot() |
250 |
except Exception,e: |
251 |
self.browser.reporter.reportError(str(e)) |
252 |
|
253 |
def do_add_row(self,values=None): |
254 |
_store = self.view.get_model() |
255 |
if self.alive: |
256 |
_row = ObserverRow() |
257 |
self.rows.append(_row) |
258 |
_oldrow = _store.get_value(self.activeiter,0) |
259 |
_oldrow.make_static(self) |
260 |
self.activeiter = _store.append(None,[_row]) |
261 |
_path = _store.get_path(self.activeiter) |
262 |
_oldpath,_oldcol = self.view.get_cursor() |
263 |
self.view.set_cursor(_path, _oldcol) |
264 |
else: |
265 |
_row = ObserverRow(values=values,active=False) |
266 |
self.rows.append(_row) |
267 |
_store.append(None,[_row]) |
268 |
#self.browser.reporter.reportNote("Added data row") |
269 |
|
270 |
def on_view_cell_edited(self, renderer, path, newtext, col): |
271 |
# we can assume it's always the self.activeiter that is edited... |
272 |
if col.instance.isFixed(): |
273 |
val = float(newtext) * col.units.getConversion() |
274 |
col.instance.setRealValue( val ) |
275 |
self.browser.reporter.reportNote("Updated value to %f" % float(newtext)) |
276 |
else: |
277 |
self.browser.reporter.reportError("Can't set a FREE variable from the Observer") |
278 |
return |
279 |
self.browser.do_solve_if_auto() |
280 |
|
281 |
def sync(self): |
282 |
self.view.queue_draw() |
283 |
#self.browser.reporter.reportNote("SYNC performed") |
284 |
|
285 |
def add_instance(self,instance): |
286 |
_col = ObserverColumn(instance,self.colindex,browser=self.browser) |
287 |
self.cols[self.colindex] = _col |
288 |
self.colindex = self.colindex + 1 |
289 |
|
290 |
# create a new column |
291 |
_renderer = gtk.CellRendererText() |
292 |
_renderer.connect('edited',self.on_view_cell_edited, _col) |
293 |
_tvcol = ClickableTreeColumn(_col.title) |
294 |
_tvcol.pack_start(_renderer,False) |
295 |
_tvcol.set_cell_data_func(_renderer, _col.cellvalue) |
296 |
self.view.append_column(_tvcol); |
297 |
_tvcol.do_connect() |
298 |
#self.browser.reporter.reportError("cols = "+str(self.cols)) |
299 |
|
300 |
def copy_to_clipboard(self,clip): |
301 |
_s = [] |
302 |
_s.append('\t'.join([_v.title for _k,_v in self.cols.iteritems()])) |
303 |
#_cf = [_v.units.getConversion() for _k,_v in self.cols.iteritems()] |
304 |
print "COPYING %d ROWS" % len(self.rows) |
305 |
#print "CONVERSIONS:",_cf |
306 |
for _r in self.rows: |
307 |
_s.append("\t".join([`_v` for _v in _r.get_values(self)])) |
308 |
|
309 |
clip.set_text('\n'.join(_s),-1) |
310 |
|
311 |
self.browser.reporter.reportNote("Observer '%s' data copied to clipboard" % self.name) |