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 filetype_bar = Tkinter.Menubutton(asf, 1177 textvariable=self.param_file_type_tk_var, 1178 relief=Tkinter.RAISED) 1179 filetype_bar.grid(row=asf.grid_row,column=2) 1180 filetype_bar.menu = Tkinter.Menu(filetype_bar,tearoff=0) 1181 filetype_bar.menu.add_radiobutton(label="Python format", 1182 value="Python format", 1183 variable=self.param_file_type_tk_var) 1184 filetype_bar.menu.add_radiobutton(label="Matlab format", 1185 value="Matlab format", 1186 variable=self.param_file_type_tk_var) 1187 filetype_bar['menu'] = filetype_bar.menu 1188 1189 asf.grid_row += 1 1190 Tkinter.Label(asf, 1191 text="Parameter file directory:").grid(row=asf.grid_row,column=0,sticky="e") 1192 Tkinter.Entry(asf, 1193 textvariable=self.autosave_dir).grid(row=asf.grid_row,column=1,sticky="we") 1194 Tkinter.Button(asf, 1195 text="Set...",command=self.set_autosave_dir).grid(row=asf.grid_row,column=2) 1196 asf.grid_row += 1 1197 Tkinter.Label(asf, 1198 text="Parameter file basename:").grid(row=asf.grid_row,column=0,sticky="e") 1199 Tkinter.Entry(asf, 1200 textvariable=self.autosave_basename).grid(row=asf.grid_row,column=1,sticky="we") 1201 Tkinter.Button(asf, 1202 text="Reset",command=self.reset_autosave_basename).grid(row=asf.grid_row,column=2) 1203 1204 row += 1 1205 Tkinter.Button(self, text='Do single trial', command=self.do_single_trial).grid(row=row,column=0) 1206 Tkinter.Button(self, text='Do sequence', command=self.do_loops).grid(row=row,column=1) 1207 1208 row += 1 1209 self.progress = VisionEgg.GUI.ProgressBar(self, 1210 width=300, 1211 relief="sunken", 1212 doLabel=0, 1213 labelFormat="%s") 1214 self.progress.labelText = "Starting..." 1215 self.progress.updateProgress(0) 1216 self.progress.grid(row=row,column=0,columnspan=2)#,sticky='we') 1217 1218 # Allow rows and columns to expand 1219 for i in range(2): 1220 self.columnconfigure(i,weight=1) 1221 for i in range(row+1): 1222 self.rowconfigure(i,weight=1) 1223 1224 self.switch_to_stimkey( stimkey ) 1225 1226 self.config_dir = None 1227 self.demoscript_filename = None 1228 self.vars_list = None
1229
1230 - def __del__( self ):
1231 if hasattr(self,'_orig_report_callback_exception'): 1232 self._tk.report_callback_exception = self._orig_report_callback_exception
1233
1234 - def switch_to_stimkey( self, stimkey ):
1235 success = 0 1236 for maybe_stimkey, maybe_control_frame, maybe_title in self.client_list: 1237 if stimkey == maybe_stimkey: 1238 control_frame_klass = maybe_control_frame 1239 success = 1 1240 1241 if not success: 1242 raise RuntimeError("Could not find valid client for server stimkey %s"%stimkey) 1243 1244 if hasattr(self, 'stim_frame'): 1245 # clear old frame 1246 self.stim_frame.destroy() 1247 del self.stim_frame 1248 1249 self.stim_frame = control_frame_klass(self,suppress_go_buttons=1) 1250 if stimkey == "dropin_server": 1251 self.stim_frame.gen_var_widgets(self.demoscript_filename, self.vars_list) 1252 self.stim_frame.connect(self.server_hostname,self.server_port) 1253 self.stim_frame.grid( **self.stim_frame_cnf ) 1254 1255 global loopable_variables 1256 loopable_variables = self.stim_frame.get_loopable_variable_names() 1257 if hasattr(self, 'loop_frame'): 1258 # clear old frame 1259 self.loop_frame.destroy() 1260 del self.loop_frame 1261 self.loop_frame = ScrollListFrame(master=self, 1262 container_class=LoopContainedObject) 1263 self.loop_frame.grid( **self.loop_frame_cnf ) 1264 1265 self.autosave_basename.set( self.stim_frame.get_shortname() ) 1266 1267 self.stimulus_tk_var.set( self.stim_frame.get_shortname() ) # set menuitem 1268 1269 self.progress.labelText = "Ready" 1270 self.progress.updateProgress(0)
1271
1272 - def change_stimulus(self, dummy_arg=None, new_stimkey=None ):
1273 # if new_stimkey is None, get from the tk variable 1274 if new_stimkey is None: 1275 new_stimkey = self.stimulus_tk_var.get() 1276 1277 found = 0 1278 for maybe_stimkey, maybe_control_frame, maybe_title in self.client_list: 1279 if new_stimkey == maybe_stimkey: 1280 new_control_frame_klass = maybe_control_frame 1281 new_stimkey = maybe_stimkey 1282 found = 1 1283 break 1284 1285 if not found: 1286 raise RuntimeError("Don't know about stimkey %s"%new_stimkey) 1287 1288 if new_control_frame_klass != self.stim_frame.__class__ or new_stimkey == "dropin_server": 1289 # make wait cursor 1290 root = self.winfo_toplevel() 1291 old_cursor = root["cursor"] 1292 root["cursor"] = "watch" 1293 root.update() 1294 try: 1295 1296 self.progress.labelText = "Changing stimulus..." 1297 self.progress.updateProgress(0) 1298 1299 self.ephys_server.set_next_stimkey( new_stimkey ) 1300 1301 # new stimulus type 1302 self.stim_frame.quit_server() # disconnect 1303 1304 # in case of loaded Vision Egg script, quit_server() 1305 # sends a signal to stop the "wait" loop, WITHOUT 1306 # raising a flag to run the script 1307 1308 self.ephys_server.get_stimkey() # wait for server to load 1309 1310 self.switch_to_stimkey( new_stimkey) 1311 finally: 1312 #restore cursor 1313 root["cursor"] = old_cursor 1314 root.update()
1315
1316 - def save_image_sequence(self):
1317 ImageSequenceLauncher(self,ephys_server=self.ephys_server)
1318
1319 - def save_config(self):
1320 self.stim_frame.send_values() # copy values from Tkinter to self.stim_frame.meta_params and send to server 1321 if self.config_dir is not None: 1322 initialdir = self.config_dir 1323 else: 1324 initialdir = VisionEgg.config.VISIONEGG_USER_DIR 1325 filename = tkFileDialog.asksaveasfilename( 1326 parent=self, 1327 defaultextension=".ve_cfg", 1328 filetypes=[('Configuration file','*.ve_cfg')], 1329 initialdir=initialdir) 1330 if not filename: 1331 return 1332 fd = open(filename,"wb") 1333 save_dict = {'stim_type':self.stim_frame.get_shortname(), 1334 'loop_list':self.loop_frame.list, 1335 'stim_frame_dict':self.stim_frame.get_parameters_dict(), 1336 'autosave':self.autosave.get(), 1337 'autosave_dir':self.autosave_dir.get(), 1338 'autosave_basename':self.autosave_basename.get()} 1339 pickle.dump( save_dict, fd ) 1340 self.config_dir = os.path.split(filename)[0] # get dir
1341
1342 - def load_config(self):
1343 if self.config_dir is not None: 1344 initialdir = self.config_dir 1345 else: 1346 initialdir = VisionEgg.config.VISIONEGG_USER_DIR 1347 filename = tkFileDialog.askopenfilename( 1348 parent=self, 1349 defaultextension=".ve_cfg", 1350 filetypes=[('Configuration file','*.ve_cfg')], 1351 initialdir=initialdir, 1352 ) 1353 if not filename: 1354 return 1355 self.config_dir = os.path.split(filename)[0] # get dir 1356 fd = open(filename,"rb") 1357 file_contents = fd.read() 1358 file_contents = file_contents.replace('\r\n','\n') # deal with Windows newlines 1359 memory_file = StringIO.StringIO(file_contents) 1360 load_dict = pickle.load(memory_file) 1361 if load_dict['stim_type'] != self.stim_frame.get_shortname(): 1362 self.change_stimulus(new_stimkey=load_dict['stim_type']+"_server") 1363 self.loop_frame.list = load_dict['loop_list'] 1364 self.loop_frame.update_now() 1365 self.stim_frame.set_parameters_dict( load_dict['stim_frame_dict'] ) 1366 self.autosave.set(load_dict['autosave']) 1367 self.autosave_dir.set(load_dict['autosave_dir']) 1368 self.autosave_basename.set(load_dict['autosave_basename']) 1369 1370 self.stim_frame.update_tk_vars()
1371
1372 - def load_params(self,orig_load_dict={}):
1373 filename = tkFileDialog.askopenfilename( 1374 parent=self, 1375 defaultextension=".py", 1376 filetypes=[('Auto-saved parameter file','*.py')]) 1377 if not filename: 1378 return 1379 locals = {} 1380 load_dict = orig_load_dict.copy() # make copy of default values 1381 execfile(filename,locals,load_dict) # execute the file 1382 if load_dict['stim_type'] != self.stim_frame.get_shortname(): 1383 self.change_stimulus(new_stimkey=load_dict['stim_type']+"_server") 1384 self.loop_frame.list = [] # clear loop list 1385 self.loop_frame.update_now() 1386 new_params = {} 1387 exception_info = [] 1388 for param_name in dir(self.stim_frame.meta_params): 1389 if param_name[:2] != "__": 1390 try: 1391 new_params[param_name] = load_dict[param_name] 1392 except Exception, x: 1393 exception_info.append(sys.exc_info()) # don't die on exception 1394 else: 1395 del load_dict[param_name] 1396 for exc_type, exc_value, exc_traceback in exception_info: 1397 # ignore actual traceback 1398 VisionEgg.GUI.showexception(exc_type,exc_value,"") 1399 self.stim_frame.set_parameters_dict( new_params ) 1400 self.autosave_basename.set(load_dict['stim_type']) 1401 1402 try: 1403 override_t_abs_sec = load_dict['go_loop_start_time_abs_sec'] 1404 except Exception, x: 1405 tkMessageBox.showwarning("No absolute time parameter", 1406 "No absolute time parameter when loading file", 1407 parent=self) 1408 else: 1409 if not self.override_t_abs_on.get(): 1410 self.override_t_abs_on.set(1) 1411 tkMessageBox.showwarning("Overriding absolute time parameter", 1412 "Overriding absolute time parameter on server. "+\ 1413 "Remember to turn this option off (in Configure "+\ 1414 "/ Calibrate menu) when done.", 1415 parent=self) 1416 self.override_t_abs_sec.set(repr(override_t_abs_sec)) # make string 1417 1418 self.stim_frame.update_tk_vars() 1419 return load_dict # return unused variables
1420
1421 - def load_demoscript(self):
1422 self.demoscript_filename = tkFileDialog.askopenfilename( 1423 parent=self, 1424 defaultextension=".py", 1425 filetypes=[('Vision Egg Demo Script','*.py')]) 1426 1427 if not self.demoscript_filename: 1428 return 1429 else: 1430 # make wait cursor 1431 root = self.winfo_toplevel() 1432 old_cursor = root["cursor"] 1433 root["cursor"] = "watch" 1434 root.update() 1435 try: 1436 fd1 = open(self.demoscript_filename, 'r') 1437 demoscript = "" 1438 1439 # List of all variables. 1440 # Each variable is a tuple of the form [x, y, z] where: 1441 # x: variable type ID, 1442 # y: variable name, 1443 # z: variable value. 1444 self.vars_list = [] 1445 1446 lines = fd1.read().splitlines() 1447 keep_lines = [] 1448 while len(lines): 1449 myString = lines.pop(0) 1450 if myString.find("get_default_screen()") != -1: 1451 append_flag = 0 1452 elif myString.find("watch_exceptions()") != -1: 1453 append_flag = 0 1454 elif myString.find("start_default_logging()") != -1: 1455 append_flag = 0 1456 elif myString.find("#%f") == 0: 1457 self.vars_list.append([VarTypes.getID("float"), myString.replace("#%f", "").split()[0]]) 1458 append_flag = 0 1459 elif myString.find("#%i") == 0: 1460 self.vars_list.append([VarTypes.getID("integer"), myString.replace("#%i", "").split()[0]]) 1461 append_flag = 0 1462 elif myString.find("#%s") == 0: 1463 self.vars_list.append([VarTypes.getID("string"), myString.replace("#%s", "").split()[0]]) 1464 append_flag = 0 1465 else: 1466 append_flag = 1 1467 1468 if append_flag == 1: 1469 keep_lines.append( myString ) 1470 1471 fd1.close() 1472 demoscript = '\n'.join(keep_lines) 1473 1474 # Client side AST. Only used to extract default values 1475 # of elected variables: 1476 try: 1477 AST = parser.suite(demoscript) 1478 1479 for var in self.vars_list: 1480 var_val = AST_ext.extract_from_AST(AST, var[1]) 1481 if var[0] == VarTypes.getID("string"): 1482 var_val = var_val[1:len(var_val) - 1] 1483 var.append(var_val) 1484 1485 del AST # save memory 1486 1487 # unfortunately, sending an AST object over a Pyro 1488 # connection has its complications... so we don't 1489 # do it 1490 self.ephys_server.build_AST(demoscript) 1491 1492 self.change_stimulus(new_stimkey="dropin_server") 1493 1494 except (parser.ParserError, SyntaxError): 1495 tkMessageBox.showerror("Error", "Invalid demo script!") 1496 err_fd = file('/home/astraw/tmp/err.py',mode='w') 1497 err_fd.write(demoscript) 1498 finally: 1499 #restore cursor 1500 root["cursor"] = old_cursor 1501 root.update()
1502
1503 - def launch_screen_pos(self, dummy_arg=None):
1504 dialog = Tkinter.Toplevel(self) 1505 frame = VisionEgg.PyroApps.ScreenPositionGUI.ScreenPositionControlFrame(dialog, 1506 auto_connect=1, 1507 server_hostname=self.server_hostname, 1508 server_port=self.server_port) 1509 frame.winfo_toplevel().title("3D Calibration - Vision Egg") 1510 frame.pack(expand=1,fill=Tkinter.BOTH)
1511
1512 - def launch_stim_onset_cal(self, dummy_arg=None):
1513 dialog = Tkinter.Toplevel(self) 1514 frame = Tkinter.Frame(dialog) 1515 frame.winfo_toplevel().title("Timing Calibration - Vision Egg") 1516 Tkinter.Label(frame, 1517 font=("Helvetica",12,"bold"), 1518 text="Stimulus onset timing").grid(row=0,column=0) 1519 Tkinter.Label(frame, 1520 text="Use a light detector to verify the onset of a trial." 1521 ).grid(row=1,column=0) 1522 Tkinter.Checkbutton( frame, 1523 text="Black box (always) with white box (during trial)", 1524 variable=self.stim_onset_cal_tk_var, 1525 command=self.update_stim_onset_cal).grid(row=2,column=0) 1526 1527 x,y,width,height = self.ephys_server.get_stim_onset_cal_location() 1528 1529 location_frame = Tkinter.Frame(frame) 1530 location_frame.grid(row=3,column=0) 1531 self.stim_onset_x = Tkinter.DoubleVar() 1532 self.stim_onset_x.set(x) 1533 self.stim_onset_y = Tkinter.DoubleVar() 1534 self.stim_onset_y.set(y) 1535 self.stim_onset_width = Tkinter.DoubleVar() 1536 self.stim_onset_width.set(width) 1537 self.stim_onset_height = Tkinter.DoubleVar() 1538 self.stim_onset_height.set(height) 1539 1540 Tkinter.Label( location_frame, text="Center X:").grid(row=0,column=0) 1541 Tkinter.Entry( location_frame, textvariable=self.stim_onset_x,width=5).grid(row=0,column=1) 1542 Tkinter.Label( location_frame, text="Center Y:").grid(row=0,column=2) 1543 Tkinter.Entry( location_frame, textvariable=self.stim_onset_y,width=5).grid(row=0,column=3) 1544 Tkinter.Label( location_frame, text="Width:").grid(row=1,column=0) 1545 Tkinter.Entry( location_frame, textvariable=self.stim_onset_width,width=5).grid(row=1,column=1) 1546 Tkinter.Label( location_frame, text="Height:").grid(row=1,column=2) 1547 Tkinter.Entry( location_frame, textvariable=self.stim_onset_height,width=5).grid(row=1,column=3) 1548 1549 Tkinter.Button( frame, 1550 text="update position and size", 1551 command=self.set_stim_onset_cal_position).grid(row=4,column=0) 1552 self.set_stim_onset_cal_position() # call it once to send server our initial values 1553 frame.pack(expand=1,fill=Tkinter.BOTH)
1554
1555 - def launch_gamma_panel(self, dummy_arg=None):
1556 dialog = Tkinter.Toplevel(self) 1557 frame = GammaFrame(dialog, 1558 self.ephys_server) 1559 frame.pack(expand=1,fill=Tkinter.BOTH)
1560
1561 - def set_autosave_dir(self):
1562 self.autosave_dir.set( os.path.abspath( tkFileDialog.askdirectory() ) )
1563
1564 - def reset_autosave_basename(self):
1565 self.autosave_basename.set( self.stim_frame.get_shortname() )
1566
1567 - def update_stim_onset_cal(self, dummy_arg=None):
1568 on = self.stim_onset_cal_tk_var.get() 1569 self.ephys_server.set_stim_onset_cal(on)
1570
1571 - def set_stim_onset_cal_position(self, dummy_arg=None):
1572 x = self.stim_onset_x.get() 1573 y = self.stim_onset_y.get() 1574 width = self.stim_onset_width.get() 1575 height = self.stim_onset_height.get() 1576 self.ephys_server.set_stim_onset_cal_location(center=(x,y),size=(width,height))
1577 1578
1579 - def do_loops(self):
1580 super_loop_list = self.loop_frame.get_list_uncontained() 1581 # "super_loop_list" is a list of lists, each of which, a 1582 # "loop_list", contains "loop" class objects 1583 # eg. [[a], [b, c], [d]] 1584 # Need to test that "get_list_uncontained()" is returning a list of lists! 1585 global need_rest_period 1586 need_rest_period = 0 1587 1588 if not len(super_loop_list): 1589 return 1590 1591 ############################################################ 1592 def process_loops(depth): # recursive processing of loops 1593 1594 class LoopInfoFrame(Tkinter.Frame): 1595 def __init__(self, master=None, **kw): 1596 Tkinter.Frame.__init__(self,master,**kw) 1597 Tkinter.Label(self, 1598 text="Doing sequence").grid(row=0,column=0) 1599 self.status_tk_var = Tkinter.StringVar() 1600 Tkinter.Label(self, 1601 textvariable = self.status_tk_var).grid(row=1, column=0) 1602 self.cancel_asap = 0 1603 Tkinter.Button(self, 1604 text="Cancel",command=self.cancel).grid(row=2,column=0) 1605 self.focus_set() 1606 self.grab_set()
1607 def cancel(self, dummy_arg=None): 1608 self.cancel_asap = 1
1609 1610 global need_rest_period 1611 1612 global loop_info_frame 1613 if depth == 0: # only make one LoopInfoFrame (when depth is 0, ie. first time) 1614 top = Tkinter.Toplevel(self) 1615 loop_info_frame = LoopInfoFrame(top) 1616 loop_info_frame.pack() 1617 1618 loop_list = super_loop_list[depth] 1619 #print "current depth" 1620 #print depth 1621 #print "Loop?" 1622 #print loop 1623 max_depth = len(super_loop_list)-1 1624 #print "max_depth" 1625 #print max_depth 1626 1627 loop = loop_list[0].get_contained() 1628 # "loop_list" is, for example: [a, b, c] 1629 # ie. each element is an object of class "loop". 1630 # If one element is done, then all other elements are effectively done, the way we've structured the lists to 1631 # be (they all have same 'N' value). 1632 1633 while not loop.is_done() and not loop_info_frame.cancel_asap: 1634 for loop_element in loop_list: 1635 if loop_element.get_contained().parameters.variable != "<repeat>": 1636 self.stim_frame.set_loopable_variable(loop_element.get_contained().parameters.variable,loop_element.get_contained().get_current()) 1637 if depth < max_depth: 1638 process_loops(depth+1) 1639 elif depth == max_depth: # deepest level -- do the trial 1640 if need_rest_period: 1641 self.progress.labelText = "Resting" 1642 self.sleep_with_progress(loop.parameters.rest_duration_sec) 1643 self.do_single_trial() 1644 need_rest_period = 1 1645 else: 1646 raise RuntimeError("Called with max_depth==-1:") 1647 for loop_element in loop_list: 1648 loop_element.get_contained().advance() 1649 for loop_element in loop_list: 1650 loop_element.get_contained().reset() 1651 if depth == 0: # destroy LoopInfoFrame 1652 top.destroy() 1653 1654 ############################################################ 1655 process_loops(0) # start recursion on top level 1656
1657 - def do_single_trial(self):
1658 # Get filename to save parameters 1659 if not self.autosave.get(): 1660 file_stream = None # not saving file 1661 else: 1662 duration_sec = self.stim_frame.get_duration_sec() 1663 (year,month,day,hour24,min,sec) = time.localtime(time.time()+duration_sec)[:6] 1664 trial_time_str = "%04d%02d%02d_%02d%02d%02d"%(year,month,day,hour24,min,sec) 1665 if self.param_file_type_tk_var.get() == "Python format": 1666 # Figure out filename to save parameters in 1667 filename = self.autosave_basename.get() + trial_time_str + "_params.py" 1668 fullpath_filename = os.path.join( self.autosave_dir.get(), filename) 1669 file_stream = open(fullpath_filename,"w") 1670 elif self.param_file_type_tk_var.get() == "Matlab format": 1671 # Figure out filename to save results in 1672 filename = self.autosave_basename.get() + trial_time_str + "_params.m" 1673 fullpath_filename = os.path.join( self.autosave_dir.get(), filename) 1674 file_stream = open(fullpath_filename,"w") 1675 else: 1676 raise ValueError('Unknown file format: "%s"'%(self.param_file_type_tk_var.get(),)) 1677 1678 # this class is broken into parts so it can be subclassed more easily 1679 self.do_single_trial_pre(file_stream) 1680 self.do_single_trial_work() 1681 self.do_single_trial_post(file_stream) 1682 1683 # Close parameter save file 1684 if self.autosave.get(): 1685 file_stream.close()
1686
1687 - def do_single_trial_pre(self, file_stream):
1688 # Ensure that we have the most up-to-date values 1689 self.stim_frame.send_values() # copy values from Tkinter to self.stim_frame.meta_params and send to server 1690 1691 # if file_stream is None, open default file 1692 if self.autosave.get(): 1693 duration_sec = self.stim_frame.get_duration_sec() 1694 (year,month,day,hour24,min,sec) = time.localtime(time.time()+duration_sec)[:6] 1695 if self.param_file_type_tk_var.get() == "Python format": 1696 file_stream.write("stim_type = '%s'\n"%self.stim_frame.get_shortname()) 1697 file_stream.write("finished_time = %04d%02d%02d%02d%02d%02d\n"%(year,month,day,hour24,min,sec)) 1698 parameter_list = self.stim_frame.get_parameters_as_py_strings() 1699 for parameter_name, parameter_value in parameter_list: 1700 file_stream.write("%s = %s\n"%(parameter_name, parameter_value)) 1701 elif self.param_file_type_tk_var.get() == "Matlab format": 1702 file_stream.write("stim_type = '%s';\n"%self.stim_frame.get_shortname()) 1703 file_stream.write("finished_time = %04d%02d%02d%02d%02d%02d;\n"%(year,month,day,hour24,min,sec)) 1704 parameter_list = self.stim_frame.get_parameters_as_m_strings() 1705 for parameter_name, parameter_value in parameter_list: 1706 file_stream.write("%s = %s;\n"%(parameter_name, parameter_value)) 1707 else: 1708 raise RuntimeError("Unknown parameter file type") # Should never get here
1709
1710 - def do_single_trial_work(self):
1711 # make wait cursor 1712 root = self.winfo_toplevel() 1713 self.old_cursor = root["cursor"] 1714 root["cursor"] = "watch" 1715 root.update() 1716 try: 1717 1718 self.progress.labelText = "Doing trial..." 1719 self.progress.updateProgress(0) 1720 1721 duration_sec = self.stim_frame.get_duration_sec() 1722 if duration_sec > 0: 1723 if self.override_t_abs_on.get(): 1724 new_t_abs_str = self.override_t_abs_sec.get() 1725 self.ephys_server.set_override_t_abs_sec( new_t_abs_str ) 1726 1727 self.stim_frame.go() # start server going, but this return control immediately 1728 self.sleep_with_progress(duration_sec) 1729 while self.ephys_server.is_in_go_loop(): # make sure go loop is really done 1730 time.sleep(0.1) # wait 100 msec for end of go loop and try again 1731 1732 if self.notify_on_dropped_frames.get(): 1733 if self.ephys_server.were_frames_dropped_in_last_go_loop(): 1734 tkMessageBox.showwarning("Dropped frame(s)", 1735 "During the last trial, at least 1 frame was dropped.", 1736 parent=self) 1737 else: 1738 if self.stim_frame.send_values(): 1739 self.ephys_server.run_demoscript() 1740 self.stim_frame.quit_server() 1741 finally: 1742 root["cursor"] = self.old_cursor 1743 root.update() 1744 1745 # restore status bar 1746 self.progress.labelText = "Ready" 1747 self.progress.updateProgress(0)
1748
1749 - def do_single_trial_post(self, file_stream):
1750 # if file_stream is None, open default file 1751 if self.autosave.get(): 1752 frames_dropped = self.ephys_server.were_frames_dropped_in_last_go_loop() 1753 go_loop_start_time = self.ephys_server.get_last_go_loop_start_time_absolute_sec() 1754 if self.param_file_type_tk_var.get() == "Python format": 1755 file_stream.write("frames_dropped = %s # boolean\n"%str(frames_dropped)) 1756 file_stream.write("go_loop_start_time_abs_sec = %s\n"%repr(go_loop_start_time)) 1757 elif self.param_file_type_tk_var.get() == "Matlab format": 1758 file_stream.write("frames_dropped = %s; %% boolean\n"%str(frames_dropped)) 1759 file_stream.write("go_loop_start_time_abs_sec = %s;\n"%repr(go_loop_start_time)) 1760 else: 1761 raise RuntimeError("Unknown parameter file type") # Should never get here
1762
1763 - def sleep_with_progress(self, duration_sec):
1764 if duration_sec == 0.0: 1765 return # don't do anything 1766 start_time = time.time() 1767 stop_time = start_time + duration_sec 1768 percent_done = 0 1769 while percent_done < 100: 1770 if sys.platform != 'darwin': # Mac OS X Tk bug... sigh... 1771 self.progress.updateProgress(percent_done) 1772 time.sleep(0.01) # wait 10 msec 1773 percent_done = (time.time() - start_time)/duration_sec*100
1774
1775 - def quit(self):
1776 if self.quit_server_too.get(): 1777 self.ephys_server.set_quit_status(1) 1778 # call parent class 1779 Tkinter.Frame.quit(self)
1780
1781 - def destroy(self):
1782 try: 1783 if self.quit_server_too.get(): 1784 self.ephys_server.set_quit_status(1) 1785 except: 1786 pass 1787 Tkinter.Frame.destroy(self)
1788
1789 -class BarButton(Tkinter.Menubutton):
1790 # Taken from Guido van Rossum's Tkinter svkill demo
1791 - def __init__(self, master=None, **cnf):
1792 Tkinter.Menubutton.__init__(self, master, **cnf) 1793 self.pack(side=Tkinter.LEFT) 1794 self.menu = Tkinter.Menu(self, name='menu', tearoff=0) 1795 self['menu'] = self.menu
1796 1797 if __name__ == '__main__': 1798 hostname = os.getenv("ephys_server_hostname","") 1799 port = int(os.getenv("ephys_server_port","7766")) 1800 result = get_server(hostname=hostname,port=port) 1801 if result: 1802 hostname,port = result 1803 app_window = AppWindow(client_list=client_list, 1804 server_hostname=hostname, 1805 server_port=port) 1806 1807 app_window.winfo_toplevel().wm_iconbitmap() 1808 app_window.pack(expand=1,fill=Tkinter.BOTH) 1809 app_window.mainloop() 1810