Package qubx :: Module cube
[hide private]
[frames] | no frames]

Source Code for Module qubx.cube

  1  """Components for the top level of the QUB Express or Fitness. 
  2   
  3  Copyright 2008-2014 Research Foundation State University of New York  
  4  This file is part of QUB Express.                                           
  5   
  6  QUB Express is free software; you can redistribute it and/or modify           
  7  it under the terms of the GNU General Public License as published by  
  8  the Free Software Foundation, either version 3 of the License, or     
  9  (at your option) any later version.                                   
 10   
 11  QUB Express is distributed in the hope that it will be useful,                
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of        
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
 14  GNU General Public License for more details.                          
 15   
 16  You should have received a copy of the GNU General Public License,    
 17  named LICENSE.txt, in the QUB Express program directory.  If not, see         
 18  <http://www.gnu.org/licenses/>.                                       
 19   
 20   
 21  """ 
 22   
 23  # py2exe: add dist\ to python path, for OpenGL 
 24  import os 
 25  import sys 
 26  for path in sys.path: 
 27      if 'dist' in path: 
 28          base, leaf = path, '' 
 29          while 'dist' in base: 
 30              base, leaf = os.path.split(base) 
 31          if leaf == 'dist': 
 32              sys.path.append(os.path.join(base, leaf)) 
 33              break 
 34   
 35  import gc 
 36  import gtk 
 37  import gobject 
 38  import sys 
 39  import linecache 
 40  import multiprocessing 
 41  import argparse 
 42  import os 
 43  import platform 
 44  import re 
 45  import time 
 46  import traceback 
 47  import urllib 
 48  import webbrowser 
 49   
 50  import qubx.fast.fast_utils # force early load of library 
 51   
 52  import qubx.crash_rep 
 53  import qubx.data_types 
 54  import qubx.data_abf 
 55  import qubx.data_acquire 
 56  import qubx.data_tacidl 
 57  import qubx.data_pulse 
 58  import qubx.data_fit 
 59  import qubx.dataGTK 
 60  import qubx.extract 
 61  import qubx.fit 
 62  import qubx.fit_robots 
 63  import qubx.global_namespace 
 64  import qubx.GTK 
 65  import qubx.ideal_tools 
 66  import qubx.idealize 
 67  import qubx.kalman 
 68  import qubx.list_average 
 69  import qubx.list_figure 
 70  import qubx.select_charts 
 71  import qubx.simulate 
 72  import qubx.spectrum 
 73  import qubx.splash_main 
 74  import qubx.tree 
 75  import qubx.treeGTK 
 76  import qubx.trials 
 77  import qubx.modelGTK 
 78  import qubx.table 
 79  import qubx.tableGTK 
 80  import qubx.task 
 81  import qubx.toolspace 
 82  import qubx.pyenv 
 83  import qubx.settings 
 84  import qubx.pyenvGTK 
 85  import qubx.model 
 86  import qubx.model_charts 
 87  import qubx.model_link 
 88  import qubx.modeling_utils 
 89  import qubx.faces 
 90  import qubx.hist 
 91  import qubx.hill 
 92  import qubx.optimize 
 93  import qubx.optimizeGTK 
 94  import qubx.notebook 
 95  import qubx.notebookGTK 
 96  import qubx.notebook_qtiplot 
 97  import qubx.notebook_scidavis 
 98  import qubx.script_seq 
 99  import qubx.stimulus 
100  import qubx.vcheck_client 
101  import qubx.workflow 
102   
103  from gtk import gdk 
104  from gtk import keysyms 
105  from itertools import izip, count 
106  from qubx.util_types import * 
107  from qubx.accept import * 
108  from qubx.data_panel import DataSourceFace, DataFace 
109  from qubx.models_panel import ModelsFace 
110  from qubx.table_panel import TablesFace 
111  from qubx.toolspace import ColorInfo 
112  from qubx.util_panels import TasksFace, ScriptsFace, AltKeysFace, AboutFace, SettingsFace, PluginsFace 
113  from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem 
114   
115  import numpy 
116  import scipy 
117  import scipy.signal 
118  from math import * 
119  from numpy import * 
120   
121  COLOR_VERSIONSTAMP = ('express.version', (1,1,1,1)) 
122  ColorInfo[COLOR_VERSIONSTAMP[0]].label = 'Window version' 
123   
124  COLOR_MENU_OTHER = ('qubx.cube.other.menu', (.2, 1, .2, 1)) 
125  ColorInfo[COLOR_MENU_OTHER[0]].label = 'Other panel menu' 
126   
127  TARGET_TYPE_URI_LIST = 80 
128  dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ] 
129   
130  # the script trace function processes up to this number of events per line (e.g. mouse, draw, idle_add, timeout_add, highlighting that line in Admin.Scripts, ...) 
131  MAX_EVENTS_PER_SCRIPT_LINE = 128 
132   
133 -def get_file_path_from_dnd_dropped_uri(uri):
134 # get the path to file 135 path = "" 136 if uri.startswith('file:\\\\\\'): # windows 137 path = uri[8:] # 8 is len('file:///') 138 elif uri.startswith('file://'): # nautilus, rox 139 path = uri[7:] # 7 is len('file://') 140 elif uri.startswith('file:'): # xffm 141 path = uri[5:] # 5 is len('file:') 142 143 path = urllib.url2pathname(path) # escape special chars 144 path = path.strip('\r\n\x00') # remove \r\n and NULL 145 146 return path
147
148 -class Cube(gtk.Window):
149 """Main window of QUB Express, Fitness. 150 151 @ivar appname: e.g. "QUB Express" 152 @ivar Layout: L{qubx.faces.ToolsToggleFace} controlling all left-hand panels and menus 153 @ivar Panels: L{qubx.faces.ToolsFace} in upper-left, for utilities; aka "Other" 154 @ivar Tools: link to Panels.Tools 155 @ivar Simulation: link to Panels.Simulation 156 @ivar Modeling: link to Panels.Modeling 157 @ivar Figures: link to Panels.Figures 158 @ivar Workflows: link to Panels.Workflows 159 @ivar Admin: link to Panels.Admin 160 @ivar Data: L{DataFace} 161 @ivar Tables: L{TablesFace} 162 @ivar Models: L{ModelsFace} 163 @ivar Tasks: L{TasksFace} 164 @ivar About: L{qubx.faces.ToolsFace} in upper-right, for context info 165 @ivar OnInit: L{WeakEvent}C{()} called on first idle 166 @ivar OnQuitting: L{WeakEvent}C{( do_cancel() )} called when trying to quit; call do_cancel() to stay open 167 @ivar OnQuit: L{WeakEvent}C{()} called just before gtk.main_quit() 168 """ 169 170 __explore_featured = ['appname', 'Layout', 'Panels', 'Tools', 'Simulation', 'Modeling', 'Figures', 'Workflows', 'Admin', 171 'Data', 'Tables', 'Models', 'Charts', 'Tasks', 'About', 'OnInit', 'OnQuitting', 'OnQuit', 172 'vbox', 'left_right', 'subVersion', 'tasks_info_log', 'tasks_ds', 'info_log', 'boxNotebook', 'btnBugReport', 173 'mnuOther', 'mnuSubOther', 'Trials', 'DataSource', 'Layout_alt', 'use_alt_layout', 'toggle_layout', 174 'show_charts', 'show_version', 'check_version', 'require_version', 'can_not_quit', 'try_to_quit', 175 'new_table', 'open_table', 'write_log', 'pause', 'resume', 'quit'] 176
177 - def __init__(self, appname, has_modeling=True, adjust_layout=lambda cube:None):
178 self.appname = appname 179 self.has_modeling = has_modeling 180 MapSettings() # get/create SettingsMgr 181 gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) 182 self.set_title(appname) 183 self.connect('delete_event', self._onDelete) 184 self.connect('destroy', self._onDestroy) 185 self.ref = Reffer() 186 self._profile = SettingsMgr['Cube'].active 187 SettingsMgr['Cube'].OnSet = self.ref(self._onPreSet) 188 self.__initial_layout = self._profile['Layout'].clone() 189 self._keepName = '' 190 self.connect('configure_event', self._onConfigure) 191 #self.add_events(gdk.KEY_PRESS_MASK) 192 #self.connect('key_press_event', self._onKeyPress) 193 194 qubx.pyenv.env.globals['QubX'] = self 195 qubx.pyenv.env.globals['SettingsMgr'] = SettingsMgr 196 gobject.idle_add(qubx.pyenv.env.after_initialize) 197 198 self.OnInit = WeakEvent() 199 self.OnQuitting = WeakEvent() # OnQuitting( do_cancel() ) 200 self.OnQuit = WeakEvent() 201 gobject.idle_add(self.OnInit) 202 203 Settings = SettingsFace(global_name='QubX.Admin.Settings') # before anything else, to set up OpenGL etc 204 205 self.vbox = gtk.VBox() 206 self.vbox.show() 207 self.add(self.vbox) 208 209 #mb = pack_item(gtk.MenuBar(), self.vbox) 210 #menu = gtk.Menu() 211 #build_menuitem('File', submenu=menu, menu=mb) 212 #build_menuitem('Quit', self.ref(bind(self.try_to_quit)), menu=menu) 213 214 self.left_right = pack_item(gtk.HPaned(), self.vbox, expand=True) 215 self.left_right.connect('notify::position', self._on_left_right) 216 217 self.Layout = qubx.faces.ToolsToggleFace('Layout', self._profile['Layout'], global_name='QubX.Layout') 218 self.Layout.add_events(gdk.KEY_PRESS_MASK) 219 self.Layout.connect('key_press_event', self._onLayoutKeyPress) 220 self.Layout.show() 221 self.left_right.pack1(self.Layout, True, True) 222 self.subVersion = qubx.toolspace.SubLayer_Label('', 0, 1, x=0, w=2, h=qubx.faces.TOOLTOGGLE_NAME_EM, 223 color=COLOR_VERSIONSTAMP, rotate=pi/2, 224 action=self.ref(bind(self.__onClickTool, ['Tools', 'About']))) 225 self.Layout.nameLayer.add_sublayer(self.subVersion) 226 self.Layout.parent_window = self 227 228 self.tasks_info_log = gtk.VBox(False) 229 self.tasks_info_log.show() 230 self.left_right.pack2(self.tasks_info_log, True, True) 231 self.tasks_ds = pack_item(gtk.VBox(False), self.tasks_info_log) 232 self.Tasks = pack_item(TasksFace(global_name='QubX.Tasks'), self.tasks_ds) 233 pack_hsep(11, self.tasks_info_log) 234 235 self.info_log = pack_item(gtk.VPaned(), self.tasks_info_log, expand=True) 236 self.About = qubx.faces.ToolsFace("About", pos=gtk.POS_TOP, global_name='QubX.About') 237 self.About.set_size_request(120, 120) 238 self.About.show() 239 self.info_log.pack1(self.About, True, False) 240 self.boxNotebook = gtk.VBox() 241 self.boxNotebook.show() 242 self.info_log.pack2(self.boxNotebook, True, False) 243 pan = pack_item(qubx.notebookGTK.NbEntryFace(), self.boxNotebook, expand=True) 244 245 #h = pack_item(gtk.HBox(), self.tasks_info_log) 246 self.btnBugReport = pack_button('Send bug report...', self.tasks_info_log, on_click=self.__onClickBugReport) 247 248 self.Panels = qubx.faces.ToolsFace(' Other', global_name='QubX.Panels') 249 self.Panels.show() 250 self.mnuOther = gtk.Menu() 251 self.mnuSubOther = qubx.toolspace.SubLayer_Popup(self.mnuOther, COLOR_MENU_OTHER, on_popup=self.ref(self.__onPopupOther), 252 w=qubx.faces.TOOLTOGGLE_WIDTH_EM, h=qubx.faces.TOOLTOGGLE_WIDTH_EM, 253 tooltip="Menu of panels, scripts, and layout options", 254 border=qubx.faces.TOOLTOGGLE_TAB_BORDER) 255 self.Panels.tab_sublayers.append(self.mnuSubOther) 256 self.Tables = TablesFace(global_name='QubX.Tables') 257 self.Tables.show() 258 259 self.Data = DataFace(self.Tables, has_modeling, global_name='QubX.Data') 260 self.Data.show() 261 if has_modeling: 262 self.Models = ModelsFace(self.Tables, global_name='QubX.Models') 263 self.Models.show() 264 self.Trials = qubx.trials.Trials(self.Data, self.Models.views[0]) 265 else: 266 self.Models = self.Trials = None 267 268 self.Layout.append_face(self.Panels) 269 self.Layout.append_face(self.Data) 270 self.Layout.append_face(self.Tables) 271 if has_modeling: 272 self.Layout.append_face(self.Models) 273 274 self.DataSource = qubx.dataGTK.DataSource(self) 275 self.DataSourceFace = pack_item(DataSourceFace(self.DataSource, global_name='QubX.DataSourceFace'), self.tasks_ds) 276 277 self.Data.init_fitting() 278 279 self.Panels.append_face(qubx.faces.ToolsFace('Tools', global_name='QubX.Tools')) 280 self.Tools = self.Panels.Tools 281 self.Tools.append_face(AboutFace(global_name='QubX.Tools.About')) 282 self.Tools.append_face(qubx.extract.ExtractFace(global_name='QubX.Tools.Extract')) 283 self.Tools.append_face(qubx.kalman.KalmanFace(global_name='QubX.Tools.Kalman')) 284 if has_modeling: 285 self.Tools.append_face(qubx.ideal_tools.IdlToolsFace(global_name='QubX.Tools.Idealization')) 286 self.Panels.append_face(qubx.simulate.SimulationFace(global_name='QubX.Simulation')) 287 self.Simulation = self.Panels.Simulation 288 self.Panels.append_face(ModelingFace(global_name='QubX.Modeling')) 289 self.Modeling = self.Panels.Modeling 290 self.Modeling.append_face(qubx.stimulus.StimulusFace(self, global_name='QubX.Modeling.Stimulus')) 291 self.Modeling.append_face(qubx.modeling_utils.ModelingUtilsFace()) 292 self.Modeling.append_face(qubx.idealize.IdlFace()) 293 skm = qubx.idealize.SKMIdealizer() 294 self.Modeling.Idealize.register_method('Viterbi', skm) 295 self.Modeling.Idealize.register_method('SKM', skm) 296 self.Modeling.Idealize.register_method('Baum-Welch', qubx.idealize.BaumWelchIdealizer()) 297 self.Modeling.Idealize.register_method('By Delta (forward)', qubx.stimulus.ByDeltaIdealizer()) 298 self.Modeling.Idealize.register_method('By Delta (binary)', qubx.stimulus.ByDeltaARIdealizer()) 299 self.Panels.append_face(qubx.faces.ToolsFace('Figures', global_name='QubX.Figures')) 300 self.Figures = self.Panels.Figures 301 self.Figures.append_face(qubx.select_charts.SelectFace(global_name='QubX.Figures.Charts')) 302 self.Charts = self.Figures.Charts 303 self.Panels.Figures.append_face(qubx.hist.AmpHistFace(global_name='QubX.Figures.AmpHist')) 304 self.Figures.append_face(qubx.list_average.ListAverageFace('Average', 'QubX.Figures.Average')) 305 self.Figures.append_face(qubx.list_figure.ListFigureFace('ListFigure', 'QubX.Figures.ListFigure')) 306 self.Panels.Figures.append_face(qubx.spectrum.SpectrumFace(global_name='QubX.Figures.Spectrum')) 307 if has_modeling: 308 self.Panels.Figures.append_face(qubx.hill.HillFace(global_name='QubX.Figures.Hill')) 309 self.Panels.append_face(qubx.faces.ToolsFace('Workflows', global_name='QubX.Workflows', pos=qubx.faces.POS_DROPDOWN, dropdown_label="Select a workflow:")) 310 self.Workflows = self.Panels.Workflows 311 self.Panels.append_face(qubx.faces.ToolsFace('Admin', global_name='QubX.Admin')) 312 self.Admin = self.Panels.Admin 313 self.Panels.Admin.append_face(Settings) 314 self.Admin.append_face(qubx.notebookGTK.NotebookFace(global_name='QubX.Admin.Notebook')) 315 self.Panels.Admin.append_face(ScriptsFace(global_name='QubX.Admin.Scripts')) 316 self.Panels.Admin.append_face(PluginsFace(global_name='QubX.Admin.Plugins')) 317 self.Panels.Admin.append_face(AltKeysFace(global_name='QubX.Admin.AltKeys')) 318 self.Panels.Admin.append_face(qubx.script_seq.SeqFace(global_name='QubX.Admin.Seq')) 319 320 self.Data.pop_info.OnWindow += self.ref(self.__connect_drag) 321 self.Tables.pop_info.OnWindow += self.ref(self.__connect_drag) 322 if has_modeling: 323 self.Models.pop_info.OnWindow += self.ref(self.__connect_drag) 324 325 self.Panels.OnSwitchFace += self.ref(self.__onSwitchPanel) 326 try: # if self._profile['Panel'].data: 327 self.Panels.show_face(str(self._profile.find('Panel').data)) 328 except: # else: 329 self.Panels.show_face('Tools') 330 self.Tools.show_face('About') 331 adjust_layout(self) 332 gobject.idle_add(self.__restore_faces) 333 334 try: 335 x, y, w, h = self._profile['alloc'].data[:] 336 self.resize(w, h) 337 gobject.idle_add(self.move, x, y) 338 except: 339 w, h = gdk.screen_width()-10, gdk.screen_height()-70 340 self.resize(w, h) 341 gobject.idle_add(self.move, 0, 0) 342 try: 343 self.left_right.set_position(int(round(w * self._profile['left_right'].data[0]))) 344 except: 345 self.left_right.set_position(w - max(150, min(300, int(round(w*15.0/80.0))))) 346 try: 347 self.info_log.set_position(int(round(h * self._profile['info_log'].data[0]))) 348 except: 349 self.info_log.set_position(h-120) 350 351 ### initialize Layout.profile elements 352 vis = self.Layout.profile['Showing'] 353 frac = self.Layout.profile['Fraction'] 354 for face in self.Layout.faces: 355 if not vis.find(face.face_name).data: 356 vis[face.face_name].data = 1 357 if not frac.find(face.face_name).data: 358 if has_modeling: 359 frac[face.face_name].data = {' Other' : .18, 360 ' Data' : .35, 361 ' Tables' : .15, 362 ' Models' : .32}[face.face_name] 363 else: 364 frac[face.face_name].data = {' Other' : .2, 365 ' Data' : .6, 366 ' Tables' : .2}[face.face_name] 367 gobject.idle_add(self.__show_faces) 368 369 self.Layout_alt = gtk.Window(gtk.WINDOW_TOPLEVEL) 370 self.Layout_alt.connect('delete_event', self._onDelete) 371 self.Layout_alt.connect('destroy', self._onDestroy) 372 self.Layout_alt.connect('configure_event', self._onConfigureAlt) 373 self.Layout_alt.add_events(gdk.KEY_PRESS_MASK) 374 self.Layout_alt.connect('key_press_event', self._onLayoutKeyPress) 375 self.Layout_alt.set_size_request(200, 480) 376 self.Layout_alt.set_title(self.appname) 377 try: 378 x, y, w, h = self._profile['alloc_alt'].data[:] 379 self.Layout_alt.resize(w, h) 380 gobject.idle_add(self.Layout_alt.move, x, y) 381 except: 382 pass 383 v = gtk.VBox() 384 v.show() 385 386 self.__use_alt_layout = False 387 self.Layout_alt.add(v) 388 self.Layout_alt.menubar = pack_item(gtk.MenuBar(), v) 389 self.Layout_alt.mnuWindows = gtk.Menu() 390 self.Layout_alt.menubar.append(build_menuitem('Windows', self.ref(self.__onPopupWindows), submenu=self.Layout_alt.mnuWindows)) 391 self.Layout_alt.mnuLayouts = gtk.Menu() 392 self.Layout_alt.menubar.append(build_menuitem('Layouts', self.ref(self.__onPopupLayouts), submenu=self.Layout_alt.mnuLayouts)) 393 self.Layout_alt.mnuScripts = gtk.Menu() 394 self.Layout_alt.menubar.append(build_menuitem('Scripts', self.ref(self.__onPopupScripts), submenu=self.Layout_alt.mnuScripts)) 395 self.Layout_alt.mnuTools = gtk.Menu() 396 self.Layout_alt.main_pane = pack_item(gtk.VBox(), v, expand=True) 397 self.Layout_alt.nameLayer = qubx.toolspace.Layer(x=-qubx.faces.TOOLTOGGLE_NAME_EM, w=-.01, h=2) 398 self.Layout_alt.subVersion = self.Layout_alt.nameLayer.add_sublayer(qubx.toolspace.SubLayer_Label(self.appname, 0, 1, x=0, w=-.01, h=2, 399 color=COLOR_VERSIONSTAMP, 400 action=self.ref(bind(self.Tools.About.pop_info.set_state, 1)))) 401 self.Layout_alt.logo = pack_item(qubx.toolspace.ToolSpace(), v, at_end=True) 402 self.Layout_alt.logo.add_layer(self.Layout_alt.nameLayer) 403 self.Layout_alt.logo.appearance.OnSetFontSize += self.ref(self.__onSetFontSize) 404 self.__onSetFontSize(self.Layout_alt.logo.appearance.font_size) 405 406 self.__connect_drag_win(self) 407 ual = False if self._profile.find('use_alt_layout').isNull else self._profile['use_alt_layout'].data[0] 408 gobject.idle_add(self.set_use_alt_layout, ual)
409 410 use_alt_layout = property(lambda self: self.__use_alt_layout, lambda self, x: self.set_use_alt_layout(x))
411 - def __onSetFontSize(self, fontsize):
412 self.Layout_alt.logo.set_size_request(-1, int(round(2*self.Layout_alt.logo.appearance.emsize)))
413 - def __connect_drag_win(self, win):
414 win.connect('drag_data_received', self.on_drag_data_received) 415 win.drag_dest_set( gtk.DEST_DEFAULT_MOTION | 416 gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP, 417 dnd_list, gtk.gdk.ACTION_COPY)
418 - def __connect_drag(self, face):
421 - def on_drag_data_received(self, widget, context, x, y, selection, target_type, timestamp):
422 if target_type == TARGET_TYPE_URI_LIST: 423 uri = selection.data.strip('\r\n\x00') 424 uri_splitted = uri.split() # we may have more than one file dropped 425 data_paths = [] 426 for uri in uri_splitted: 427 path = get_file_path_from_dnd_dropped_uri(uri) 428 if os.path.isfile(path): 429 typ = os.path.splitext(path)[1].lower() 430 if typ in ['.qmf', '.qmj', '.mdl']: 431 self.Models.open(path) 432 elif typ == '.py': 433 self.Admin.Scripts.scripts.open(path, promptSave=True) 434 else: 435 data_paths.append(path) 436 if (len(data_paths) > 1) and qubx.pyenvGTK.prompt_choices("Open the files as a sequence?", ["No", "Yes"], parent=widget): 437 self.Data.open_sequence(data_paths) 438 else: 439 for path in data_paths: 440 self.Data.open(path)
441 - def __onPopupWindows(self, item):
442 def nest_faces(face): 443 submenu = None 444 try: 445 if face.faces: 446 submenu = gtk.Menu() 447 for subface in face.faces: 448 submenu.append(nest_faces(subface)) 449 except: 450 pass 451 if submenu: 452 item = build_menuitem(face.face_name, submenu=submenu) 453 else: 454 item = build_menuitem(face.face_name, menu.ref(lambda item: face.pop_info.set_state(1) or face.pop_info.window.present())) 455 return item
456 menu = self.Layout_alt.mnuWindows 457 menu.foreach(lambda item: menu.remove(item)) 458 menu.ref = Reffer() 459 build_menuitem('Data', menu.ref(lambda item: self.Data.pop_info.set_state(1) or self.Data.pop_info.window.present()), menu=menu) 460 if 'Models' in self.__dict__: 461 build_menuitem('Models', menu.ref(lambda item: self.Models.pop_info.set_state(1) or self.Models.pop_info.window.present()), menu=menu) 462 if 'Charts' in self.__dict__: 463 build_menuitem('Charts', menu.ref(lambda item: self.Charts.pop_info.set_state(1) or self.Charts.pop_info.window.present()), menu=menu) 464 build_menuitem('Tables', menu.ref(lambda item: self.Tables.pop_info.set_state(1) or self.Tables.pop_info.window.present()), menu=menu) 465 for face in self.Panels.faces: 466 menu.append(nest_faces(face)) 467 ####chk = build_menuitem('Reset all settings (upon exiting)', self.ref(bind(self.__onClickResetAll)), menu=menu, item_class=gtk.CheckMenuItem, active=qubx.global_namespace.RESET_ALL_SETTINGS) 468 build_menuitem('Go to QUB Web Site...', self.ref(bind(webbrowser.open, 'https://qub.mandelics.com')), menu=menu)
469 - def __onPopupLayouts(self, item):
470 layout_menu = self.Layout_alt.mnuLayouts 471 layout_menu.foreach(lambda item: layout_menu.remove(item)) 472 layout_menu.ref = Reffer() 473 build_menuitem('Switch to single-window layout', layout_menu.ref(bind(self.toggle_layout)), menu=layout_menu) 474 layout_menu.append(build_menuitem('Show All', layout_menu.ref(bind(self.__onClickShowAll)))) 475 layout_menu.append(build_menuitem('Hide All', layout_menu.ref(bind(self.__onClickHideAll)))) 476 layout_menu.append(build_menuitem('Equal Sizes', layout_menu.ref(bind(self.__onClickEqualSizes)))) 477 for name in SettingsMgr['Cube'].listNames(): 478 layout_menu.append(build_menuitem(name, layout_menu.ref(bind(self.__onClickPreset, name)))) 479 layout_menu.append(build_menuitem('Manage Layouts...', layout_menu.ref(bind(self.__onClickManageLayouts)))) 480 layout_menu.append(build_menuitem('Add Layout to Menu...', layout_menu.ref(bind(self.__onClickAddToMenu))))
481 - def __onPopupScripts(self, item):
482 def nest_scripts(script_menu, path): 483 for base, dirs, files in os.walk(path): 484 for d in dirs: 485 sub = gtk.Menu() 486 build_menuitem(d, submenu=sub, menu=script_menu) 487 nest_scripts(sub, os.path.join(path, d)) 488 dirs[:] = [] 489 for f in files: 490 name, ext = os.path.splitext(f) 491 if ext.lower() == '.py': 492 build_menuitem(name, script_menu.ref(bind(self.__onClickScript, os.path.join(path, f))), menu=script_menu)
493 scripts_menu = self.Layout_alt.mnuScripts 494 scripts_menu.foreach(lambda item: scripts_menu.remove(item)) 495 scripts_menu.ref = Reffer() 496 build_menuitem('Scripts Window', scripts_menu.ref(lambda item: self.Admin.Scripts.pop_info.set_state(1) or self.Admin.Scripts.pop_info.window.present()), menu=scripts_menu) 497 build_menuitem('Sequencer', scripts_menu.ref(lambda item: self.Admin.Seq.pop_info.set_state(1) or self.Admin.Seq.pop_info.window.present()), menu=scripts_menu) 498 build_menuitem('-', menu=scripts_menu) 499 nest_scripts(scripts_menu, os.path.join(qubx.pyenv.env.globals['app_path'], 'Scripts')) 500 nest_scripts(scripts_menu, os.path.join(qubx.pyenv.env.folder, 'Scripts'))
501 - def toggle_layout(self):
502 self.use_alt_layout = not self.__use_alt_layout
503 - def set_use_alt_layout(self, x):
504 if self.__use_alt_layout == bool(x): 505 return 506 self.__use_alt_layout = bool(x) 507 self._profile['use_alt_layout'].data = bool(x) 508 if x: 509 self.left_right.remove(self.tasks_info_log) 510 self.Layout_alt.main_pane.pack_start(self.tasks_info_log) 511 qubx.faces.REQUEST_SHOW_AS_WINDOW = True 512 self.Layout.showing = False 513 gobject.idle_add(self.hide) 514 gobject.idle_add(self.Layout_alt.show) 515 else: 516 self.Layout_alt.main_pane.remove(self.tasks_info_log) 517 self.left_right.pack2(self.tasks_info_log, True, True) 518 self.Layout.unpop_all() 519 qubx.faces.REQUEST_SHOW_AS_WINDOW = False 520 self.Layout.showing = True 521 gobject.idle_add(self.Layout_alt.hide) 522 gobject.idle_add(self.show)
523 - def show_charts(self):
524 self.Figures.Charts.request_show()
525 - def show_version(self, version, url=None):
526 self.subVersion.label = version 527 self.Layout_alt.subVersion.label = version 528 if url: 529 self.subVersion.action = self.ref(lambda x,y,e: webbrowser.open(url)) 530 self.Layout_alt.subVersion.action = self.ref(lambda x,y,e: webbrowser.open(url)) 531 gobject.timeout_add(4000, self.check_version)
532 - def check_version(self):
533 gobject.timeout_add(15000, self.__check_version_netwait)
534 - def __check_version_netwait(self):
535 # Suppose the 1-day wait expires while computer is sleeping. 536 # On wake, it takes a few seconds to re-establish network connections, 537 # so we need an additional delay before dialing the web server 538 qubx.vcheck_client.vcheck_client_task(qubx.global_namespace.QUBX_CODENAME).start() 539 gobject.timeout_add(1000*60*60*24, self.check_version) # daily check
540 - def require_version(self, *version):
541 qubx_version = qubx.pyenv.parse_version_str(qubx.pyenv.env.globals['QUBX_VERSION']) 542 n_common = min(len(version), len(qubx_version)) 543 for q, v in izip(qubx_version[:n_common], version[:n_common]): 544 if q < v: 545 raise Exception('Plugin requires QUB Express %s, but this is version %s. Please download a newer version of QUB Express.' % 546 ('.'.join(str(x) for x in version), '.'.join(str(x) for x in qubx_version))) 547 elif q > v: 548 return
549 - def __restore_faces(self):
550 if not qubx.global_namespace.QUBX_READY: 551 return True 552 try: 553 self.Panels.Tools.show_face(str(self._profile.find('Tool').data)) 554 self.Panels.Figures.show_face(str(self._profile.find('Figure').data)) 555 self.Panels.Admin.show_face(str(self._profile.find('AdminFace').data)) 556 if self.has_modeling: 557 self.Panels.Modeling.show_face(str(self._profile.find('ModelingFace').data)) 558 except: 559 pass 560 self.Panels.Tools.OnSwitchFace += self.ref(self.__onSwitchToolFace) 561 self.Panels.Figures.OnSwitchFace += self.ref(self.__onSwitchFigureFace) 562 self.Panels.Admin.OnSwitchFace += self.ref(self.__onSwitchAdminFace) 563 if self.has_modeling: 564 self.Panels.Modeling.OnSwitchFace += self.ref(self.__onSwitchModelingFace) 565 self.__updateLayout(SettingsMgr['Layout'], self.__initial_layout)
566 - def __show_faces(self):
567 self.Tasks.showing = True 568 self.About.showing = True 569 self.Layout.showing = True
570 - def __onSwitchPanel(self, panels, face_name):
571 self._profile['Panel'].data = face_name
572 - def __onSwitchToolFace(self, tools, face_name):
573 self._profile['Tool'].data = face_name
574 - def __onSwitchModelingFace(self, tools, face_name):
575 self._profile['ModelingFace'].data = face_name
576 - def __onSwitchFigureFace(self, figures, face_name):
577 self._profile['Figure'].data = face_name
578 - def __onSwitchAdminFace(self, tools, face_name):
579 self._profile['AdminFace'].data = face_name
580 - def _onDelete(self, w, evt):
581 return self.can_not_quit()
582 - def can_not_quit(self):
583 self._cancelDelete = False 584 self.OnQuitting(self._do_cancelDelete) 585 return self._cancelDelete
586 - def try_to_quit(self):
587 if not self.can_not_quit(): 588 self.OnQuit() 589 gtk.main_quit()
590 - def _do_cancelDelete(self):
591 self._cancelDelete = True
592 - def _onDestroy(self, window):
593 self.OnQuit() 594 gtk.main_quit()
595 - def _onConfigure(self, window, alloc):
596 x, y = self.get_position() 597 self._profile['alloc'].data = [x, y, alloc.width, alloc.height]
598 - def _onConfigureAlt(self, window, alloc):
599 x, y = self.Layout_alt.get_position() 600 self._profile['alloc_alt'].data = [x, y, alloc.width, alloc.height]
601 - def _onPreSet(self, settings, updates):
602 try: 603 p = self._profile['left_right'].data[0] 604 self.left_right.set_position(int(round(w * p))) 605 self._profile['left_right'].data = p 606 except: 607 pass 608 try: 609 p = self._profile['info_log'].data[0] 610 self.info_log.set_position(int(round(h * p))) 611 self._profile['info_log'].data = p 612 except: 613 pass 614 if updates.find('use_alt_layout').data: 615 self.use_alt_layout = updates['use_alt_layout'].data[0] 616 if self.__updateLayout(settings, updates['Layout']) and not self.use_alt_layout: # ignore position if everything's docked 617 x, y, w, h = updates['alloc'].data[:] 618 self.resize(w, h) 619 gobject.idle_add(self.move, x, y) 620 if self.use_alt_layout: 621 x, y, w, h = updates['alloc_alt'].data[:] 622 self.Layout_alt.resize(w, h) 623 gobject.idle_add(self.Layout_alt.move, x, y)
624 - def __updateLayout(self, settings, layout):
625 return self.Layout.updateProfile(settings, layout)
626 - def __onPopupOther(self):
627 def nest_scripts(script_menu, path): 628 for base, dirs, files in os.walk(path): 629 for d in dirs: 630 sub = gtk.Menu() 631 build_menuitem(d, submenu=sub, menu=script_menu) 632 nest_scripts(sub, os.path.join(path, d)) 633 dirs[:] = [] 634 for f in files: 635 name, ext = os.path.splitext(f) 636 if ext.lower() == '.py': 637 build_menuitem(name, self.ref(bind(self.__onClickScript, os.path.join(path, f))), menu=script_menu)
638 def nest_faces(face, path=[]): 639 path2 = path+[face.face_name] 640 submenu = None 641 try: 642 if face.faces: 643 submenu = gtk.Menu() 644 for subface in face.faces: 645 submenu.append(nest_faces(subface, path2)) 646 except: 647 pass 648 if submenu: 649 item = build_menuitem(face.face_name, submenu=submenu) 650 else: 651 item = build_menuitem(face.face_name, self.ref(bind(self.__onClickTool, path2))) 652 return item 653 menu = self.mnuOther 654 menu.foreach(lambda item: menu.remove(item)) 655 panel_menu = gtk.Menu() 656 item = build_menuitem('Show Panel', submenu=panel_menu, menu=menu) 657 for face in self.Panels.faces: 658 panel_menu.append(nest_faces(face, [' Other'])) 659 scripts_menu = gtk.Menu() 660 item = build_menuitem('Scripts', submenu=scripts_menu, menu=menu) 661 nest_scripts(scripts_menu, os.path.join(qubx.pyenv.env.globals['app_path'], 'Scripts')) 662 nest_scripts(scripts_menu, os.path.join(qubx.pyenv.env.folder, 'Scripts')) 663 layout_menu = gtk.Menu() 664 item = build_menuitem('Layout', submenu=layout_menu, menu=menu) 665 layout_menu.append(build_menuitem('Show All', self.ref(bind(self.__onClickShowAll)))) 666 layout_menu.append(build_menuitem('Hide All', self.ref(bind(self.__onClickHideAll)))) 667 layout_menu.append(build_menuitem('Equal Sizes', self.ref(bind(self.__onClickEqualSizes)))) 668 for name in SettingsMgr['Cube'].listNames(): 669 layout_menu.append(build_menuitem(name, self.ref(bind(self.__onClickPreset, name)))) 670 layout_menu.append(build_menuitem('Manage Layouts...', self.ref(bind(self.__onClickManageLayouts)))) 671 layout_menu.append(build_menuitem('Add Layout to Menu...', self.ref(bind(self.__onClickAddToMenu)))) 672 menu.append(build_menuitem('Switch to multi-window layout', self.ref(bind(self.toggle_layout)))) 673 chk = build_menuitem('Reset all settings (upon exiting)', self.ref(bind(self.__onClickResetAll)), menu=menu, item_class=gtk.CheckMenuItem, active=qubx.global_namespace.RESET_ALL_SETTINGS) 674 menu.append(build_menuitem('QUB Web Site...', self.ref(bind(webbrowser.open, 'https://qub.mandelics.com')))) 675 return True
676 - def __onClickScript(self, path):
677 qubx.pyenv.env.exec_file(path) 678 qubx.pyenv.env.OnScriptable('qubx.pyenv.env.exec_file(%s)' % repr(path))
679 - def __onClickTool(self, path):
680 self.Layout.show_face(path[0]) 681 node = self.Panels 682 for name in path[1:]: 683 node.show_face(name) 684 node = node.__dict__[name]
685 - def _onLayoutKeyPress(self, w, evt):
686 if qubx.pyenv.env.globals['global_key_press'](w, evt): 687 return True 688 #print 'Layout:',evt.string 689 return False
690 - def new_table(self, name=None, initial_row=True):
691 return self.Tables.new_table(name=name, initial_row=initial_row)
692 - def open_table(self, fname=None):
693 return self.Tables.open_table(fname=fname)
694 - def __onClickShowAll(self):
695 for face in self.Layout.faces: 696 self.Layout.show_face(face.face_name)
697 - def __onClickHideAll(self):
698 for face in self.Layout.faces: 699 self.Layout.show_face(face.face_name, False)
700 - def __onClickEqualSizes(self):
701 for data in self.Layout.data_showing: 702 data.fraction = 1.0 / len(self.Layout.data_showing) 703 self.Layout.update_from_fractions()
704 - def __onClickPreset(self, name):
705 self._onPreSet(SettingsMgr['Cube'], SettingsMgr['Cube'].read(name))
706 - def __onClickManageLayouts(self):
707 dlg = qubx.settingsGTK.PresetsDialog('Cube', qubx.GTK.get_active_window()) 708 dlg.run() 709 dlg.destroy()
710 - def __onClickAddToMenu(self):
711 name = None 712 dlg = qubx.GTK.NumEntryDialog('%s - Add layout to menu'%self.appname, qubx.GTK.get_active_window(), 'Name:', 'Untitled', acceptString) 713 if gtk.RESPONSE_ACCEPT == dlg.run(): 714 name = dlg.value 715 dlg.destroy() 716 if not name: return 717 qubx.settings.SettingsMgr.save(self._profile, 'Cube', name)
718 - def __onClickResetAll(self):
719 qubx.global_namespace.RESET_ALL_SETTINGS = not qubx.global_namespace.RESET_ALL_SETTINGS 720 if qubx.global_namespace.RESET_ALL_SETTINGS: 721 qubx.pyenvGTK.show_message("""Next time you start %s, all settings will be restored their default values. 722 To cancel, choose the menu item again (to un-check it).""" % self.appname)
723 - def _on_left_right(self, paned, param_spec):
724 x,y,w,h = self.get_allocation() 725 if w <= 1: return 726 p = paned.get_position() 727 self._profile['left_right'].data = min(1.0, max(0.0, p * 1.0 / w))
728 - def _on_info_log(self, paned, param_spec):
729 x,y,w,h = self.get_allocation() 730 if h <= 1: return 731 p = paned.get_position() 732 self._profile['info_log'].data = min(1.0, max(0.0, p * 1.0 / h))
733 - def __onClickBugReport(self, btn):
734 qubx.crash_rep.offer_report_crash('Thanks for helping improve this program.')
735 - def write_log(self, msg):
736 """Appends msg to the scrolling text at lower right; trailing newline optional.""" 737 end = len(msg) 738 if len(msg) and (msg[-1] == '\n'): 739 end -= 1 740 clipped = msg[:end] 741 #buf = self.txtLog.get_buffer() 742 #buf.insert(buf.get_end_iter(), clipped+'\n') 743 #self.txtLog.scroll_to_mark(buf.get_insert(), 0) 744 print clipped
745 - def pause(self):
746 """Call only from a script; doesn't return until resume().""" 747 qubx.pyenv.env.exec_script.script_pause()
748 - def resume(self):
749 qubx.pyenv.env.exec_script.resume()
750 - def quit(self, message='Quitting from script'):
751 """Call only from a script; raises KeyboardInterrupt.""" 752 self.destroy() 753 raise KeyboardInterrupt(message)
754 755
756 -class ModelingFace(qubx.faces.ToolsFace):
757 - def __init__(self, global_name=""):
759 - def onShow(self, showing):
760 qubx.faces.ToolsFace.onShow(self, showing) 761 if showing: # update stimulus idlzation only when Modeling is visible 762 self.Stimulus.kick_robot()
763 764 765
766 -def MapSettings():
767 """Sets up global SettingsMgr.""" 768 global SettingsMgr 769 qubx.settings.InitSettings() 770 SettingsMgr = qubx.settings.SettingsMgr
771 772 773
774 -def process_events(max_events=MAX_EVENTS_PER_SCRIPT_LINE):
775 for i in xrange(max_events): 776 if not gtk.events_pending(): 777 break 778 gtk.main_iteration(False)
779 780 781
782 -class App(object):
783 """Sets up the main window (L{Cube}) and calls gtk.main.""" 784 __explore_featured = ['cube', 'setup_about', 'setup_paths', 'setup_globals', 'setup_altmap', 'global_key_press', 'main']
785 - def __init__(self, appname, version, user_dir, has_modeling=True, DEBUG=False, codename='qubx', adjust_layout=lambda cube:None):
786 self.__ref = Reffer() 787 788 global app_path, home, documents_path 789 app_path, home, documents_path = self.setup_paths() 790 user_path = os.path.join(home, user_dir) 791 if os.path.exists(os.path.join(app_path, user_dir)): 792 user_path = os.path.join(app_path, user_dir) 793 else: 794 oneup, leaf = os.path.split(app_path) 795 if (leaf == 'dist') and os.path.exists(os.path.join(oneup, user_dir)): 796 user_path = os.path.join(oneup, user_dir) 797 # user scripting support 798 qubx.pyenv.Init(user_path, gobject.idle_add, gobject.timeout_add, gobject.MainLoop, process_events, app_path) 799 qubx.pyenv.env.globals['DEBUG'] = DEBUG 800 qubx.pyenv.env.globals['home'] = home 801 qubx.pyenv.env.globals['app_path'] = app_path 802 qubx.pyenv.env.globals['documents_path'] = documents_path 803 qubx.pyenv.env.globals['global_key_press'] = self.global_key_press 804 codename_ext = '64' if ((platform.system() == 'Linux') and ('64' in platform.architecture()[0])) else '' 805 qubx.pyenv.env.globals['QUBX_CODENAME'] = '%s%s' % (codename, codename_ext) 806 # saved settings and presets 807 qubx.settings.InitSettings(os.path.join(app_path, 'Presets'), os.path.join(user_path, "Presets")) 808 # curve fitting 809 qubx.fit_robots.InitFitRobots(self.__ref(lambda: qubx.task.Tasks.add_task(qubx.fit_robots.robots)), 810 self.__ref(lambda: qubx.task.Tasks.remove_task(qubx.fit_robots.robots))) 811 # lab notebook (after qubx.settings) 812 qubx.notebookGTK.Init() 813 qubx.notebook_qtiplot.Init() 814 qubx.notebook_scidavis.Init() 815 816 self.setup_altmap() 817 818 if DEBUG: 819 qubx.util_types.ShowDropped = True 820 821 self.cube = Cube(appname, has_modeling, adjust_layout) 822 self.cube.OnQuitting += self.__ref(self._onQuitting) 823 self.cube.show() 824 825 if has_modeling: 826 self.cube.Models.new() 827 828 self.cube.show_version('%s %s' % (appname, version), 'https://qub.mandelics.com') 829 print '%s %s' % (appname, version) 830 print 'app_path: ',app_path 831 self.setup_about() 832 833 qubx.pyenv.Plugins.initialize(os.path.join(app_path, 'Plugins'), 834 os.path.join(user_path, 'Plugins'))
835 - def setup_about(self):
836 pass
837
838 - def setup_paths(self):
839 home = os.path.expanduser('~') # environ.get("HOME") 840 documents_path = home 841 if home.find(':') >= 0: 842 #home = os.path.join(home, 'Application Data') 843 documents_path = os.path.join(home, 'My Documents') 844 app_path = os.path.split(sys.argv[0])[0] 845 stem, leaf = os.path.split(app_path) 846 if leaf == 'qubx': 847 app_path = stem 848 else: 849 app_path = os.environ.get('QUBX_PATH') or '/usr/share/qub-express' 850 app_path = os.path.abspath(app_path) 851 return app_path, home, documents_path
852
853 - def setup_globals(self):
854 globals = qubx.pyenv.env.globals 855 globals['Notebook'] = qubx.notebook.Notebook
856
857 - def setup_altmap(self):
858 env = qubx.pyenv.env 859 if not env.altmap['c']: 860 env.set_alt('c', 'Chop(silent=False)') 861 if not env.altmap['r']: 862 env.set_alt('r', 'QubX.Admin.Scripts.scripts.run()') 863 if not env.altmap['t']: 864 env.set_alt('t', "QubX.Tables.request_show(); QubX.Tables.grab_focus()") 865 if not env.altmap['`']: 866 env.set_alt('`', 'QubX.mnuSubOther.do_popup()')
867
868 - def global_key_press(self, w, evt):
869 mod = gdk.MOD1_MASK if (qubx.pyenv.env.global_modifier == 'Alt') else gdk.MOD2_MASK 870 if evt.state & mod: 871 if evt.keyval < 128: 872 try: 873 qubx.pyenv.env.run_alt(chr(evt.keyval)) 874 return True 875 except: 876 traceback.print_exc() 877 qubx.pyenvGTK.show_message(traceback.format_exc(), '%s - Script error' % self.cube.appname) 878 elif evt.keyval == keysyms.Left: 879 self.cube.Tables.show_prev_table() 880 return True 881 elif evt.keyval == keysyms.Right: 882 self.cube.Tables.show_next_table() 883 return True 884 return False
885
886 - def main(self, startup_script=None):
887 script_path = os.path.abspath(startup_script) if startup_script else None 888 self.setup_globals() 889 qubx.notebookGTK.InitAfter() 890 mark_qubx_ready() 891 if script_path: 892 if script_path == '-': # run stdin directly 893 qubx.pyenv.env.exec_file(script_path) 894 elif os.path.exists(script_path): # load file into Scripts panel for line tracing, pausing, etc. 895 self.cube.Admin.Scripts.scripts.open(script_path) 896 gobject.idle_add(self.cube.Admin.Scripts.scripts.run) 897 elif qubx.pyenv.env.folder == os.path.split(script_path)[0]: # e.g. default user script, not created yet first run 898 try: 899 open(script_path, 'w').write("""# This script is run each time QUB Express starts. If you click the same ten things each time the program starts, 900 # try recording them in this script: 901 # * click "record" above (the red circle) 902 # * do your repetitive setup, e.g. open some files 903 # * click "record" or "stop" above to finish the script 904 # * click "save" above 905 # To reset the startup script, delete this file or its contents at any time. 906 # Tip: While recording, click "pause" above to insert a break into the script. 907 # The script will wait at that point until you click "play." 908 909 """) 910 except: 911 traceback.print_exc() 912 gobject.idle_add(qubx.crash_rep.Init) 913 if qubx.splash_main.Splash: 914 gobject.idle_add(lambda: gobject.idle_add(qubx.splash_main.Splash.destroy) and None) # idle_add returns True; thus repeating 915 gtk.main() 916 qubx.crash_rep.Fini()
917
918 - def _onQuitting(self, do_cancel):
919 self.cube.Layout.pop_prefs.freeze = True # app closing windows shouldn't affect prefs 920 try: 921 if not (self.cube.Models.close_down() and self.cube.Data.close_down()): 922 do_cancel() 923 except: 924 pass 925 qubx.fit_robots.robots.stop()
926
927 -def mark_qubx_ready():
928 qubx.global_namespace.QUBX_READY = True
929 930
931 -def run_me_first():
932 qubx.global_namespace.RESET_ALL_SETTINGS = False
933 934 935
936 -def run_me_last():
937 qubx.pyenv.env.save_altmap() 938 if qubx.global_namespace.RESET_ALL_SETTINGS: 939 qubx.settings.SettingsMgr.resetAll() 940 else: 941 qubx.settings.SettingsMgr.saveAll()
942