Package VisionEgg :: Package PyroApps :: Module EPhysGUI
[frames] | no frames]

Source Code for Module VisionEgg.PyroApps.EPhysGUI

   1  #!/usr/bin/env python 
   2  # 
   3  # The Vision Egg: EPhysGUI 
   4  # 
   5  # Copyright (C) 2001-2004 Andrew Straw. 
   6  # Copyright (C) 2004 Imran S. Ali, Lachlan Dowd 
   7  # Copyright (C) 2004, 2008 California Institute of Technology 
   8  # 
   9  # Author: Andrew Straw <astraw@users.sourceforge.net> 
  10  # URL: <http://www.visionegg.org/> 
  11  # 
  12  # Distributed under the terms of the GNU Lesser General Public License 
  13  # (LGPL). See LICENSE.TXT that came with this file. 
  14  # 
  15  # $Id: EPhysGUI.py 1455 2008-06-07 15:42:14Z astraw $ 
  16   
  17  import VisionEgg 
  18  __version__ = VisionEgg.release_name 
  19  __cvs__ = '$Revision: 1455 $'.split()[1] 
  20  __date__ = ' '.join('$Date: 2008-06-07 08:42:14 -0700 (Sat, 07 Jun 2008) $'.split()[1:3]) 
  21  __author__ = 'Andrew Straw <astraw@users.sourceforge.net>' 
  22   
  23  import sys, socket, re, time, string, types, os 
  24  import parser, symbol, token, compiler 
  25  import pickle, random, math, threading 
  26  import Tkinter, tkMessageBox, tkSimpleDialog, tkFileDialog 
  27  import StringIO 
  28  import Pyro 
  29  import numpy 
  30   
  31  import VisionEgg 
  32  import VisionEgg.PyroClient 
  33  import VisionEgg.PyroApps.ScreenPositionGUI 
  34  import VisionEgg.GUI 
  35  import VisionEgg.ParameterTypes as ve_types 
  36   
  37  # Add your client modules here 
  38  import VisionEgg.PyroApps.TargetGUI 
  39  import VisionEgg.PyroApps.MouseTargetGUI 
  40  import VisionEgg.PyroApps.FlatGratingGUI 
  41  import VisionEgg.PyroApps.SphereGratingGUI 
  42  import VisionEgg.PyroApps.SpinningDrumGUI 
  43  import VisionEgg.PyroApps.GridGUI 
  44  import VisionEgg.PyroApps.ColorCalGUI 
  45   
  46  import VisionEgg.PyroApps.DropinGUI 
  47  import VisionEgg.PyroApps.AST_ext as AST_ext 
  48  import VisionEgg.PyroApps.VarTypes as VarTypes 
  49   
  50  client_list = [] 
  51  client_list.extend( VisionEgg.PyroApps.TargetGUI.get_control_list() ) 
  52  client_list.extend( VisionEgg.PyroApps.MouseTargetGUI.get_control_list() ) 
  53  client_list.extend( VisionEgg.PyroApps.FlatGratingGUI.get_control_list() ) 
  54  client_list.extend( VisionEgg.PyroApps.SphereGratingGUI.get_control_list() ) 
  55  client_list.extend( VisionEgg.PyroApps.SpinningDrumGUI.get_control_list() ) 
  56  client_list.extend( VisionEgg.PyroApps.GridGUI.get_control_list() ) 
  57  client_list.extend( VisionEgg.PyroApps.ColorCalGUI.get_control_list() ) 
  58  client_list.extend( VisionEgg.PyroApps.DropinGUI.get_control_list() ) 
  59   
60 -class ContainedObjectBase:
61 """Base class to encapsulate objects, provides useful methods when used in GUI"""
62 - def __init__(self):
63 raise RuntimeError("Abstract base class!")
64 - def get_str_30(self):
65 return "**** this is a generic str_30 ****"
66 - def get_contained(self):
67 return self.contained
68 header = "unknown parameters"
69
70 -class ScrollListFrame(Tkinter.Frame):
71 - def __init__(self,master=None,list_of_contained_objects=None,contained_objectbject_maker=None, 72 container_class=ContainedObjectBase, 73 **cnf):
74 Tkinter.Frame.__init__(self, master, **cnf) 75 if list_of_contained_objects is None: 76 self.list = [] 77 else: 78 self.list = list_of_contained_objects 79 self.container_class = container_class 80 81 # allow column to expand 82 self.columnconfigure(0,weight=1) 83 84 # The frame that has the list and the vscroll 85 self.frame = Tkinter.Frame(self,borderwidth=2) 86 self.frame.grid(row=0,sticky="nwes") 87 88 # allow column to expand 89 self.frame.columnconfigure(0,weight=1) 90 91 self.frame.vscroll = Tkinter.Scrollbar(self.frame,orient=Tkinter.VERTICAL) 92 self.frame.hscroll = Tkinter.Scrollbar(self.frame,orient=Tkinter.HORIZONTAL) 93 self.frame.title = Tkinter.Listbox( 94 self.frame, 95 relief=Tkinter.FLAT, 96 font=('courier',10,'bold'), 97 height=1, 98 # selectbackground='#eed5b7', 99 # selectborderwidth=0, 100 # selectmode=None, 101 exportselection=0) 102 self.frame.title.insert(Tkinter.END, self.container_class.header) 103 self.frame.list = Tkinter.Listbox( 104 self.frame, 105 relief=Tkinter.SUNKEN, 106 font=('courier',10,'normal'), 107 width=40, height=10, 108 selectbackground='#eed5b7', 109 selectborderwidth=0, 110 selectmode=Tkinter.BROWSE, 111 xscroll=self.frame.hscroll.set, 112 yscroll=self.frame.vscroll.set, 113 exportselection=0) 114 115 self.frame.hscroll['command'] = self.delegate_hscroll 116 self.frame.hscroll.grid(row=3,column=0,sticky='we') 117 self.frame.vscroll['command'] = self.frame.list.yview 118 self.frame.vscroll.grid(row=2,column=1,sticky='ns') 119 self.frame.title.grid(row=1,column=0,ipady=0,pady=0,sticky='we') 120 self.frame.list.grid(row=2,column=0,sticky='nwes') 121 self.frame.list.bind('<Double-Button-1>',self.edit_selected) 122 123 # The buttons on bottom 124 self.bar = Tkinter.Frame(self,borderwidth=2) 125 self.bar.grid(row=1,sticky="we") 126 self.bar.add = Tkinter.Button(self.bar,text='Add...',command=self.add_new) 127 self.bar.add.grid(row=0,column=0,sticky='we') 128 self.bar.edit = Tkinter.Button(self.bar,text='Edit...',command=self.edit_selected) 129 self.bar.edit.grid(row=0,column=1,sticky='we') 130 self.bar.remove = Tkinter.Button(self.bar,text='Remove',command=self.remove_selected) 131 self.bar.remove.grid(row=0,column=2,sticky='we') 132 self.bar.move_up = Tkinter.Button(self.bar,text='Up',command=self.move_selected_up) 133 self.bar.move_up.grid(row=0,column=3,sticky='we') 134 self.bar.move_down = Tkinter.Button(self.bar,text='Down',command=self.move_selected_down) 135 self.bar.move_down.grid(row=0,column=4,sticky='we') 136 self.bar.tk_menuBar(self.bar.add,self.bar.remove) 137 #Lachie- My bar for setting parent 138 self.bar.merge = Tkinter.Button(self.bar,text='Merge/Unmerge',command=self.make_merge) 139 self.bar.merge.grid(row=0,column=5,sticky='we') 140 self.bar.tk_menuBar(self.bar.add,self.bar.remove) 141 self.update_now()
142
143 - def list2D_coordinates(self, main_index, main_list):
144 # This is a function for finding the 2-d 145 # list coordinates of an element which may be inside a 146 # list-nested-list. 147 # eg. if x = [[e, e, e], [e], [e, e]] 148 # Then the coordinates of the element at index 4 is: (2, 0) 149 # 150 # Initialization: 151 i = -1 152 j = -1 153 element_count = 0 154 # Main body: 155 for nested_list in main_list: 156 j = -1 157 i = i + 1 158 for element in nested_list: 159 j = j + 1 160 element_count = element_count + 1 161 if (element_count - 1) == main_index: 162 return [i, j] 163 # Unsuccessful exit: 164 return [-1, -1]
165
166 - def delegate_hscroll(self,*args,**kw):
167 self.frame.title.xview(*args,**kw) 168 self.frame.list.xview(*args,**kw)
169
170 - def get_list_uncontained(self):
171 results = [] 172 for contained_object_item in self.list: 173 #results.append( contained_object_item.get_contained() ) 174 results.append( contained_object_item ) 175 return results
176
177 - def update_now(self):
178 self.frame.list.delete(0,Tkinter.END) 179 max_len = 0 180 for loop_container in self.list: 181 for loop in loop_container: 182 item_str_30 = loop.get_str_30() 183 max_len = max(max_len,len(item_str_30)) 184 self.frame.list.insert(Tkinter.END,item_str_30) 185 self.frame.list.insert(Tkinter.END,"") 186 self.frame.title.delete(0,Tkinter.END) 187 self.frame.title.insert(Tkinter.END, self.container_class.header.ljust(max_len))
188
189 - def add_new(self):
190 contained_object = self.make_contained_object(self.container_class) 191 if contained_object: 192 self.list.append( [contained_object] ) 193 self.update_now()
194
195 - def edit_selected(self,dummy_arg=None):
196 selected = self.get_selected() 197 # Get 2-D list coordinates of selected object of class "LoopContainedObject": 198 loop_coordinates = self.list2D_coordinates(selected, self.list) 199 main_list_index = loop_coordinates[0] 200 loop_list_index = loop_coordinates[1] 201 if selected is not None: 202 if len(self.list[main_list_index]) == 1: 203 orig_contained_object = self.list[main_list_index][loop_list_index] 204 modified_contained_object = self.edit_contained_object( orig_contained_object ) 205 if modified_contained_object is not None: # "Cancel" press results in None 206 self.list[main_list_index][loop_list_index] = modified_contained_object 207 self.update_now() 208 else: 209 tkMessageBox.showerror("Cannot edit this variable", "This variable needs to be isolated/unmerged")
210
211 - def remove_selected(self):
212 selected = self.get_selected() 213 # Get 2-D list coordinates of selected object of class "LoopContainedObject": 214 loop_coordinates = self.list2D_coordinates(selected, self.list) 215 main_list_index = loop_coordinates[0] 216 loop_list_index = loop_coordinates[1] 217 if selected is not None: 218 del self.list[main_list_index][loop_list_index] 219 if self.list[main_list_index] == []: 220 del self.list[main_list_index] 221 self.update_now()
222
223 - def move_selected_up(self,dummy_arg=None):
224 selected = self.get_selected() 225 # Get 2-D list coordinates of selected object of class "LoopContainedObject": 226 loop_coordinates = self.list2D_coordinates(selected, self.list) 227 main_list_index = loop_coordinates[0] 228 loop_list_index = loop_coordinates[1] 229 new_selected_index = selected 230 if selected is not None: 231 # If the selected variable is first in its "loop_list": 232 if loop_list_index == 0: 233 # If not the first "loop_list": 234 if main_list_index != 0: 235 # Then we move up the entire "loop_list": 236 selected_loop_list = self.list[main_list_index] 237 del self.list[main_list_index] 238 new_main_list_index = main_list_index - 1 239 self.list.insert(new_main_list_index, selected_loop_list) 240 new_selected_index = selected - len(self.list[main_list_index]) 241 self.update_now() 242 243 # Else we just move up a variable within a "loop_list": 244 else: 245 selected_loop_container = self.list[main_list_index][loop_list_index] 246 del self.list[main_list_index][loop_list_index] 247 new_loop_list_index = loop_list_index - 1 248 self.list[main_list_index].insert(new_loop_list_index, selected_loop_container) 249 new_selected_index = selected - 1 250 self.update_now() 251 252 new_selected_index = self.map_to_listbox_index(new_selected_index) 253 self.frame.list.selection_set(new_selected_index)
254
255 - def move_selected_down(self,dummy_arg=None):
256 selected = self.get_selected() 257 # Get 2-D list coordinates of selected object of class "LoopContainedObject": 258 loop_coordinates = self.list2D_coordinates(selected, self.list) 259 main_list_index = loop_coordinates[0] 260 loop_list_index = loop_coordinates[1] 261 new_selected_index = selected 262 if selected is not None: 263 # If the selected variable is last in its "loop_list": 264 if loop_list_index == (len(self.list[main_list_index]) - 1): 265 # If not the last "loop_list": 266 if main_list_index != (len(self.list) - 1): 267 # Then we move down the entire "loop_list": 268 selected_loop_list = self.list[main_list_index] 269 del self.list[main_list_index] 270 new_main_list_index = main_list_index + 1 271 self.list.insert(new_main_list_index, selected_loop_list) 272 new_selected_index = selected + len(self.list[main_list_index]) 273 self.update_now() 274 275 # Else we just move down a variable within a "loop_list": 276 #elif loop_list_index != (len(self.list[main_list_index]) - 1): 277 else: 278 selected_loop_container = self.list[main_list_index][loop_list_index] 279 del self.list[main_list_index][loop_list_index] 280 new_loop_list_index = loop_list_index + 1 281 self.list[main_list_index].insert(new_loop_list_index, selected_loop_container) 282 new_selected_index = selected + 1 283 self.update_now() 284 #else: 285 #tkMessageBox.showerror("Cannot move this variable down", "Select unmerge instead") 286 287 new_selected_index = self.map_to_listbox_index(new_selected_index) 288 self.frame.list.selection_set(new_selected_index)
289 290 291 292 293 294 295
296 - def make_merge(self):
297 # Notes: 298 # "self.list" is a list of lists, each of which, a 299 # "loop_list", contains "LoopContainedObject" class objects: 300 # eg. [[a], [b, c], [d]] 301 selected = self.get_selected() 302 303 merge_error = 0 304 merge_error_msg = "" 305 306 # The purpose of this function is to "merge" selected objects of class 307 # "LoopContainedObject" into a preceding list: 308 # eg. [[a], [b, c], [d]] => [[a], [b, c, d]]] 309 # where selected 'd' was "merged" into preceding list. 310 # Note, this function can also perform the reverse, provided that the 311 # the selected object of class "LoopContainedObject" is the LAST one 312 # in its "loop_list". 313 # Supported cases: 314 # [[a], [b*, c], [d]] => [[a, b, c], [d]] merge 315 # [[a], [b, c*], [d]] => [[a], [b], [c, d]] unmerge 316 # Unsupported cases: 317 # [[a], [b, c*, d], [e]] => cannot unmerge! 318 319 # Get 2-D list coordinates of selected object of class "LoopContainedObject": 320 loop_coordinates = self.list2D_coordinates(selected, self.list) 321 main_list_index = loop_coordinates[0] 322 loop_list_index = loop_coordinates[1] 323 324 # Checking that an item is actually selected: 325 if selected is not None: 326 327 selected_loop_list = self.list[main_list_index] 328 selected_loop_container = selected_loop_list[loop_list_index] 329 preceding_loop_container = self.list[main_list_index - 1][0] 330 331 # Trying to perform merge? 332 if loop_list_index == 0: 333 334 # Ensure selected "LoopContainerObject" is not in first "loop_list": 335 if main_list_index > 0: 336 337 # Can only carry out merge if "Loop" object sequence lengths are 338 # the same length within a "loop_list": 339 if len(selected_loop_container.contained.parameters.sequence) == len(preceding_loop_container.contained.parameters.sequence): 340 341 # Perform the merge. All variables that are currently merged with the selected variable, 342 # are merged with the new variable(s) as well. 343 i = 0 344 max_index = len(selected_loop_list) 345 while i < max_index: 346 dummy_loop_container = selected_loop_list[0] 347 del self.list[main_list_index][0] 348 self.list[main_list_index - 1].append(dummy_loop_container) 349 i = i + 1 350 351 # Remove the selected "loop_list" if it is now empty: 352 if self.list[main_list_index] == []: 353 del self.list[main_list_index] 354 355 else: 356 merge_error = 1 357 merge_error_msg = "Cannot merge variables with different sequence lengths" 358 359 else: 360 merge_error = 3 361 #merge_error_msg = "Variable is at the top level" 362 363 # Trying to perform an "unmerge": 364 else: 365 366 # Ensure selected "LoopContainerObject" is last object in its "loop_list": 367 if loop_list_index == (len(selected_loop_list) - 1): 368 369 # Perform the unmerge: 370 del self.list[main_list_index][loop_list_index] 371 self.list.insert((main_list_index + 1), [selected_loop_container]) 372 373 else: 374 merge_error = 2 375 merge_error_msg = "Unmerge lowest variable in this cluster first" 376 377 if merge_error == 1: 378 tkMessageBox.showerror("Cannot perform merge", merge_error_msg) 379 elif merge_error == 2: 380 tkMessageBox.showerror("Cannot perform unmerge", merge_error_msg) 381 elif merge_error == 3: 382 # non critical errors 383 pass 384 else: 385 #debugger: 386 #print len(self.list) 387 #print "" 388 #for x in self.list: 389 # print len(x) 390 #print "--------------" 391 self.update_now()
392 393 394
395 - def make_contained_object(self, container_class):
396 """Factory function for ContainedObjectBase""" 397 if container_class == LoopContainedObject: 398 return self.make_loop_contained_object() 399 params = {} 400 p = container_class.contained_class.parameters_and_defaults 401 keys = p.keys() 402 keys.sort() 403 for pname in keys: 404 if p[pname][1] == ve_types.String: 405 params[pname] = tkSimpleDialog.askstring(pname,pname,initialvalue=p[pname][0]) 406 elif p[pname][1] == ve_types.Integer: 407 params[pname] = tkSimpleDialog.askinteger(pname,pname,initialvalue=p[pname][0]) 408 elif p[pname][1] == ve_types.Real: 409 params[pname] = tkSimpleDialog.askfloat(pname,pname,initialvalue=p[pname][0]) 410 elif p[pname][1] == ve_types.Sequence: 411 params[pname] = eval("["+tkSimpleDialog.askstring(pname,pname,initialvalue="1,2,3")+"]") 412 if type(params[pname]) is not types.ListType: 413 raise ValueError("You must enter a list in the form of '[1,2,3]'") 414 else: 415 raise NotImplementedError("Don't know about type %s"%(p[pname][1],)) 416 if params[pname] is None: 417 raise RuntimeError("Input cancelled") 418 contained = container_class.contained_class(**params) # call constructor 419 return container_class(contained)
420
421 - def edit_contained_object(self, contained_object):
422 if not isinstance(contained_object,LoopContainedObject): 423 raise NotImplementedError("") 424 orig_contained = contained_object.get_contained() 425 d = LoopParamDialog(self, title="Loop Parameters", orig_values=orig_contained ) 426 if d.result: 427 return LoopContainedObject(d.result) 428 else: 429 return
430
431 - def make_loop_contained_object(self):
432 d = LoopParamDialog(self, title="Loop Parameters" ) 433 if d.result: 434 return LoopContainedObject(d.result) 435 else: 436 return
437 438 # Returns index of selected item ignoring blank listbox entries: 439 # eg. if listbox had: 440 # 441 # 0 a 442 # 1 b 443 # 2 444 # 3 c 445 # 4 d 446 # 447 # Then the index of element 'c' would be 2
448 - def get_selected(self):
449 items = self.frame.list.curselection() 450 try: 451 items = map(int, items) 452 453 except ValueError: pass 454 if len(items) > 0: 455 selected_item_index = items[0] 456 if self.frame.list.get(selected_item_index) != "": 457 blankentrycount = 0 458 i = 0 459 while i < selected_item_index: 460 if self.frame.list.get(i) == "": 461 blankentrycount = blankentrycount + 1 462 i = i + 1 463 return (selected_item_index - blankentrycount) 464 465 return None
466 467 468 # Performs reverse of above: 469 # eg. if listbox had: 470 # 471 # 0 a 472 # 1 b 473 # 2 474 # 3 c 475 # 4 d 476 # 477 # Then "mapping" of given index 2 would result in return value of 3
478 - def map_to_listbox_index(self, index):
479 validentrycount = 0 480 i = 0 481 while i < self.frame.list.size(): 482 if self.frame.list.get(i) != "": 483 validentrycount = validentrycount + 1 484 if validentrycount == (index + 1): 485 return i 486 i = i + 1 487 return -1
488 ################################################### 489
490 -class Loop(VisionEgg.ClassWithParameters):
491 parameters_and_defaults = {'variable':('<repeat>', 492 ve_types.String), 493 'sequence':([1, 1, 1], 494 ve_types.Sequence(ve_types.Real)), 495 'rest_duration_sec':(1.0, 496 ve_types.Real)} 497 __slots__ = ( 498 'num_done', 499 ) 500 501
502 - def __init__(self,**kw):
503 VisionEgg.ClassWithParameters.__init__(self,**kw) 504 self.num_done = 0
505 - def is_done(self):
506 return self.num_done >= len(self.parameters.sequence)
507 - def get_current(self):
508 return self.parameters.sequence[self.num_done]
509 - def advance(self):
510 self.num_done += 1
511 - def reset(self):
512 self.num_done = 0
513
514 -class LoopContainedObject(ContainedObjectBase):
515 """Container for Loop class""" 516 contained_class = Loop 517 header = " variable rest N values"
518 - def __init__(self,contained):
519 self.contained = contained
520 - def get_str_30(self):
521 p = self.contained.parameters 522 seq_str = "" 523 for val in p.sequence: 524 seq_str += str(val) + " " 525 name_str = p.variable 526 if len(name_str) > 15: 527 name_str = name_str[:15] 528 return "% 15s % 4s % 4d % 4s"%(name_str, str(p.rest_duration_sec), len(p.sequence), seq_str)
529
530 -class LoopParamDialog(tkSimpleDialog.Dialog):
531 - def __init__(self,*args,**kw):
532 #intercept orig_values argument 533 if kw.has_key('orig_values'): 534 self.orig_values = kw['orig_values'] 535 del kw['orig_values'] 536 else: 537 self.orig_values = None 538 return tkSimpleDialog.Dialog.__init__(self, *args, **kw )
539
540 - def body(self,master):
541 Tkinter.Label(master, 542 text="Add sequence of automatic variable values", 543 font=("Helvetica",12,"bold"),).grid(row=0,column=0,columnspan=2) 544 545 var_frame = Tkinter.Frame(master, 546 relief=Tkinter.GROOVE, 547 borderwidth=2) 548 var_frame.grid(row=1,column=0) 549 550 sequence_frame = Tkinter.Frame(master) 551 sequence_frame.grid(row=1,column=1) 552 553 rest_dur_frame = Tkinter.Frame(master) 554 rest_dur_frame.grid(row=2,column=0,columnspan=2) 555 556 # loopable variable frame stuff 557 global loopable_variables 558 num_cols = int(math.ceil(len(loopable_variables)/10.0)) # 10 variables per column 559 560 var_frame_row = 0 561 Tkinter.Label(var_frame, 562 text="Select a variable", 563 font=("Helvetica",12,"bold"),).grid(row=var_frame_row, 564 column=0, 565 columnspan=num_cols) 566 567 self.var_name = Tkinter.StringVar() 568 self.var_name.set("<repeat>") 569 var_names = loopable_variables[:] # copy 570 var_names.sort() 571 572 var_frame_row += 1 573 Tkinter.Radiobutton( var_frame, 574 text="Repeat (Average)", 575 variable=self.var_name, 576 value="<repeat>", 577 anchor=Tkinter.W).grid(row=var_frame_row, 578 column=0, 579 sticky="w") 580 var_frame_row += 1 581 for var_name in var_names: 582 use_row = var_frame_row%10+1 583 Tkinter.Radiobutton( var_frame, 584 text=var_name, 585 variable=self.var_name, 586 value=var_name, 587 anchor=Tkinter.W).grid(row=use_row, 588 column=int(math.floor(var_frame_row/10.)), 589 sticky="w") 590 var_frame_row += 1 591 592 # sequence entry frame 593 seq_row = 0 594 Tkinter.Label(sequence_frame, 595 text="Sequence values", 596 font=("Helvetica",12,"bold"),).grid(row=seq_row,column=0,columnspan=2) 597 598 seq_row += 1 599 self.sequence_type = Tkinter.StringVar() 600 self.sequence_type.set("manual") 601 602 Tkinter.Radiobutton( sequence_frame, 603 text="Manual:", 604 variable=self.sequence_type, 605 value="manual", 606 anchor=Tkinter.W).grid(row=seq_row,column=0,sticky="w") 607 608 self.sequence_manual_string = Tkinter.StringVar() 609 self.sequence_manual_string.set("[1,2,3]") 610 Tkinter.Entry(sequence_frame, 611 textvariable=self.sequence_manual_string).grid(row=seq_row,column=1) 612 613 seq_row += 1 614 Tkinter.Radiobutton( sequence_frame, 615 text="Linear:", 616 variable=self.sequence_type, 617 value="linear", 618 anchor=Tkinter.W).grid(row=seq_row,column=0,sticky="w") 619 620 self.lin_start_tk = Tkinter.DoubleVar() 621 self.lin_start_tk.set(1.0) 622 self.lin_stop_tk = Tkinter.DoubleVar() 623 self.lin_stop_tk.set(100.0) 624 self.lin_n_tk = Tkinter.IntVar() 625 self.lin_n_tk.set(3) 626 627 lin_frame = Tkinter.Frame( sequence_frame) 628 lin_frame.grid(row=seq_row,column=1) 629 Tkinter.Label(lin_frame,text="start:").grid(row=0,column=0) 630 Tkinter.Entry(lin_frame,textvariable=self.lin_start_tk,width=6).grid(row=0,column=1) 631 Tkinter.Label(lin_frame,text=" stop:").grid(row=0,column=2) 632 Tkinter.Entry(lin_frame,textvariable=self.lin_stop_tk,width=6).grid(row=0,column=3) 633 Tkinter.Label(lin_frame,text=" N:").grid(row=0,column=4) 634 Tkinter.Entry(lin_frame,textvariable=self.lin_n_tk,width=6).grid(row=0,column=5) 635 636 seq_row += 1 637 Tkinter.Radiobutton( sequence_frame, 638 text="Log:", 639 variable=self.sequence_type, 640 value="log", 641 anchor=Tkinter.W).grid(row=seq_row,column=0,sticky="w") 642 643 self.log_start_tk = Tkinter.DoubleVar() 644 self.log_start_tk.set(-1.0) 645 self.log_stop_tk = Tkinter.DoubleVar() 646 self.log_stop_tk.set(2.0) 647 self.log_n_tk = Tkinter.IntVar() 648 self.log_n_tk.set(5) 649 650 log_frame = Tkinter.Frame( sequence_frame) 651 log_frame.grid(row=seq_row,column=1) 652 Tkinter.Label(log_frame,text="start: 10^").grid(row=0,column=0) 653 Tkinter.Entry(log_frame,textvariable=self.log_start_tk,width=6).grid(row=0,column=1) 654 Tkinter.Label(log_frame,text=" stop: 10^").grid(row=0,column=2) 655 Tkinter.Entry(log_frame,textvariable=self.log_stop_tk,width=6).grid(row=0,column=3) 656 Tkinter.Label(log_frame,text=" N:").grid(row=0,column=4) 657 Tkinter.Entry(log_frame,textvariable=self.log_n_tk,width=6).grid(row=0,column=5) 658 659 seq_row += 1 660 Tkinter.Radiobutton( sequence_frame, 661 text="Log:", 662 variable=self.sequence_type, 663 value="logb", 664 anchor=Tkinter.W).grid(row=seq_row,column=0,sticky="w") 665 666 self.logb_start_tk = Tkinter.DoubleVar() 667 self.logb_start_tk.set(0.1) 668 self.logb_stop_tk = Tkinter.DoubleVar() 669 self.logb_stop_tk.set(100.0) 670 self.logb_n_tk = Tkinter.IntVar() 671 self.logb_n_tk.set(5) 672 673 logb_frame = Tkinter.Frame( sequence_frame) 674 logb_frame.grid(row=seq_row,column=1) 675 Tkinter.Label(logb_frame,text="start:").grid(row=0,column=0) 676 Tkinter.Entry(logb_frame,textvariable=self.logb_start_tk,width=6).grid(row=0,column=1) 677 Tkinter.Label(logb_frame,text=" stop:").grid(row=0,column=2) 678 Tkinter.Entry(logb_frame,textvariable=self.logb_stop_tk,width=6).grid(row=0,column=3) 679 Tkinter.Label(logb_frame,text=" N:").grid(row=0,column=4) 680 Tkinter.Entry(logb_frame,textvariable=self.logb_n_tk,width=6).grid(row=0,column=5) 681 682 # rest duration frame 683 Tkinter.Label(rest_dur_frame, 684 text="Other sequence parameters", 685 font=("Helvetica",12,"bold"),).grid(row=0,column=0,columnspan=2) 686 687 Tkinter.Label(rest_dur_frame, 688 text="Interval duration (seconds)").grid(row=1,column=0) 689 self.rest_dur = Tkinter.DoubleVar() 690 self.rest_dur.set(0.5) 691 Tkinter.Entry(rest_dur_frame, 692 textvariable=self.rest_dur, 693 width=10).grid(row=1,column=1) 694 695 self.shuffle_tk_var = Tkinter.BooleanVar() 696 self.shuffle_tk_var.set(0) 697 Tkinter.Checkbutton( rest_dur_frame, 698 text="Shuffle sequence order", 699 variable=self.shuffle_tk_var).grid(row=2,column=0,columnspan=2) 700 701 if self.orig_values is not None: 702 self.var_name.set( self.orig_values.parameters.variable ) 703 704 self.sequence_manual_string.set( str(self.orig_values.parameters.sequence) ) 705 706 self.rest_dur.set( self.orig_values.parameters.rest_duration_sec )
707 708
709 - def validate(self):
710 if self.sequence_type.get() == "manual": 711 try: 712 seq = eval(self.sequence_manual_string.get()) 713 except Exception, x: 714 tkMessageBox.showwarning("Invalid sequence parameters", 715 "Manual sequence entry: %s"%(str(x),), 716 parent=self) 717 return 0 718 if type(seq) != types.ListType: 719 tkMessageBox.showwarning("Invalid sequence parameters", 720 "Manual sequence entry: Not a list", 721 parent=self) 722 return 0 723 elif self.sequence_type.get() == "linear": 724 start = self.lin_start_tk.get() 725 stop = self.lin_stop_tk.get() 726 n = self.lin_n_tk.get() 727 if n < 2: 728 tkMessageBox.showwarning("Invalid sequence parameters", 729 "Must have n >= 2.", 730 parent=self) 731 return 0 732 733 incr = (stop-start)/float(n-1) 734 seq = range(n) 735 for i in range(n): 736 seq[i] = i*incr + start 737 elif self.sequence_type.get() == "log": 738 start = self.log_start_tk.get() 739 stop = self.log_stop_tk.get() 740 n = self.log_n_tk.get() 741 if n < 2: 742 tkMessageBox.showwarning("Invalid sequence parameters", 743 "Must have n >= 2.", 744 parent=self) 745 return 0 746 747 incr = (stop-start)/float(n-1) 748 seq = range(n) 749 for i in range(n): 750 seq[i] = 10.0**( i*incr + start ) 751 elif self.sequence_type.get() == "logb": 752 start = self.logb_start_tk.get() 753 stop = self.logb_stop_tk.get() 754 start = math.log10(start) 755 stop = math.log10(stop) 756 n = self.logb_n_tk.get() 757 if n < 2: 758 tkMessageBox.showwarning("Invalid sequence parameters", 759 "Must have n >= 2.", 760 parent=self) 761 return 0 762 incr = (stop-start)/float(n-1) 763 seq = range(n) 764 for i in range(n): 765 seq[i] = 10.0**( i*incr + start ) 766 else: 767 tkMessageBox.showwarning("Invalid sequence parameters", 768 "Invalid sequence type.", 769 parent=self) 770 return 0 771 rest_dur_sec = self.rest_dur.get() 772 773 if self.shuffle_tk_var.get(): 774 random.shuffle(seq) 775 776 self.result = Loop(variable=self.var_name.get(), 777 sequence=seq, 778 rest_duration_sec=rest_dur_sec) 779 return 1
780
781 - def destroy(self):
782 # clear tk variables 783 self.var_name = None 784 self.sequence_type = None 785 self.sequence_manual_string = None 786 self.rest_dur = None 787 # call master's destroy method 788 tkSimpleDialog.Dialog.destroy(self)
789
790 -def get_server(hostname="",port=7766):
791 class ConnectWindow(Tkinter.Frame): 792 def __init__(self,master=None,hostname="",port=7766,**kw): 793 # Allow VisionEgg Tkinter exception window 794 VisionEgg.config._Tkinter_used = True 795 796 Tkinter.Frame.__init__(self,master, **kw) 797 self.winfo_toplevel().title("EPhysGUI Connect - Vision Egg") 798 current_row = 0 799 Tkinter.Message(self,\ 800 text='Welcome to the "EPhys GUI" of the Vision Egg!\n\n'+\ 801 'Please enter the hostname '+\ 802 'and port number '+\ 803 'of the computer on which you have the '+\ 804 '"EPhys server" running.').grid(row=current_row,column=0,columnspan=2) 805 hostname = 'localhost' 806 807 self.hostname_tk = Tkinter.StringVar() 808 self.hostname_tk.set(hostname) 809 current_row += 1 810 Tkinter.Label(self,text="Hostname:").grid(row=current_row, column=0) 811 Tkinter.Entry(self,textvariable=self.hostname_tk).grid(row=current_row, column=1) 812 813 self.port_tk = Tkinter.IntVar() 814 self.port_tk.set(port) 815 current_row += 1 816 Tkinter.Label(self,text="Port:").grid(row=current_row, column=0) 817 Tkinter.Entry(self,textvariable=self.port_tk).grid(row=current_row, column=1) 818 819 current_row += 1 820 bf = Tkinter.Frame(self) 821 bf.grid(row=current_row,column=0,columnspan=2) 822 ok=Tkinter.Button(bf,text="OK",command=self.ok) 823 ok.grid(row=0,column=0) 824 ok.focus_force() 825 ok.bind('<Return>',self.ok) 826 Tkinter.Button(bf,text="Cancel",command=self.quit).grid(row=0,column=1) 827 self.result = None
828 829 def ok(self,dummy_arg=None): 830 self.result = (self.hostname_tk.get(),self.port_tk.get()) 831 self.destroy() 832 self.quit() 833 834 connect_win = ConnectWindow(hostname=hostname,port=port) 835 connect_win.pack() 836 connect_win.mainloop() 837 return connect_win.result 838
839 -class GammaFrame(Tkinter.Frame):
840 - def __init__(self, 841 master=None, 842 ephys_server=None,**kw):
843 Tkinter.Frame.__init__(self,master,**kw) 844 self.winfo_toplevel().title("Gamma - Vision Egg") 845 self.ephys_server = ephys_server 846 847 self.columnconfigure(0,weight=1) 848 849 row = 0 850 Tkinter.Label(self, 851 font=("Helvetica",12,"bold"), 852 text="Load Gamma Table").grid(row=row) 853 854 row += 1 855 Tkinter.Button(self, 856 text="Set from .ve_gamma file...", 857 command=self.set_from_file).grid(row=row,sticky="w") 858 859 row += 1 860 Tkinter.Button(self, 861 text="Set to monitor default (linear gamma table)", 862 command=self.set_monitor_default).grid(row=row,sticky="w") 863 864 row += 1 865 invert_frame = Tkinter.Frame(self) 866 invert_frame.grid(row=row,sticky="we") 867 868 Tkinter.Button(invert_frame, 869 text="Linearize luminance for gammas", 870 command=self.linearize).grid(row=0,column=0) 871 872 Tkinter.Label(invert_frame, 873 text="Red:").grid(row=0,column=1) 874 875 self.red_gamma = Tkinter.DoubleVar() 876 self.red_gamma.set(2.2) 877 878 Tkinter.Entry(invert_frame, 879 textvariable=self.red_gamma, 880 width=5).grid(row=0,column=2) 881 882 Tkinter.Label(invert_frame, 883 text="Green:").grid(row=0,column=3) 884 885 self.green_gamma = Tkinter.DoubleVar() 886 self.green_gamma.set(2.2) 887 888 Tkinter.Entry(invert_frame, 889 textvariable=self.green_gamma, 890 width=5).grid(row=0,column=4) 891 892 Tkinter.Label(invert_frame, 893 text="Blue:").grid(row=0,column=5) 894 895 self.blue_gamma = Tkinter.DoubleVar() 896 self.blue_gamma.set(2.2) 897 898 Tkinter.Entry(invert_frame, 899 textvariable=self.blue_gamma, 900 width=5).grid(row=0,column=6) 901 902 row += 1 903 self.success_label = Tkinter.Label(self) 904 self.success_label.grid(row=row)
905
906 - def get_corrected_gamma_table(self,gamma):
907 # c is a constant scale factor. It is always 1.0 when 908 # luminance is normalized to range [0.0,1.0] and input units 909 # in range [0.0,1.0], as is OpenGL standard. 910 c = 1.0 911 inc = 1.0/255 912 target_luminances = numpy.arange(0.0,1.0+inc,inc) 913 output_ramp = numpy.zeros(target_luminances.shape,dtype=numpy.int32) 914 for i in range(len(target_luminances)): 915 L = target_luminances[i] 916 if L == 0.0: 917 v_88fp = 0 918 else: 919 v = math.exp( (math.log(L) - math.log(c)) /gamma) 920 v_88fp = int(round((v*255) * 256)) # convert to from [0.0,1.0] floating point to [0.0,255.0] 8.8 fixed point 921 output_ramp[i] = v_88fp # 8.8 fixed point format 922 return list(output_ramp) # convert to Python list
923
924 - def linearize(self, dummy_arg=None):
925 self.success_label.configure(text="Setting...") 926 try: 927 red = self.get_corrected_gamma_table(self.red_gamma.get()) 928 green = self.get_corrected_gamma_table(self.green_gamma.get()) 929 blue = self.get_corrected_gamma_table(self.blue_gamma.get()) 930 except: 931 self.success_label.configure(text="Calculation error") 932 raise 933 try: 934 if self.ephys_server.set_gamma_ramp(red,green,blue): 935 self.success_label.configure(text="Success") 936 else: 937 self.success_label.configure(text="Failed: Invalid gamma values?") 938 except Exception,x: 939 self.success_label.configure(text="Failed: %s: %s"%(x.__class__,str(x))) 940 raise
941
942 - def set_monitor_default(self, dummy_arg=None):
943 self.success_label.configure(text="Setting...") 944 try: 945 red = self.get_corrected_gamma_table(1.0) # linear gamma table 946 except: 947 self.success_label.configure(text="Calculation error") 948 raise 949 green = red 950 blue = red 951 try: 952 if self.ephys_server.set_gamma_ramp(red,green,blue): 953 self.success_label.configure(text="Success") 954 else: 955 self.success_label.configure(text="Failed: Invalid gamma values?") 956 except Exception,x: 957 self.success_label.configure(text="Failed: %s: %s"%(x.__class__,str(x))) 958 raise
959
960 - def set_from_file(self):
961 self.success_label.configure(text="Setting...") 962 filename = tkFileDialog.askopenfilename( 963 parent=self, 964 defaultextension=".ve_gamma", 965 filetypes=[('Configuration file','*.ve_gamma')], 966 initialdir=VisionEgg.config.VISIONEGG_USER_DIR) 967 if not filename: 968 self.success_label.configure(text="No file given") 969 return 970 fd = open(filename,"r") 971 gamma_values = [] 972 for line in fd.readlines(): 973 line = line.strip() # remove leading/trailing whitespace 974 if line.startswith("#"): # comment, ignore 975 continue 976 try: 977 gamma_values.append( map(int, line.split() ) ) 978 except Exception, x: 979 self.success_label.configure(text="File error") 980 raise 981 if len(gamma_values[-1]) != 3: 982 self.success_label.configure(text="File error") 983 raise RuntimeError("expected 3 values per gamma entry") 984 if len(gamma_values) != 256: 985 self.success_label.configure(text="File error") 986 raise RuntimeError("expected 256 gamma entries") 987 red, green, blue = zip(*gamma_values) 988 try: 989 if self.ephys_server.set_gamma_ramp(red,green,blue): 990 self.success_label.configure(text="Success") 991 else: 992 self.success_label.configure(text="Failed: Invalid gamma values?") 993 except Exception,x: 994 self.success_label.configure(text="Failed: %s: %s"%(x.__class__,str(x))) 995 raise
996
997 -class ImageSequenceLauncher(Tkinter.Toplevel):
998 - def __init__(self,master=None,ephys_server=None,**cnf):
999 Tkinter.Toplevel.__init__(self,master,**cnf) 1000 if ephys_server is None: 1001 raise ValueError("Must specify ephys_server") 1002 self.ephys_server = ephys_server 1003 1004 self.columnconfigure(1,weight=1) 1005 1006 row = 0 1007 Tkinter.Label(self,text="Frames per second").grid(row=row,column=0) 1008 self.fps_var = Tkinter.DoubleVar() 1009 self.fps_var.set(12.0) 1010 Tkinter.Entry(self,textvariable=self.fps_var).grid(row=row,column=1,sticky="we") 1011 row += 1 1012 Tkinter.Label(self,text="Filename base").grid(row=row,column=0) 1013 self.filename_base = Tkinter.StringVar() 1014 self.filename_base.set("im") 1015 Tkinter.Entry(self,textvariable=self.filename_base).grid(row=row,column=1,sticky="we") 1016 row += 1 1017 Tkinter.Label(self,text="Filename suffix").grid(row=row,column=0) 1018 self.filename_suffix = Tkinter.StringVar() 1019 self.filename_suffix.set(".tif") 1020 Tkinter.Entry(self,textvariable=self.filename_suffix).grid(row=row,column=1,sticky="we") 1021 row += 1 1022 Tkinter.Label(self,text="Save directory on server").grid(row=row,column=0) 1023 self.server_save_dir = Tkinter.StringVar() 1024 server_dir = self.ephys_server.get_cwd() 1025 self.server_save_dir.set(server_dir) 1026 Tkinter.Entry(self,textvariable=self.server_save_dir).grid(row=row,column=1,sticky="we") 1027 row += 1 1028 Tkinter.Button(self,text="Save movie",command=self.do_it).grid(row=row,column=0,columnspan=2) 1029 self.focus_set() 1030 self.grab_set()
1031 - def do_it(self):
1032 fps = self.fps_var.get() 1033 filename_base = self.filename_base.get() 1034 filename_suffix = self.filename_suffix.get() 1035 server_save_dir = self.server_save_dir.get() 1036 self.ephys_server.save_image_sequence(fps=fps, 1037 filename_base=filename_base, 1038 filename_suffix=filename_suffix, 1039 save_dir=server_save_dir) 1040 self.destroy()
1041
1042 -class AppWindow(Tkinter.Frame):
1043 - def __init__(self, 1044 master=None, 1045 client_list=None, 1046 server_hostname='', 1047 server_port=7766, 1048 **cnf):
1049 1050 if hasattr(VisionEgg, '_exception_hook_keeper'): 1051 # Keep original exception handler 1052 self._orig_report_callback_exception = Tkinter.Tk.report_callback_exception 1053 self._tk = Tkinter.Tk 1054 # Use Vision Egg exception handler 1055 Tkinter.Tk.report_callback_exception = VisionEgg._exception_hook_keeper.handle_exception 1056 1057 # Allow VisionEgg Tkinter exception window 1058 VisionEgg.config._Tkinter_used = True 1059 1060 # create myself 1061 Tkinter.Frame.__init__(self,master, **cnf) 1062 self.winfo_toplevel().title("EPhysGUI - Vision Egg") 1063 1064 self.client_list = client_list 1065 1066 self.server_hostname = server_hostname 1067 self.server_port = server_port 1068 1069 self.pyro_client = VisionEgg.PyroClient.PyroClient(self.server_hostname,self.server_port) 1070 self.ephys_server = self.pyro_client.get("ephys_server") 1071 self.ephys_server.first_connection() 1072 1073 self.stim_onset_cal_tk_var = Tkinter.BooleanVar() 1074 self.stim_onset_cal_tk_var.set(0) 1075 1076 self.autosave_dir = Tkinter.StringVar() 1077 self.autosave_dir.set( os.path.abspath(os.curdir) ) 1078 1079 self.autosave_basename = Tkinter.StringVar() 1080 1081 # create menu bar 1082 self.bar = Tkinter.Menu(tearoff=0) 1083 top = self.winfo_toplevel() 1084 top.configure(menu=self.bar) 1085 1086 self.bar.file_menu = Tkinter.Menu(self.bar, name="file_menu") 1087 self.bar.add_cascade(label="File",menu=self.bar.file_menu) 1088 1089 self.bar.file_menu.add_command(label='Save image sequence...', command=self.save_image_sequence) 1090 self.bar.file_menu.add_command(label='Save configuration file...', command=self.save_config) 1091 self.bar.file_menu.add_command(label='Load configuration file...', command=self.load_config) 1092 self.bar.file_menu.add_command(label='Load auto-saved .py parameter file...', command=self.load_params) 1093 self.bar.file_menu.add_separator() 1094 self.bar.file_menu.add_command(label='Load Vision Egg script...', command=self.load_demoscript) 1095 self.bar.file_menu.add_separator() 1096 1097 self.quit_server_too = Tkinter.BooleanVar() 1098 self.quit_server_too.set(1) 1099 self.bar.file_menu.add_checkbutton(label='Quit server too', 1100 variable=self.quit_server_too) 1101 self.bar.file_menu.add_command(label='Quit', 1102 command=self.quit, 1103 ) 1104 1105 stimkey = self.ephys_server.get_stimkey() 1106 self.stimulus_tk_var = Tkinter.StringVar() 1107 self.stimulus_tk_var.set( stimkey ) 1108 1109 self.bar.stimuli_menu = Tkinter.Menu(self.bar, name="stimuli_menu") 1110 self.bar.add_cascade(label="Stimuli",menu=self.bar.stimuli_menu) 1111 for maybe_stimkey, maybe_control_frame, maybe_title in self.client_list: 1112 if maybe_title != "Vision Egg Script": 1113 self.bar.stimuli_menu.add_radiobutton(label=maybe_title, 1114 command=self.change_stimulus, 1115 variable=self.stimulus_tk_var, 1116 value=maybe_stimkey) 1117 1118 self.bar.calibration_menu = Tkinter.Menu(self.bar, name="calibration_menu") 1119 self.bar.add_cascade(label="Configure/Calibrate", 1120 menu=self.bar.calibration_menu) 1121 1122 self.bar.calibration_menu.add_command(label='3D Perspective...', command=self.launch_screen_pos) 1123 self.bar.calibration_menu.add_command(label='Stimulus onset timing...', command=self.launch_stim_onset_cal) 1124 self.bar.calibration_menu.add_command(label='Load gamma table...', command=self.launch_gamma_panel) 1125 self.notify_on_dropped_frames = Tkinter.BooleanVar() 1126 self.notify_on_dropped_frames.set(1) 1127 self.bar.calibration_menu.add_checkbutton(label='Warn on frame skip', 1128 variable=self.notify_on_dropped_frames) 1129 1130 self.override_t_abs_sec = Tkinter.StringVar() # Tkinter DoubleVar loses precision 1131 self.override_t_abs_sec.set("0.0") 1132 1133 self.override_t_abs_on = Tkinter.BooleanVar() 1134 self.override_t_abs_on.set(0) 1135 self.bar.calibration_menu.add_checkbutton(label='Override server absolute time (CAUTION)', 1136 variable=self.override_t_abs_on) 1137 1138 row = 0 1139 1140 # options for self.stim_frame in grid layout manager 1141 self.stim_frame_cnf = {'row':row, 1142 'column':0, 1143 'columnspan':2, 1144 'sticky':'nwes'} 1145 1146 row += 1 1147 Tkinter.Label(self, 1148 text="Sequence information", 1149 font=("Helvetica",12,"bold")).grid(row=row,column=0) 1150 row += 1 1151 # options for self.loop_frame in grid layout manager 1152 self.loop_frame_cnf = {'row':row, 1153 'column':0, 1154 'sticky':'nwes'} 1155 1156 row -= 1 1157 Tkinter.Label(self, 1158 text="Parameter Save Options", 1159 font=("Helvetica",12,"bold")).grid(row=row,column=1) 1160 row += 1 1161 self.auto_save_frame = Tkinter.Frame(self) 1162 asf = self.auto_save_frame # shorthand 1163 asf.grid(row=row,column=1,sticky="nwes") 1164 asf.columnconfigure(1,weight=1) 1165 1166 asf.grid_row = 0 1167 self.autosave = Tkinter.BooleanVar() 1168 self.autosave.set(1) 1169 self.auto_save_button = Tkinter.Checkbutton(asf, 1170 text="Auto save trial parameters", 1171 variable=self.autosave) 1172 self.auto_save_button.grid(row=asf.grid_row,column=0,columnspan=2) 1173 1174 self.param_file_type_tk_var = Tkinter.StringVar() 1175 self.param_file_type_tk_var.set("Python format") 1176