def leftDown(self, event): """ When in clipboard mode, set hotspot if a Singlet is highlighted. """ if self.elementMode: return obj = self.selectedObj if isinstance(obj, Atom) and (obj.element is Singlet): mol = obj.molecule if not mol is self.lastHotspotChunk: if self.lastHotspotChunk: # Unset previous hotspot [bruce 060629 fix bug 1974 -- only if in same part] if mol.part is self.lastHotspotChunk.part and mol.part is not None: # Old and new hotspot chunks are in same part. Unset old hotspot, # so as to encourage there to be only one per Part. # This should happen when you try to make more than one hotspot in one # library part or clipboard item, using the MMKit to make both. # It might make more sense for more general code in Part to prevent # more than one hotspot per part... but we have never decided whether # that would be a good feature. (I have long suspected that hotspots # should be replaced by some sort of jig, to give more control....) # I don't know if this case can ever happen as of now, since multichunk # clipboard items aren't shown in MMKit -- whether it can happen now # depends on whether any multichunk library parts have bondpoints on # more than one chunk. [bruce 060629] if env.debug() and self.lastHotspotChunk.hotspot: # bruce 060629 re bug 1974 print "debug: unsetting hotspot of %r (was %r)" % ( self.lastHotspotChunk, self.lastHotspotChunk.hotspot, ) self.lastHotspotChunk.set_hotspot(None) else: # Don't unset hotspot in this case (doing so was causing bug 1974). if env.debug() and self.lastHotspotChunk.hotspot: print "debug: NOT unsetting hotspot of %r" % (self.lastHotspotChunk,) pass self.lastHotspotChunk = mol # [as of 060629, the only purpose of this is to permit the above code to unset it in some cases] mol.set_hotspot(obj) if 1: # bruce 060328 fix gl_update part of bug 1775 (the code looks like that was a bug forever, don't know for sure) main_glpane = env.mainwindow().glpane if mol.part is main_glpane.part: main_glpane.gl_update() self.hotspotAtom = obj self.updateGL() return
def leftDown(self, event): """ When in clipboard mode, set hotspot if a Singlet is highlighted. """ if self.elementMode: return obj = self.selectedObj if isinstance(obj, Atom) and (obj.element is Singlet): mol = obj.molecule if not mol is self.lastHotspotChunk: if self.lastHotspotChunk: # Unset previous hotspot [bruce 060629 fix bug 1974 -- only if in same part] if mol.part is self.lastHotspotChunk.part and mol.part is not None: # Old and new hotspot chunks are in same part. Unset old hotspot, # so as to encourage there to be only one per Part. # This should happen when you try to make more than one hotspot in one # library part or clipboard item, using the MMKit to make both. # It might make more sense for more general code in Part to prevent # more than one hotspot per part... but we have never decided whether # that would be a good feature. (I have long suspected that hotspots # should be replaced by some sort of jig, to give more control....) # I don't know if this case can ever happen as of now, since multichunk # clipboard items aren't shown in MMKit -- whether it can happen now # depends on whether any multichunk library parts have bondpoints on # more than one chunk. [bruce 060629] if env.debug( ) and self.lastHotspotChunk.hotspot: #bruce 060629 re bug 1974 print "debug: unsetting hotspot of %r (was %r)" % \ (self.lastHotspotChunk, self.lastHotspotChunk.hotspot) self.lastHotspotChunk.set_hotspot(None) else: # Don't unset hotspot in this case (doing so was causing bug 1974). if env.debug() and self.lastHotspotChunk.hotspot: print "debug: NOT unsetting hotspot of %r" % ( self.lastHotspotChunk, ) pass self.lastHotspotChunk = mol # [as of 060629, the only purpose of this is to permit the above code to unset it in some cases] mol.set_hotspot(obj) if 1: #bruce 060328 fix gl_update part of bug 1775 (the code looks like that was a bug forever, don't know for sure) main_glpane = env.mainwindow().glpane if mol.part is main_glpane.part: main_glpane.gl_update() self.hotspotAtom = obj self.updateGL() return
def undo_redo_ops(self): # copied code below [dup code is in undo_manager_older.py, not in cvs] # the following value for warn_when_change_counters_seem_wrong is a kluge # (wrong in principle but probably safe, not entirely sure it's correct) [060309] # (note, same value was hardcoded inside that method before bruce 071025; # see comment there about when I see the warnings; it's known that it gives # false warnings if we pass True when _AutoCheckpointing_enabled is false): ops = self.archive.find_undoredos( warn_when_change_counters_seem_wrong=_AutoCheckpointing_enabled) # state_version - now held inside UndoArchive.last_cp (might be wrong) ###@@@ # [what the heck does that comment mean? bruce 071025 Q] undos = [] redos = [] d1 = {'Undo': undos, 'Redo': redos} for op in ops: optype = op.optype() d1[optype].append(op) # sort ops by type ## done in the subr: redos = filter( lambda redo: not redo.destroyed, redos) #060309 since destroyed ones are not yet unstored # remove obsolete redo ops if redos: lis = [(redo.cps[1].cp_counter, redo) for redo in redos] lis.sort() only_redo = lis[-1][1] redos = [only_redo] for obs_redo in lis[:-1]: if undo_archive.debug_undo2 or env.debug(): #060309 adding 'or env.debug()' since this should never happen once clear_redo_stack() is implemented in archive print "obsolete redo:", obs_redo pass #e discard it permanently? ####@@@@ return undos, redos
def undo_redo_ops(self): # copied code below [dup code is in undo_manager_older.py, not in cvs] # the following value for warn_when_change_indicators_seem_wrong is a kluge # (wrong in principle but probably safe, not entirely sure it's correct) [060309] # (note, same value was hardcoded inside that method before bruce 071025; # see comment there about when I see the warnings; it's known that it gives # false warnings if we pass True when _AutoCheckpointing_enabled is false): ops = self.archive.find_undoredos( warn_when_change_indicators_seem_wrong = _AutoCheckpointing_enabled ) # state_version - now held inside UndoArchive.last_cp (might be wrong) ###@@@ # [what the heck does that comment mean? bruce 071025 Q] undos = [] redos = [] d1 = {'Undo':undos, 'Redo':redos} for op in ops: optype = op.optype() d1[optype].append(op) # sort ops by type ## done in the subr: redos = filter( lambda redo: not redo.destroyed, redos) #060309 since destroyed ones are not yet unstored # remove obsolete redo ops if redos: lis = [ (redo.cps[1].cp_counter, redo) for redo in redos ] lis.sort() only_redo = lis[-1][1] redos = [only_redo] for obs_redo in lis[:-1]: if undo_archive.debug_undo2 or env.debug(): #060309 adding 'or env.debug()' since this should never happen once clear_redo_stack() is implemented in archive print "obsolete redo:", obs_redo pass #e discard it permanently? ####@@@@ return undos, redos
def _capitalize_first_word(words): #bruce 060705 res = words[0].upper() + words[1:] if res == words: if env.debug(): print "debug warning: %r did not change in _capitalize_first_word" % ( words, ) return res
def update_cond_from_update_data(self, update_data): #e could be a static method update_number, update_units, update_as_fast_as_possible_data, enable = update_data if not enable: return False #e someday we might do this at the end, if the subsequent code is extended to save some prefs update_as_fast_as_possible = (update_as_fast_as_possible_data != 1) if env.debug(): print "debug: using update_as_fast_as_possible = %r, update_number, update_units = %r, %r" % \ ( update_as_fast_as_possible, update_number, update_units ) pass if update_as_fast_as_possible: # This radiobutton might be misnamed; it really means "use the old code, # i.e. not worse than 20% slowdown, with threshholds". # It's also ambiguous -- does "fast" mean "fast progress" # or "often" (which are opposites)? It sort of means "often". update_cond = (lambda simtime, pytime, nframes: simtime >= max( 0.05, min(pytime * 4, 2.0))) elif update_units == 'frames': update_cond = (lambda simtime, pytime, nframes, _nframes= update_number: nframes >= _nframes) elif update_units == 'seconds': update_cond = (lambda simtime, pytime, nframes, _timelimit= update_number: simtime + pytime >= _timelimit) elif update_units == 'minutes': update_cond = (lambda simtime, pytime, nframes, _timelimit= update_number * 60: simtime + pytime >= _timelimit) elif update_units == 'hours': update_cond = ( lambda simtime, pytime, nframes, _timelimit=update_number * 3600: simtime + pytime >= _timelimit) else: print "don't know how to set update_cond from (%r, %r)" % ( update_number, update_units) update_cond = None # some callers can tolerate this, though it's always a reportable error return update_cond
def update_cond_from_update_data(self, update_data): # e could be a static method update_number, update_units, update_as_fast_as_possible_data, enable = update_data if not enable: return False # e someday we might do this at the end, if the subsequent code is extended to save some prefs update_as_fast_as_possible = update_as_fast_as_possible_data != 1 if env.debug(): print "debug: using update_as_fast_as_possible = %r, update_number, update_units = %r, %r" % ( update_as_fast_as_possible, update_number, update_units, ) pass if update_as_fast_as_possible: # This radiobutton might be misnamed; it really means "use the old code, # i.e. not worse than 20% slowdown, with threshholds". # It's also ambiguous -- does "fast" mean "fast progress" # or "often" (which are opposites)? It sort of means "often". update_cond = lambda simtime, pytime, nframes: simtime >= max(0.05, min(pytime * 4, 2.0)) elif update_units == "frames": update_cond = lambda simtime, pytime, nframes, _nframes=update_number: nframes >= _nframes elif update_units == "seconds": update_cond = lambda simtime, pytime, nframes, _timelimit=update_number: simtime + pytime >= _timelimit elif update_units == "minutes": update_cond = lambda simtime, pytime, nframes, _timelimit=update_number * 60: simtime + pytime >= _timelimit elif update_units == "hours": update_cond = ( lambda simtime, pytime, nframes, _timelimit=update_number * 3600: simtime + pytime >= _timelimit ) else: print "don't know how to set update_cond from (%r, %r)" % (update_number, update_units) update_cond = None # some callers can tolerate this, though it's always a reportable error return update_cond
def _do_whycode_reenable( reasons_list_val, whycode): """ [private helper function for maintaining whycode,whymsg lists] """ res = filter( lambda (code, msg): code != whycode , reasons_list_val ) # zap items with same whycode if len(res) == len(reasons_list_val) and env.debug(): print_compact_stack("debug fyi: redundant call of _do_whycode_reenable, whycode %r, remaining reasons %r" % \ ( whycode, res ) ) return res
def include_dir_ok(include_dir): """ Is this include_dir acceptable (or maybe acceptable)? Return (0, "") or (errorcode, errortext). """ if env.debug(): print "debug: include_dir_ok(include_dir = %r)" % (include_dir,) if os.path.isdir(include_dir): # ok, but warn if transforms.inc is not inside it if not os.path.exists(os.path.join(include_dir, "transforms.inc")): msg = "Warning: transforms.inc not present in POV include directory [%s]; rendering might not work" % (include_dir,) env.history.message(orangemsg(msg)) if env.debug(): print "debug: include_dir_ok returns 0 (ok)" return 0, "" # ok else: if env.debug(): print "debug: include_dir_ok returns 1 (Not found or not a directory)" return 1, "POV include directory: Not found or not a directory" #e pathname might be too long for a dialog pass
def setDisplay_command(self, widget): win = env.mainwindow() win.setDisplayStyle_of_selection(self.ind) #bruce 080910 comment, upon renaming that method from setDisplay: # the new name says what it does, but when originally coded # this would set global style if nothing was selected # (since that method used to act that way). if env.debug(): print "setDisplayStyle_of_selection to %r.ind == %r" % (self, self.ind) return
def _do_whycode_reenable(reasons_list_val, whycode): """ [private helper function for maintaining whycode,whymsg lists] """ res = filter(lambda (code, msg): code != whycode, reasons_list_val) # zap items with same whycode if len(res) == len(reasons_list_val) and env.debug(): print_compact_stack("debug fyi: redundant call of _do_whycode_reenable, whycode %r, remaining reasons %r" % \ ( whycode, res ) ) return res
def _do_whycode_disable( reasons_list_val, whycode, whymsg): """ [private helper function for maintaining whycode,whymsg lists] """ res = filter( lambda (code, msg): code != whycode , reasons_list_val ) # zap items with same whycode if len(res) < len(reasons_list_val) and env.debug(): print_compact_stack("debug fyi: redundant call of _do_whycode_disable, whycode %r msg %r, preserved reasons %r" % \ ( whycode, whymsg, res ) ) res.append( (whycode, whymsg) ) # put the changed one at the end (#k good??) return res
def decide(self, sender, signal): """ should we wrap the slot for this signal when it's sent from this sender? """ if 'treeChanged' in str(signal): ###@@@ kluge: knowing this [bruce 060320 quick hack to test-optimize Undo checkpointing] if env.debug(): ###@@@ kluge: assuming what we're used for, in this message text print "debug: note: not wrapping this signal for undo checkpointing:", signal return False return True # try wrapping them all, for simplicity
def _do_whycode_disable(reasons_list_val, whycode, whymsg): """ [private helper function for maintaining whycode,whymsg lists] """ res = filter(lambda (code, msg): code != whycode, reasons_list_val) # zap items with same whycode if len(res) < len(reasons_list_val) and env.debug(): print_compact_stack("debug fyi: redundant call of _do_whycode_disable, whycode %r msg %r, preserved reasons %r" % \ ( whycode, whymsg, res ) ) res.append((whycode, whymsg)) # put the changed one at the end (#k good??) return res
def include_dir_ok(include_dir): """ Is this include_dir acceptable (or maybe acceptable)? Return (0, "") or (errorcode, errortext). """ if env.debug(): print "debug: include_dir_ok(include_dir = %r)" % (include_dir, ) if os.path.isdir(include_dir): # ok, but warn if transforms.inc is not inside it if not os.path.exists(os.path.join(include_dir, "transforms.inc")): msg = "Warning: transforms.inc not present in POV include directory [%s]; rendering might not work" % ( include_dir, ) env.history.message(orangemsg(msg)) if env.debug(): print "debug: include_dir_ok returns 0 (ok)" return 0, "" # ok else: if env.debug(): print "debug: include_dir_ok returns 1 (Not found or not a directory)" return 1, "POV include directory: Not found or not a directory" #e pathname might be too long for a dialog pass
def cancel_btn_clicked(self): """ Slot for Cancel button """ if env.debug(): print "cancel" # restore values we grabbed on entry. for key,val in zip((endRMS_prefs_key, endMax_prefs_key, cutoverRMS_prefs_key, cutoverMax_prefs_key), self.previousParams): env.prefs[key] = val self.update_widgets(update_seltype = False) #k might not matter since we're about to hide it, but can't hurt QDialog.reject(self) return
def decide(self, sender, signal): """ should we wrap the slot for this signal when it's sent from this sender? """ if 'treeChanged' in str( signal ): ###@@@ kluge: knowing this [bruce 060320 quick hack to test-optimize Undo checkpointing] if env.debug(): ###@@@ kluge: assuming what we're used for, in this message text print "debug: note: not wrapping this signal for undo checkpointing:", signal return False return True # try wrapping them all, for simplicity
def selobj_still_ok(self, glpane): res = self.__class__ is ReferenceGeometry if res: our_selobj = self glname = self.glname owner = env.obj_with_glselect_name.get(glname, None) if owner is not our_selobj: res = False # Do debug prints [perhaps never seen as of 061121] print "%r no longer owns glname %r, instead %r does" % (self, glname, owner) if not res and env.debug(): print "debug: selobj_still_ok is false for %r" % self return res
def _run_bond_updater(warn_if_needed=False): #bruce 080210 split this out if not (changed_structure_atoms or changed_bond_types): # Note: this will be generalized to: # if no changes of any kind, since the last call # Note: the dna updater processes changes in other dicts, # but we don't need to check those in this function. return # some changes occurred, so this function needed to be called # (even if they turn out to be trivial) if warn_if_needed and env.debug(): # whichever user event handler made these changes forgot to call # this function when it was done! # [as of 071115 warn_if_needed is never true; see docstring] print "atom_debug: _master_model_updater should have been called " \ "before this call (since the most recent model changes), " \ "but wasn't!" #e use print_compact_stack?? pass # (other than printing this, we handle unreported changes normally) # handle and clear all changes since the last call # (in the proper order, when there might be more than one kind of change) # Note: reloading this module won't work, the way this code is currently # structured. If reloading is needed, this routine needs to be # unregistered prior to the reload, and reregistered afterwards. # Also, note that the module might be reloading itself, so be careful. if changed_structure_atoms: update_bonds_after_each_event(changed_structure_atoms) #bruce 060315 revised following comments: # Note: this can modify changed_bond_types (from bond-inference, # if we implement that in the future, or from correcting # illegal bond types, in the present code). # SOMEDAY: I'm not sure if that routine will need to use or change # other similar globals in this module; if it does, passing just # that one might be a bit silly (so we could pass none, or all # affected ones) changed_structure_atoms.clear() if changed_bond_types: # WARNING: this dict may have been modified by the above loop # which processes changed_structure_atoms... process_changed_bond_types(changed_bond_types) # REVIEW: our interface to that function needs review if it can # recursively add bonds to this dict -- if so, it should .clear, # not us! changed_bond_types.clear() return # from _run_bond_updater
def selobj_still_ok(self, glpane): res = self.__class__ is ReferenceGeometry if res: our_selobj = self glname = self.glname owner = env.obj_with_glselect_name.get(glname, None) if owner is not our_selobj: res = False # Do debug prints [perhaps never seen as of 061121] print "%r no longer owns glname %r, instead %r does" \ % (self, glname, owner) if not res and env.debug(): print "debug: selobj_still_ok is false for %r" % self return res
def _run_bond_updater(warn_if_needed = False): #bruce 080210 split this out if not (changed_structure_atoms or changed_bond_types): # Note: this will be generalized to: # if no changes of any kind, since the last call # Note: the dna updater processes changes in other dicts, # but we don't need to check those in this function. return # some changes occurred, so this function needed to be called # (even if they turn out to be trivial) if warn_if_needed and env.debug(): # whichever user event handler made these changes forgot to call # this function when it was done! # [as of 071115 warn_if_needed is never true; see docstring] print "atom_debug: _master_model_updater should have been called " \ "before this call (since the most recent model changes), " \ "but wasn't!" #e use print_compact_stack?? pass # (other than printing this, we handle unreported changes normally) # handle and clear all changes since the last call # (in the proper order, when there might be more than one kind of change) # Note: reloading this module won't work, the way this code is currently # structured. If reloading is needed, this routine needs to be # unregistered prior to the reload, and reregistered afterwards. # Also, note that the module might be reloading itself, so be careful. if changed_structure_atoms: update_bonds_after_each_event( changed_structure_atoms) #bruce 060315 revised following comments: # Note: this can modify changed_bond_types (from bond-inference, # if we implement that in the future, or from correcting # illegal bond types, in the present code). # SOMEDAY: I'm not sure if that routine will need to use or change # other similar globals in this module; if it does, passing just # that one might be a bit silly (so we could pass none, or all # affected ones) changed_structure_atoms.clear() if changed_bond_types: # WARNING: this dict may have been modified by the above loop # which processes changed_structure_atoms... process_changed_bond_types( changed_bond_types) # REVIEW: our interface to that function needs review if it can # recursively add bonds to this dict -- if so, it should .clear, # not us! changed_bond_types.clear() return # from _run_bond_updater
def default_include_dir( ): #bruce 060710 split out and revised Mark's & Will's code for this in write_povray_ini_file """ The user did not specify an include dir, so guess one from the POV-Ray path (after verifying it's set). Return 0, include_dir or errorcode, errortext. If not having one is deemed worthy of only a warning, not an error, emit the warning and return 0 [nim]. """ # Motivation: # If MegaPOV is enabled, the Library_Path option must be added and set to the POV-Ray/include # directory in the INI. This is so MegaPOV can find the include file "transforms.inc". Mark 060628. # : Povray also needs transforms.inc - wware 060707 # : : [but when this is povray, it might know where it is on its own (its own include dir)? not sure. bruce 060707] # : : [it looks like it would not know that in the Mac GUI version (which NE1 has no way of supporting, # since external programs can't pass it arguments); I don't know about Unix/Linux or Windows. bruce 060710] if not this_platform_can_guess_include_dir_from_povray_path(): # this runs on Mac return 1, "Can't guess include dir from POV-Ray executable\npath on this platform; please set it explicitly" povray_path = env.prefs[povray_path_prefs_key] if not povray_path: return 1, "Either the POV include directory or the POV-Ray\nexecutable path must be set (even when using MegaPOV)" #e in future, maybe we could use one from POV-Ray, even if it was not enabled, so don't preclude this here try: # try to guess the include directory (include_dir) from povray_path; exception if you fail if sys.platform == 'win32': # Windows povray_bin, povray_exe = os.path.split(povray_path) povray_dir, bin = os.path.split(povray_bin) include_dir = os.path.normpath(os.path.join(povray_dir, "include")) elif sys.platform == 'darwin': # Mac assert 0 else: # Linux povray_bin = povray_path.split( os.path.sep) # list of pathname components assert povray_bin[-2] == 'bin' and povray_bin[ -1] == 'povray' # this is the only kind of path we can do this for include_dir = os.path.sep.join(povray_bin[:-2] + ['share', 'povray-3.6', 'include']) return 0, include_dir except: if env.debug( ) and this_platform_can_guess_include_dir_from_povray_path(): print_compact_traceback( "debug fyi: this is the exception inside default_include_dir: " ) msg = "Unable to guess POV include directory from\nPOV-Ray executable path; please set it explicitly" return 1, msg pass
def set_view_for_Undo( self, assy, namedView ): # shares code with NamedView.set_view; might be very similar to some GLPane method, too """ Restore the view (and the current Part) to what was saved by current_view_for_Undo. WARNING: present implem of saving current Part (using its index in MT) is not suitable for out-of-order Redo. WARNING: might not gl_update, assume caller does so [#k obs warning?] """ ## compare to NamedView.set_view (which passes animate = True) -- not sure if we want to animate in this case [we do, for A8], # but if we do, we might have to do that at a higher level in the call chain restore_view = env.prefs[undoRestoreView_prefs_key] #060314 restore_current_part = True # always do this no matter what ## restore_mode?? nah (not for A7 anyway; unclear what's best in long run) if restore_view: if type(namedView) == type(""): #####@@@@@ code copied from GLPane.__init__, should be shared somehow, or at least comment GLPane and warn it's copied #e also might not be the correct view, it's just the hardcoded default view... but i guess it's correct. # rotation self.quat = Q(1, 0, 0, 0) # point of view (i.e. negative of center of view) self.pov = V(0.0, 0.0, 0.0) # half-height of window in Angstroms (gets reset by certain #view-changing operations [bruce 050615 comment]) #@REVIEW: Should self.scale here should be set from #startup_GLPane_scale_prefs_key ?? self.scale = 10.0 # zoom factor self.zoomFactor = 1.0 else: self.animateToView(namedView.quat, namedView.scale, namedView.pov, namedView.zoomFactor, animate=False) # if we want this to animate, we probably have to move that higher in the call chain and do it after everything else if restore_current_part: if type(namedView) == type(""): if env.debug(): print "debug: fyi: cys == '' still happens" # does it? ###@@@ 060314 remove if seen, or if not seen current_selgroup_index = 0 else: current_selgroup_index = namedView.current_selgroup_index sg = assy.selgroup_at_index(current_selgroup_index) assy.set_current_selgroup(sg) #e how might that interact with setting the selection? Hopefully, not much, since selection (if any) should be inside sg. #e should we update_parts? return
def selobj_still_ok(self, glpane): res = self.__class__ is DirectionArrow if res: our_selobj = self glname = self.glname owner = glpane.assy.object_for_glselect_name(glname) if owner is not our_selobj: res = False # Do debug prints. print "%r no longer owns glname %r, instead %r does" \ % (self, glname, owner) # [perhaps never seen as of 061121] pass pass if not res and env.debug(): print "debug: selobj_still_ok is false for %r" % self return res
def selobj_still_ok(self, glpane): # bugfix: compare to correct class [bruce 070924] res = self.__class__ is ResizeHandle if res: our_selobj = self glname = self.glname owner = env.obj_with_glselect_name.get(glname, None) if owner is not our_selobj: res = False # Do debug prints. print "%r no longer owns glname %r, instead %r does" \ % (self, glname, owner) #[perhaps never seen as of 061121] pass if not res and env.debug(): print "debug: selobj_still_ok is false for %r" % self return res
def set_view_for_Undo(self, assy, namedView): """ Restore the view (and the current Part) to what was saved by current_view_for_Undo. @warning: present implem of saving current Part (using its index in MT) is not suitable for out-of-order Redo. @warning: might not gl_update, assume caller does so [#k obs warning?] """ # shares code with NamedView.set_view; might be very similar to some GLPane method, too ## compare to NamedView.set_view (which passes animate = True) -- not sure if we want # to animate in this case [we do, for A8], # but if we do, we might have to do that at a higher level in the call chain restore_view = env.prefs[undoRestoreView_prefs_key] #060314 restore_current_part = True # always do this no matter what ## restore_mode?? nah (not for A7 anyway; unclear what's best in long run) if restore_view: if type(namedView) == type(""): self._initialize_view_attributes() #bruce 090220 revision to remove copied code; not fully # equivalent to prior code (sets scale from prefs rather # than to 10.0) but I think that's ok, since I think this # functionality (Undo changing the view) is only cosmetic. else: self.animateToView(namedView.quat, namedView.scale, namedView.pov, namedView.zoomFactor, animate=False) # if we want this to animate, we probably have to move that # higher in the call chain and do it after everything else if restore_current_part: if type(namedView) == type(""): if env.debug(): print "debug: fyi: cys == '' still happens" # does it? ###@@@ 060314 remove if seen, or if not seen current_selgroup_index = 0 else: current_selgroup_index = namedView.current_selgroup_index sg = assy.selgroup_at_index(current_selgroup_index) assy.set_current_selgroup(sg) #e how might that interact with setting the selection? # Hopefully, not much, since selection (if any) should be inside sg. #e should we update_parts? return
def set_view_for_Undo( self, assy, namedView ): # shares code with NamedView.set_view; might be very similar to some GLPane method, too """ Restore the view (and the current Part) to what was saved by current_view_for_Undo. WARNING: present implem of saving current Part (using its index in MT) is not suitable for out-of-order Redo. WARNING: might not gl_update, assume caller does so [#k obs warning?] """ ## compare to NamedView.set_view (which passes animate = True) -- not sure if we want to animate in this case [we do, for A8], # but if we do, we might have to do that at a higher level in the call chain restore_view = env.prefs[undoRestoreView_prefs_key] # 060314 restore_current_part = True # always do this no matter what ## restore_mode?? nah (not for A7 anyway; unclear what's best in long run) if restore_view: if type(namedView) == type(""): #####@@@@@ code copied from GLPane.__init__, should be shared somehow, or at least comment GLPane and warn it's copied # e also might not be the correct view, it's just the hardcoded default view... but i guess it's correct. # rotation self.quat = Q(1, 0, 0, 0) # point of view (i.e. negative of center of view) self.pov = V(0.0, 0.0, 0.0) # half-height of window in Angstroms (gets reset by certain # view-changing operations [bruce 050615 comment]) # @REVIEW: Should self.scale here should be set from # startup_GLPane_scale_prefs_key ?? self.scale = 10.0 # zoom factor self.zoomFactor = 1.0 else: self.animateToView(namedView.quat, namedView.scale, namedView.pov, namedView.zoomFactor, animate=False) # if we want this to animate, we probably have to move that higher in the call chain and do it after everything else if restore_current_part: if type(namedView) == type(""): if env.debug(): print "debug: fyi: cys == '' still happens" # does it? ###@@@ 060314 remove if seen, or if not seen current_selgroup_index = 0 else: current_selgroup_index = namedView.current_selgroup_index sg = assy.selgroup_at_index(current_selgroup_index) assy.set_current_selgroup(sg) # e how might that interact with setting the selection? Hopefully, not much, since selection (if any) should be inside sg. # e should we update_parts? return
def set_view_for_Undo(self, assy, namedView): """ Restore the view (and the current Part) to what was saved by current_view_for_Undo. @warning: present implem of saving current Part (using its index in MT) is not suitable for out-of-order Redo. @warning: might not gl_update, assume caller does so [#k obs warning?] """ # shares code with NamedView.set_view; might be very similar to some GLPane method, too ## compare to NamedView.set_view (which passes animate = True) -- not sure if we want # to animate in this case [we do, for A8], # but if we do, we might have to do that at a higher level in the call chain restore_view = env.prefs[undoRestoreView_prefs_key] #060314 restore_current_part = True # always do this no matter what ## restore_mode?? nah (not for A7 anyway; unclear what's best in long run) if restore_view: if type(namedView) == type(""): self._initialize_view_attributes() #bruce 090220 revision to remove copied code; not fully # equivalent to prior code (sets scale from prefs rather # than to 10.0) but I think that's ok, since I think this # functionality (Undo changing the view) is only cosmetic. else: self.animateToView(namedView.quat, namedView.scale, namedView.pov, namedView.zoomFactor, animate = False) # if we want this to animate, we probably have to move that # higher in the call chain and do it after everything else if restore_current_part: if type(namedView) == type(""): if env.debug(): print "debug: fyi: cys == '' still happens" # does it? ###@@@ 060314 remove if seen, or if not seen current_selgroup_index = 0 else: current_selgroup_index = namedView.current_selgroup_index sg = assy.selgroup_at_index(current_selgroup_index) assy.set_current_selgroup(sg) #e how might that interact with setting the selection? # Hopefully, not much, since selection (if any) should be inside sg. #e should we update_parts? return
def _same_Numeric_array_helper(obj1, obj2): if obj1.typecode() != obj2.typecode(): raise _NotTheSame if obj1.shape != obj2.shape: raise _NotTheSame if obj1.typecode() == PyObject: if env.debug(): print "atom_debug: ran _same_Numeric_array_helper, PyObject case" # remove when works once ###@@@ # assume not multi-dimensional (if we are, this should work [untested] but it will be inefficient) for i in xrange(len(obj1)): _same_vals_helper(obj1[i], obj2[i]) # two PyObjects (if obj1 is 1-dim) or two lower-dim Numeric arrays else: ## if env.debug(): ## print "atom_debug: ran _same_Numeric_array_helper, non-PyObject case" # remove when works once ###@@@ if obj1 != obj2: # take pointwise !=, then boolean value of that (correct, but is there a more efficient Numeric function?) # note: using '==' here (and negating boolean value of result) would NOT be correct raise _NotTheSame return
def ok_btn_clicked(self): """ Slot for OK button """ QDialog.accept(self) if env.debug(): print "ok" self.gather_parameters() ### kluge: has side effect on env.prefs # (should we pass these as arg to Minimize_CommandRun rather than thru env.prefs??) if debug_flags.atom_debug: print "debug: reloading runSim & sim_commandruns on each use, for development" import simulation.runSim as runSim reload_once_per_event(runSim) # bug: only works some of the times runSim.py is modified, # don't know why; might be that sim_commandruns.py # also needs to be modified, but touching them both # doesn't seem to work consistently either. # [bruce 080520] import simulation.sim_commandruns as sim_commandruns reload_once_per_event(sim_commandruns) from simulation.sim_commandruns import Minimize_CommandRun # do this in gather? if self.minimize_all_rbtn.isChecked(): self.seltype = 'All' seltype_name = "All" else: self.seltype = 'Sel' seltype_name = "Selection" self.win.assy.current_command_info( cmdname=self.plain_cmdname + " (%s)" % seltype_name) # cmdname for Undo update_cond = self.ruc.get_update_cond_from_widgets() engine = self.minimize_engine_combobox.currentIndex() env.prefs[Minimize_minimizationEngine_prefs_key] = engine cmdrun = Minimize_CommandRun(self.win, self.seltype, type='Minimize', update_cond=update_cond, engine=engine) cmdrun.run() return
def default_include_dir(): #bruce 060710 split out and revised Mark's & Will's code for this in write_povray_ini_file """ The user did not specify an include dir, so guess one from the POV-Ray path (after verifying it's set). Return 0, include_dir or errorcode, errortext. If not having one is deemed worthy of only a warning, not an error, emit the warning and return 0 [nim]. """ # Motivation: # If MegaPOV is enabled, the Library_Path option must be added and set to the POV-Ray/include # directory in the INI. This is so MegaPOV can find the include file "transforms.inc". Mark 060628. # : Povray also needs transforms.inc - wware 060707 # : : [but when this is povray, it might know where it is on its own (its own include dir)? not sure. bruce 060707] # : : [it looks like it would not know that in the Mac GUI version (which NE1 has no way of supporting, # since external programs can't pass it arguments); I don't know about Unix/Linux or Windows. bruce 060710] if not this_platform_can_guess_include_dir_from_povray_path(): # this runs on Mac return 1, "Can't guess include dir from POV-Ray executable\npath on this platform; please set it explicitly" povray_path = env.prefs[povray_path_prefs_key] if not povray_path: return 1, "Either the POV include directory or the POV-Ray\nexecutable path must be set (even when using MegaPOV)" #e in future, maybe we could use one from POV-Ray, even if it was not enabled, so don't preclude this here try: # try to guess the include directory (include_dir) from povray_path; exception if you fail if sys.platform == 'win32': # Windows povray_bin, povray_exe = os.path.split(povray_path) povray_dir, bin = os.path.split(povray_bin) include_dir = os.path.normpath(os.path.join(povray_dir, "include")) elif sys.platform == 'darwin': # Mac assert 0 else: # Linux povray_bin = povray_path.split(os.path.sep) # list of pathname components assert povray_bin[-2] == 'bin' and povray_bin[-1] == 'povray' # this is the only kind of path we can do this for include_dir = os.path.sep.join(povray_bin[:-2] + ['share', 'povray-3.6', 'include']) return 0, include_dir except: if env.debug() and this_platform_can_guess_include_dir_from_povray_path(): print_compact_traceback("debug fyi: this is the exception inside default_include_dir: ") msg = "Unable to guess POV include directory from\nPOV-Ray executable path; please set it explicitly" return 1, msg pass
def make_dialog_if_needed(self): """ Create self.dialog if necessary. """ # For developers, remake the dialog from its description file each time that file changes. # (The point of only remaking it then is not speed, but to test the code when it doesn't get remade, # since that's what always happens for non-developers.) # (Someday, when remaking it, copy its window geometry from the old one. Then put that code into the MMKit too. ###e) # For others, only make it the first time. if (EndUser.enableDeveloperFeatures() or env.debug()) and self.dialog: # For developers, remake the dialog if its description file changed (by zapping the old dialog here). zapit = False modtime = os.stat(self.param_desc_path).st_mtime if modtime != self.param_desc_path_modtime: zapit = True self.param_desc_path_modtime = modtime if zapit: #e save geometry? self.dialog.hide() self.dialog.destroy() ###k self.dialog = None pass if not self.dialog: if debug_run(): print "making dialog from", self.parameter_set_filename dialog_env = self # KLUGE... it needs to be something with an imagename_to_pixmap function that knows our icon_path. # the easiest way to make one is self... in future we want our own env, and to modify it by inserting that path... if use_property_pane(): # experimental, doesn't yet work [060623] parent = self.win.vsplitter2 ###@@@ could this parent be wrong? it acted like parent was self.win or so. clas = ParameterPane ###@@@ worked internally, buttons printed debug msgs, but didn't have any effects in GBC. else: # usual case parent = self.win clas = ParameterDialog self.dialog = clas( self.win, self.param_desc_path, env = dialog_env ) # this parses the description file and makes the dialog, # but does not show it and does not connect a controller to it. #e set its geometry if that was saved (from above code or maybe in prefs db) return
def _same_Numeric_array_helper(obj1, obj2): if obj1.typecode() != obj2.typecode(): raise _NotTheSame if obj1.shape != obj2.shape: raise _NotTheSame if obj1.typecode() == PyObject: if env.debug(): print "atom_debug: ran _same_Numeric_array_helper, PyObject case" # remove when works once ###@@@ # assume not multi-dimensional (if we are, this should work [untested] but it will be inefficient) for i in xrange(len(obj1)): _same_vals_helper( obj1[i], obj2[i] ) # two PyObjects (if obj1 is 1-dim) or two lower-dim Numeric arrays else: ## if env.debug(): ## print "atom_debug: ran _same_Numeric_array_helper, non-PyObject case" # remove when works once ###@@@ if obj1 != obj2: # take pointwise !=, then boolean value of that (correct, but is there a more efficient Numeric function?) # note: using '==' here (and negating boolean value of result) would NOT be correct raise _NotTheSame return
def _in_event_loop_changed( self, beginflag, infodict, tracker): # 060127; 060321 added infodict to API "[this bound method will be added to env.command_segment_subscribers so as to be told when ..." # infodict is info about the nature of the stack change, passed from the tracker [bruce 060321 for bug 1440 et al] # this makes "report all checkpoints" useless -- too many null ones. # maybe i should make it only report if state changes or cmdname passed... if not self.active: self.__begin_retval = False #k probably doesn't matter return True # unsubscribe # print beginflag, len(tracker.stack) # typical: True 1; False 0 if 1: #bruce 060321 for bug 1440: we need to not do checkpoints in some cases. Not sure if this is correct re __begin_retval; # if not, either clean it up for that or pass the flag into the checkpoint routine to have it not really do the checkpoint # (which might turn out better for other reasons anyway, like tracking proper cmdnames for changes). ##e pushed = infodict.get('pushed') popped = infodict.get('popped') # zero or one of these exists, and is the op_run just pushed or popped from the stack if pushed is not None: typeflag = pushed.typeflag # entering this guy elif popped is not None: typeflag = popped.typeflag # leaving this guy (entering vs leaving doesn't matter for now) else: typeflag = '' # does this ever happen? (probably not) want_cp = (typeflag != 'beginrec') if not want_cp: if 0 and env.debug(): print "debug: skipping cp as we enter or leave recursive event processing" return # this might be problematic, see above comment [tho it seems to work for now, for Minimize All anyway]; # if it ever is, then instead of returning here, we'll pass want_cp to checkpoint routines below if beginflag: self.__begin_retval = self.undo_checkpoint_before_command() ###e grab cmdname guess from top op_run i.e. from begin_op? yes for debugging; doesn't matter in the end though. else: if self.__begin_retval is None: # print "self.__begin_retval is None" # not a bug, will be normal ... happens with file open (as expected) self.__begin_retval = self.auto_checkpoint_pref() self.undo_checkpoint_after_command(self.__begin_retval) self.__begin_retval = False # should not matter return
def _in_event_loop_changed(self, beginflag, infodict, tracker): # 060127; 060321 added infodict to API "[this bound method will be added to env.command_segment_subscribers so as to be told when ..." # infodict is info about the nature of the stack change, passed from the tracker [bruce 060321 for bug 1440 et al] # this makes "report all checkpoints" useless -- too many null ones. # maybe i should make it only report if state changes or cmdname passed... if not self.active: self.__begin_retval = False #k probably doesn't matter return True # unsubscribe # print beginflag, len(tracker.stack) # typical: True 1; False 0 if 1: #bruce 060321 for bug 1440: we need to not do checkpoints in some cases. Not sure if this is correct re __begin_retval; # if not, either clean it up for that or pass the flag into the checkpoint routine to have it not really do the checkpoint # (which might turn out better for other reasons anyway, like tracking proper cmdnames for changes). ##e pushed = infodict.get('pushed') popped = infodict.get('popped') # zero or one of these exists, and is the op_run just pushed or popped from the stack if pushed is not None: typeflag = pushed.typeflag # entering this guy elif popped is not None: typeflag = popped.typeflag # leaving this guy (entering vs leaving doesn't matter for now) else: typeflag = '' # does this ever happen? (probably not) want_cp = (typeflag != 'beginrec') if not want_cp: if 0 and env.debug(): print "debug: skipping cp as we enter or leave recursive event processing" return # this might be problematic, see above comment [tho it seems to work for now, for Minimize All anyway]; # if it ever is, then instead of returning here, we'll pass want_cp to checkpoint routines below if beginflag: self.__begin_retval = self.undo_checkpoint_before_command() ###e grab cmdname guess from top op_run i.e. from begin_op? yes for debugging; doesn't matter in the end though. else: if self.__begin_retval is None: # print "self.__begin_retval is None" # not a bug, will be normal ... happens with file open (as expected) self.__begin_retval = self.auto_checkpoint_pref() self.undo_checkpoint_after_command( self.__begin_retval ) self.__begin_retval = False # should not matter return
def ok_btn_clicked(self): """ Slot for OK button """ QDialog.accept(self) if env.debug(): print "ok" self.gather_parameters() ### kluge: has side effect on env.prefs # (should we pass these as arg to Minimize_CommandRun rather than thru env.prefs??) if debug_flags.atom_debug: print "debug: reloading runSim & sim_commandruns on each use, for development" import simulation.runSim as runSim reload_once_per_event(runSim) # bug: only works some of the times runSim.py is modified, # don't know why; might be that sim_commandruns.py # also needs to be modified, but touching them both # doesn't seem to work consistently either. # [bruce 080520] import simulation.sim_commandruns as sim_commandruns reload_once_per_event(sim_commandruns) from simulation.sim_commandruns import Minimize_CommandRun # do this in gather? if self.minimize_all_rbtn.isChecked(): self.seltype = 'All' seltype_name = "All" else: self.seltype = 'Sel' seltype_name = "Selection" self.win.assy.current_command_info(cmdname = self.plain_cmdname + " (%s)" % seltype_name) # cmdname for Undo update_cond = self.ruc.get_update_cond_from_widgets() engine = self.minimize_engine_combobox.currentIndex() env.prefs[Minimize_minimizationEngine_prefs_key] = engine cmdrun = Minimize_CommandRun( self.win, self.seltype, type = 'Minimize', update_cond = update_cond, engine = engine) cmdrun.run() return
return mgr.undo_cmds_menuspec(widget) register_debug_menu_command_maker("undo_cmds", undo_cmds_maker) # fyi: this runs once when the first assy is being created, but undo_cmds_maker runs every time the debug menu is put up. # == # some global private state (which probably ought to be undo manager instance vars) try: _editAutoCheckpointing_recursing except: _editAutoCheckpointing_recursing = False # only if we're not reloading -- otherwise, bug when setChecked calls MWsem slot which reloads else: if _editAutoCheckpointing_recursing and env.debug(): pass # print "note: _editAutoCheckpointing_recursing true during reload" # this happens! try: _AutoCheckpointing_enabled # on reload, use old value unchanged (since we often reload automatically during debugging) except: _AutoCheckpointing_enabled = True # this might be changed based on env.prefs whenever an undo_manager gets created [060314] # older comment about that, not fully obs: #e this might be revised to look at env.prefs sometime during app startup, # and to call editAutoCheckpointing (or some part of it) with the proper initial state; # the current code is designed, internally, for checkpointing to be enabled except # for certain intervals, so we might start out True and set this to False when # an undo_manager is created... we'll see; maybe it won't even (or mainly or only) be a global? [060309] def _set_AutoCheckpointing_enabled(enabled):
def begin(self): ## if 1: # 060121 debug code ## try: ## se = self.sender() # this can only be tried when we inherit from QObject, but it always had this exception. ## except RuntimeError: # underlying C/C++ object has been deleted [common, don't yet know why, but have a guess] ## print "no sender" ## pass ## else: ## print "sender",se ## cp_fn = None # None, or a true thing enabling us to call undo_checkpoint_after_command if 1: #060127 in_event_loop = env._in_event_loop mc = env.begin_op( "(wr)" ) # should always change env._in_event_loop to False (or leave it False) assert not env._in_event_loop if in_event_loop: #060121, revised 060127 and cond changed from 1 to in_event_loop #e if necessary we could find out whether innermost op_run in changes.py's stack still *wants* a cmdname to be guessed... # this would be especially important if it turns out this runs in inner calls and guesses it wrong, # overwriting a correct guess from somewhere else... # also don't we need to make sure that the cmd_seg we're guessing for is the right one, somehow??? # doesn't that mean the same as, this begin_op is the one that changed the boundary? (ie call came from event loop?) sender = self.__sender ##print "sender",sender # or could grab its icon for insertion into history from foundation.whatsthis_utilities import map_from_id_QAction_to_featurename fn = map_from_id_QAction_to_featurename.get(id(sender)) # When we used sender rather than id(sender), the UI seemed noticably slower!! # Possible problem with using id() is for temporary items -- when they're gone, # newly allocated ones with same id might seem to have those featurenames. # Perhaps we need to verify the name is still present in the whatsthis text? # But we don't have the item itself here! We could keep it in the value, and then # it would stick around forever anyway so its id wouldn't be reused, # but we'd have a memory leak for dynamic menus. Hmm... maybe we could add our own # key attribute to these items? And also somehow remove temporary ones from this dict # soon after they go away, or when new temp items are created for same featurename? # ... Decision [nim]: use our own key attr, don't bother removing old items from dict, # the leak per-cmenu is smaller than others we have per-user-command. ####@@@@ DOIT if fn: if 1: #experiment 060121 from utilities.debug import print_compact_traceback try: win = env.mainwindow() assert win.initialised # make sure it's not too early assy = win.assy except: if debug_flags.atom_debug: print_compact_traceback( "atom_debug: fyi: normal exception: ") pass # this is normal during init... or at least I thought it would be -- I never actually saw it yet. else: ## begin_retval = assy.undo_checkpoint_before_command(fn) ## cp_fn = fn, begin_retval #e this should include a retval from that method, but must also always be true if 1: #060127 # note, ideally this assy and the one that subscribes to command_segment changes # should be found in the same way (ie that one should sub to this too) -- could this just iterate over # same list and call it differently, with a different flag?? ##e assy.current_command_info( cmdname=fn ) #e cmdname might be set more precisely by the slot we're wrapping if 0: print " featurename =", fn # This works! prints correct names for toolbuttons and main menu items. # Doesn't work for glpane cmenu items, but I bet it will when we fix them to have proper WhatsThis text. # Hmm, how will we do that? There is presently no formal connection between them and the usual qactions # or toolbuttons or whatsthis features for the main UI for the same method. We might have to detect the # identity of the bound method they call as a slot! Not sure if this is possible. If not, we have to set # command names from inside the methods that implement them (not the end of the world), or grab them from # history text (doable). else: #060320 debug code; note, this shows signals that might not need undo cp's, but for almost all signals, # they might in theory need them in the future for some recipients, so it's not usually safe to exclude them. # Instead, someday we'll optimize this more when no changes actually occurred (e.g. detect that above). if 0 and env.debug(): print "debug: wrappedslot found no featurename, signal = %r, sender = %r" % ( self.__signal, sender) ## return cp_fn, mc #060123 revised retval return mc
def set_defaults(self, dict1): if env.debug(): print "debug: set_defaults is nim" ####k is it even sensible w/o a controller being involved?? return
def setDisplay_command(self, widget): win = env.mainwindow() win.setDisplay(self.ind) if env.debug(): print "setDisplay to %r.ind == %r" % (self, self.ind) return
def doMinimize(self, mtype = 1, simaspect = None): #bruce 051115 renamed method from makeMinMovie #bruce 051115 revised docstring to fit current code #e should clean it up more """ Minimize self.part (if simaspect is None -- no longer used) or its given simaspect (simulatable aspect) (used for both Minimize Selection and Minimize All), generating and showing a movie (no longer asked for) or generating and applying to part an xyz file. The mtype flag means: 1 = tell writemovie() to create a single-frame XYZ file. 2 = tell writemovie() to create a multi-frame DPB moviefile. [###@@@ not presently used, might not work anymore] """ assert mtype == 1 #bruce 051115 assert simaspect is not None #bruce 051115 #bruce 050324 made this from the Part method makeMinMovie. suffix = self.part.movie_suffix() if suffix is None: #bruce 050316 temporary kluge; as of circa 050326 this is not used anymore msg = "%s is not yet implemented for clipboard items." % self.word_Minimize env.history.message( redmsg( msg)) return #e use suffix below? maybe no need since it's ok if the same filename is reused for this. # bruce 050325 change: don't use or modify self.assy.current_movie, # since we're not making a movie and don't want to prevent replaying # the one already stored from some sim run. # [this is for mtype == 1 (always true now) and might affect writemovie ###@@@ #k.] # NOTE: the movie object is used to hold params and results from minimize, # even if it makes an xyz file rather than a movie file. # And at the moment it never makes a movie file when called from this code. # [bruce 051115 comment about months-old situation] movie = Movie(self.assy) # do this in writemovie? no, the other call of it needs it passed in # from the dialog... #k # note that Movie class is misnamed since it's really a # SimRunnerAndResultsUser... which might use .xyz or .dpb results... # maybe rename it SimRun? ###e also, it needs subclasses for the # different kinds of sim runs and their results... or maybe it needs # a subobject which has such subclasses -- not yet sure. [bruce 050329] self._movie = movie #bruce 050415 kluge; note that class SimRun does the same thing. # Probably it means that this class, SimRun, and this way of using # class Movie should all be the same, or at least have more links # than they do now. ###@@@ # Set update_cond for controlling realtime update settings for watching # this "movie" (an ongoing sim). There are three possible ways # (soon after A8 only the first one will be used) [bruce 060705]: # - caller specified it. # - if it didn't, use new common code to get it from General Prefs page. # - if that fails, use older code for that. # # WARNING: it turns out this happens whether or not the checkbox pref # says it should -- that is checked separately elsewhere! That's a bug, # since we need to use a different checkbox depending on the command. # let's see if we can consolidate the "enabling flag" into # update_cond itself? so it is None or False if we won't update. # this is now attempted... if env.debug(): print "debug fyi: runSim/sim_commandruns watch_motion update_cond computed here " \ "(even if not watching motion)" #bruce 060705 try: # Only the client code knows where to find the correct realtime # update settings widgets (or someday, knows whether these values # come from widgets at all, vs from a script). # It should figure out the update_cond # (False if we should not watch motion), # and tell us in self.kws['update_cond']. update_cond = self.kws['update_cond'] assert update_cond or (update_cond is False) # a callable or False [remove when works] # WARNING: as of 080321, this apparently fails routinely # for Adjust All, and then the first fallback in the # except clause also fails (userPrefs.watch_motion_buttongroup # attributeerror), and then its fallback finally works. # Cleanup is severely needed. [bruce 080321 comment] except: ## print_compact_traceback("bug ...: ") if env.debug(): print "debug: fyi: sim_commandruns grabbing userPrefs data" # For A8, this is normal, since only (at most) Minimize Energy sets self.kws['update_cond'] itself. # This will be used routinely in A8 by Adjust All and Adjust Selection, and maybe Adjust Atoms (not sure). # # Just get the values from the "Adjust" prefs page. # But at least try to do that using new common code. try: from widgets.widget_controllers import realtime_update_controller userPrefs = env.mainwindow().userPrefs from utilities.prefs_constants import Adjust_watchRealtimeMinimization_prefs_key ###@@@ should depend on command, or be in movie... ruc = realtime_update_controller( ( userPrefs.watch_motion_buttongroup, # Note: watch_motion_buttongroup exists in MinimizeEnergyProp.py # and in SimSetup.py and now its back in Preferences.py, # so this is no longer a bug (for "Adjust All"). [mark 2008-06-04] userPrefs.update_number_spinbox, userPrefs.update_units_combobox ), None, # checkbox ###@@@ maybe not needed, since UserPrefs sets up the connection #k Adjust_watchRealtimeMinimization_prefs_key ) update_cond = ruc.get_update_cond_from_widgets() # note, if those widgets are connected to env.prefs, that's not handled here or in ruc; # I'm not sure if they are. Ideally we'd tell ruc the prefs_keys and have it handle that too, # perhaps making it a long-lived object (though that might not be necessary). assert update_cond or (update_cond is False) # a callable or False except: # even that didn't work. Complain, then fall back to otherwise-obsolete old code. msg = "bug using realtime_update_controller in sim_commandruns, will use older code instead: " print_compact_traceback(msg) # This code works (except for always using the widgets from the General Prefs page, # even for Minimize Energy), but I'll try to replace it with calls to common code. # [bruce 060705] # This code for setting update_cond is duplicated (inexactly) # in SimSetup.createMoviePressed() in SimSetup.py. userPrefs = env.mainwindow().userPrefs update_units = userPrefs.update_units_combobox.currentText() update_number = userPrefs.update_number_spinbox.value() if userPrefs.update_asap_rbtn.isChecked(): update_cond = ( lambda simtime, pytime, nframes: simtime >= max(0.05, min(pytime * 4, 2.0)) ) elif update_units == 'frames': update_cond = ( lambda simtime, pytime, nframes, _nframes = update_number: nframes >= _nframes ) elif update_units == 'seconds': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number: simtime + pytime >= _timelimit ) elif update_units == 'minutes': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 60: simtime + pytime >= _timelimit ) elif update_units == 'hours': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 3600: simtime + pytime >= _timelimit ) else: print "don't know how to set update_cond from (%r, %r)" % (update_number, update_units) update_cond = None # new as of 060705, in this old code if not env.prefs[Adjust_watchRealtimeMinimization_prefs_key]: update_cond = False pass # now do this with update_cond, however it was computed movie.update_cond = update_cond # semi-obs comment, might still be useful [as of 050406]: # Minimize Selection [bruce 050330] (ought to be a distinct # command subclass...) this will use the spawning code in writemovie # but has its own way of writing the mmp file. # To make this clean, we need to turn writemovie into more than one # method of a class with more than one subclass, so we can override # one of them (writing mmp file) and another one (finding atom list). # But to get it working I might just kluge it # by passing it some specialized options... ###@@@ not sure movie._cmdname = self.cmdname #bruce 050415 kluge so writemovie knows proper progress bar caption to use # (not really wrong -- appropriate for only one of several # classes Movie should be split into, i.e. one for the way we're using it here, # to know how to run the sim, which is perhaps really self (a SimRunner), # once the code is fully cleaned up. # [review: is that the same SimRunner which is by 080321 # a real class in runSim?] # write input for sim, and run sim # this also sets movie.alist from simaspect r = writemovie(self.part, movie, mtype, simaspect = simaspect, print_sim_warnings = True, cmdname = self.cmdname, cmd_type = self.cmd_type, useGromacs = self.useGromacs, background = self.background) if r: # We had a problem writing the minimize file. # Simply return (error message already emitted by writemovie). ###k return if mtype == 1: # Load single-frame XYZ file. if (self.useGromacs): if (self.background): return tracefileProcessor = movie._simrun.tracefileProcessor newPositions = readGromacsCoordinates(movie.filename + "-out.gro", movie.alist, tracefileProcessor) else: newPositions = readxyz( movie.filename, movie.alist ) # movie.alist is now created in writemovie [bruce 050325] # retval is either a list of atom posns or an error message string. assert type(newPositions) in [type([]),type("")] if type(newPositions) == type([]): #bruce 060102 note: following code is approximately duplicated somewhere else in this file. movie.moveAtoms(newPositions) # bruce 050311 hand-merged mark's 1-line bugfix in assembly.py (rev 1.135): self.part.changed() # Mark - bugfix 386 self.part.gl_update() else: #bruce 050404: print error message to history env.history.message(redmsg( newPositions)) else: # Play multi-frame DPB movie file. ###@@@ bruce 050324 comment: can this still happen? [no] is it correct [probably not] # (what about changing mode to movieMode, does it ever do that?) [don't know] # I have not reviewed this and it's obviously not cleaned up (since it modifies private movie attrs). # But I will have this change the current movie, which would be correct in theory, i think, and might be needed # before trying to play it (or might be a side effect of playing it, this is not reviewed either). ###e bruce 050428 comment: if self.assy.current_movie exists, should do something like close or destroy it... need to review self.assy.current_movie = movie # If cueMovie() returns a non-zero value, something went wrong loading the movie. if movie.cueMovie(): return movie._play() movie._close() return
def dismissed(self): if env.debug(): print "debug fyi: dismissed -- should we tell owner to destroy us? is it even still there?" ####@@@@ self.destroy() # let's just take the initiative ourselves, though it might cause bugs, maybe we should do it later...
def doMinimize(self, mtype=1, simaspect=None): #bruce 051115 renamed method from makeMinMovie #bruce 051115 revised docstring to fit current code #e should clean it up more """ Minimize self.part (if simaspect is None -- no longer used) or its given simaspect (simulatable aspect) (used for both Minimize Selection and Minimize All), generating and showing a movie (no longer asked for) or generating and applying to part an xyz file. The mtype flag means: 1 = tell writemovie() to create a single-frame XYZ file. 2 = tell writemovie() to create a multi-frame DPB moviefile. [###@@@ not presently used, might not work anymore] """ assert mtype == 1 #bruce 051115 assert simaspect is not None #bruce 051115 #bruce 050324 made this from the Part method makeMinMovie. suffix = self.part.movie_suffix() if suffix is None: #bruce 050316 temporary kluge; as of circa 050326 this is not used anymore msg = "%s is not yet implemented for clipboard items." % self.word_Minimize env.history.message(redmsg(msg)) return #e use suffix below? maybe no need since it's ok if the same filename is reused for this. # bruce 050325 change: don't use or modify self.assy.current_movie, # since we're not making a movie and don't want to prevent replaying # the one already stored from some sim run. # [this is for mtype == 1 (always true now) and might affect writemovie ###@@@ #k.] # NOTE: the movie object is used to hold params and results from minimize, # even if it makes an xyz file rather than a movie file. # And at the moment it never makes a movie file when called from this code. # [bruce 051115 comment about months-old situation] movie = Movie(self.assy) # do this in writemovie? no, the other call of it needs it passed in # from the dialog... #k # note that Movie class is misnamed since it's really a # SimRunnerAndResultsUser... which might use .xyz or .dpb results... # maybe rename it SimRun? ###e also, it needs subclasses for the # different kinds of sim runs and their results... or maybe it needs # a subobject which has such subclasses -- not yet sure. [bruce 050329] self._movie = movie #bruce 050415 kluge; note that class SimRun does the same thing. # Probably it means that this class, SimRun, and this way of using # class Movie should all be the same, or at least have more links # than they do now. ###@@@ # Set update_cond for controlling realtime update settings for watching # this "movie" (an ongoing sim). There are three possible ways # (soon after A8 only the first one will be used) [bruce 060705]: # - caller specified it. # - if it didn't, use new common code to get it from General Prefs page. # - if that fails, use older code for that. # # WARNING: it turns out this happens whether or not the checkbox pref # says it should -- that is checked separately elsewhere! That's a bug, # since we need to use a different checkbox depending on the command. # let's see if we can consolidate the "enabling flag" into # update_cond itself? so it is None or False if we won't update. # this is now attempted... if env.debug(): print "debug fyi: runSim/sim_commandruns watch_motion update_cond computed here " \ "(even if not watching motion)" #bruce 060705 try: # Only the client code knows where to find the correct realtime # update settings widgets (or someday, knows whether these values # come from widgets at all, vs from a script). # It should figure out the update_cond # (False if we should not watch motion), # and tell us in self.kws['update_cond']. update_cond = self.kws['update_cond'] assert update_cond or (update_cond is False ) # a callable or False [remove when works] # WARNING: as of 080321, this apparently fails routinely # for Adjust All, and then the first fallback in the # except clause also fails (userPrefs.watch_motion_buttongroup # attributeerror), and then its fallback finally works. # Cleanup is severely needed. [bruce 080321 comment] except: ## print_compact_traceback("bug ...: ") if env.debug(): print "debug: fyi: sim_commandruns grabbing userPrefs data" # For A8, this is normal, since only (at most) Minimize Energy sets self.kws['update_cond'] itself. # This will be used routinely in A8 by Adjust All and Adjust Selection, and maybe Adjust Atoms (not sure). # # Just get the values from the "Adjust" prefs page. # But at least try to do that using new common code. try: from widgets.widget_controllers import realtime_update_controller userPrefs = env.mainwindow().userPrefs from utilities.prefs_constants import Adjust_watchRealtimeMinimization_prefs_key ###@@@ should depend on command, or be in movie... ruc = realtime_update_controller( ( userPrefs.watch_motion_buttongroup, # Note: watch_motion_buttongroup exists in MinimizeEnergyProp.py # and in SimSetup.py and now its back in Preferences.py, # so this is no longer a bug (for "Adjust All"). [mark 2008-06-04] userPrefs.update_number_spinbox, userPrefs.update_units_combobox), None, # checkbox ###@@@ maybe not needed, since UserPrefs sets up the connection #k Adjust_watchRealtimeMinimization_prefs_key) update_cond = ruc.get_update_cond_from_widgets() # note, if those widgets are connected to env.prefs, that's not handled here or in ruc; # I'm not sure if they are. Ideally we'd tell ruc the prefs_keys and have it handle that too, # perhaps making it a long-lived object (though that might not be necessary). assert update_cond or (update_cond is False ) # a callable or False except: # even that didn't work. Complain, then fall back to otherwise-obsolete old code. msg = "bug using realtime_update_controller in sim_commandruns, will use older code instead: " print_compact_traceback(msg) # This code works (except for always using the widgets from the General Prefs page, # even for Minimize Energy), but I'll try to replace it with calls to common code. # [bruce 060705] # This code for setting update_cond is duplicated (inexactly) # in SimSetup.createMoviePressed() in SimSetup.py. userPrefs = env.mainwindow().userPrefs update_units = userPrefs.update_units_combobox.currentText() update_number = userPrefs.update_number_spinbox.value() if userPrefs.update_asap_rbtn.isChecked(): update_cond = (lambda simtime, pytime, nframes: simtime >= max(0.05, min(pytime * 4, 2.0))) elif update_units == 'frames': update_cond = (lambda simtime, pytime, nframes, _nframes= update_number: nframes >= _nframes) elif update_units == 'seconds': update_cond = ( lambda simtime, pytime, nframes, _timelimit= update_number: simtime + pytime >= _timelimit) elif update_units == 'minutes': update_cond = ( lambda simtime, pytime, nframes, _timelimit= update_number * 60: simtime + pytime >= _timelimit) elif update_units == 'hours': update_cond = ( lambda simtime, pytime, nframes, _timelimit= update_number * 3600: simtime + pytime >= _timelimit) else: print "don't know how to set update_cond from (%r, %r)" % ( update_number, update_units) update_cond = None # new as of 060705, in this old code if not env.prefs[Adjust_watchRealtimeMinimization_prefs_key]: update_cond = False pass # now do this with update_cond, however it was computed movie.update_cond = update_cond # semi-obs comment, might still be useful [as of 050406]: # Minimize Selection [bruce 050330] (ought to be a distinct # command subclass...) this will use the spawning code in writemovie # but has its own way of writing the mmp file. # To make this clean, we need to turn writemovie into more than one # method of a class with more than one subclass, so we can override # one of them (writing mmp file) and another one (finding atom list). # But to get it working I might just kluge it # by passing it some specialized options... ###@@@ not sure movie._cmdname = self.cmdname #bruce 050415 kluge so writemovie knows proper progress bar caption to use # (not really wrong -- appropriate for only one of several # classes Movie should be split into, i.e. one for the way we're using it here, # to know how to run the sim, which is perhaps really self (a SimRunner), # once the code is fully cleaned up. # [review: is that the same SimRunner which is by 080321 # a real class in runSim?] # write input for sim, and run sim # this also sets movie.alist from simaspect r = writemovie(self.part, movie, mtype, simaspect=simaspect, print_sim_warnings=True, cmdname=self.cmdname, cmd_type=self.cmd_type, useGromacs=self.useGromacs, background=self.background) if r: # We had a problem writing the minimize file. # Simply return (error message already emitted by writemovie). ###k return if mtype == 1: # Load single-frame XYZ file. if (self.useGromacs): if (self.background): return tracefileProcessor = movie._simrun.tracefileProcessor newPositions = readGromacsCoordinates( movie.filename + "-out.gro", movie.alist, tracefileProcessor) else: newPositions = readxyz(movie.filename, movie.alist) # movie.alist is now created in writemovie [bruce 050325] # retval is either a list of atom posns or an error message string. assert type(newPositions) in [type([]), type("")] if type(newPositions) == type([]): #bruce 060102 note: following code is approximately duplicated somewhere else in this file. movie.moveAtoms(newPositions) # bruce 050311 hand-merged mark's 1-line bugfix in assembly.py (rev 1.135): self.part.changed() # Mark - bugfix 386 self.part.gl_update() else: #bruce 050404: print error message to history env.history.message(redmsg(newPositions)) else: # Play multi-frame DPB movie file. ###@@@ bruce 050324 comment: can this still happen? [no] is it correct [probably not] # (what about changing mode to movieMode, does it ever do that?) [don't know] # I have not reviewed this and it's obviously not cleaned up (since it modifies private movie attrs). # But I will have this change the current movie, which would be correct in theory, i think, and might be needed # before trying to play it (or might be a side effect of playing it, this is not reviewed either). ###e bruce 050428 comment: if self.assy.current_movie exists, should do something like close or destroy it... need to review self.assy.current_movie = movie # If cueMovie() returns a non-zero value, something went wrong loading the movie. if movie.cueMovie(): return movie._play() movie._close() return
def createMoviePressed(self): """ Creates a DPB (movie) file of the current part. [Actually only saves the params and filename which should be used by the client code (in writemovie?) to create that file.] The part does not have to be saved as an MMP file first, as it used to. """ ###@@@ bruce 050324 comment: Not sure if/when user can rename the file. QDialog.accept(self) if self.simulation_engine_combobox.currentIndex() == 1: # GROMACS was selected as the simulation engine. # # NOTE: This code is just for demo and prototyping purposes - the # real approach will be architected and utilize plugins. # # Brian Helfrich 2007-04-06 # from simulation.GROMACS.GROMACS import GROMACS gmx = GROMACS(self.assy.part) gmx.run("md") else: # NanoDynamics-1 was selected as the simulation engine # errorcode, partdir = self.assy.find_or_make_part_files_directory() self.movie.cancelled = False # This is the only way caller can tell we succeeded. self.movie.totalFramesRequested = self.totalFramesSpinBox.value() self.movie.temp = self.temperatureSpinBox.value() self.movie.stepsper = self.stepsPerFrameDoubleSpinBox.value() * 10.0 self.movie.print_energy = self.potential_energy_checkbox.isChecked() # self.movie.timestep = self.timestepSB.value() # Not supported in Alpha #self.movie.create_movie_file = self.create_movie_file_checkbox.isChecked() # removed for A7 (bug 1729). mark 060321 self.movie.create_movie_file = True # compute update_data and update_cond, using new or old code try: # try new common code for this, bruce 060705 ruc = self.ruc update_cond = ruc.get_update_cond_from_widgets() assert update_cond or (update_cond is False) ###@@@ remove when works, and all the others like this # note, if those widgets are connected to env.prefs, that's not handled here or in ruc; # I'm not sure if they are. Ideally we'd tell ruc the prefs_keys and have it handle that too, # perhaps making it a long-lived object (though that might not be necessary). update_data = ruc.get_update_data_from_widgets() # redundant, but we can remove it when ruc handles prefs except: print_compact_traceback("bug using realtime_update_controller in SimSetup, will use older code instead: ") # this older code can be removed after A8 if we don't see that message #bruce 060530 use new watch_motion rate parameters self.movie.watch_motion = self.watch_motion_groupbox.isChecked() # [deprecated for setattr as of 060705] if env.debug(): print "debug fyi: sim setup watch_motion = %r" % (self.movie.watch_motion,) # This code works, but I'll try to replace it with calls to common code (above). [bruce 060705] # first grab them from the UI update_as_fast_as_possible_data = self.watch_motion_groupbox.selectedId() # 0 means yes, 1 means no (for now) # ( or -1 means neither, but that's prevented by how the button group is set up, at least when it's enabled) update_as_fast_as_possible = (update_as_fast_as_possible_data != 1) update_number = self.update_number_spinbox.value() # 1, 2, etc (or perhaps 0??) update_units = str(self.update_units_combobox.currentText()) # 'frames', 'seconds', 'minutes', 'hours' # for sake of propogating them to the next sim run: update_data = update_number, update_units, update_as_fast_as_possible_data, self.movie.watch_motion ## if env.debug(): ## print "stored _update_data %r into movie %r" % (self.movie._update_data, self.movie) ## print "debug: self.watch_motion_groupbox.selectedId() = %r" % (update_as_fast_as_possible_data,) ## print "debug: self.update_number_spinbox.value() is %r" % self.update_number_spinbox.value() # e.g. 1 ## print "debug: combox text is %r" % str(self.update_units_combobox.currentText()) # e.g. 'frames' # Now figure out what these user settings mean our realtime updating algorithm should be, # as a function to be used for deciding whether to update the 3D view when each new frame is received, # which takes as arguments the time since the last update finished (simtime), the time that update took (pytime), # and the number of frames since then (nframes, 1 or more), and returns a boolean for whether to draw this new frame. # Notes: # - The Qt progress update will be done independently of this, at most once per second (in runSim.py). # - The last frame we expect to receive will always be drawn. (This func may be called anyway in case it wants # to do something else with the info like store it somewhere, or it may not (check runSim.py for details #k), # but its return value will be ignored if it's called for the last frame.) # The details of these functions (and the UI feeding them) might be revised. # This code for setting update_cond is duplicated (inexactly) in Minimize_CommandRun.doMinimize() if update_as_fast_as_possible: # This radiobutton might be misnamed; it really means "use the old code, # i.e. not worse than 20% slowdown, with threshholds". # It's also ambiguous -- does "fast" mean "fast progress" # or "often" (which are opposites)? It sort of means "often". update_cond = ( lambda simtime, pytime, nframes: simtime >= max(0.05, min(pytime * 4, 2.0)) ) elif update_units == 'frames': update_cond = ( lambda simtime, pytime, nframes, _nframes = update_number: nframes >= _nframes ) elif update_units == 'seconds': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number: simtime + pytime >= _timelimit ) elif update_units == 'minutes': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 60: simtime + pytime >= _timelimit ) elif update_units == 'hours': update_cond = ( lambda simtime, pytime, nframes, _timelimit = update_number * 3600: simtime + pytime >= _timelimit ) else: print "don't know how to set update_cond from (%r, %r)" % (update_number, update_units) update_cond = None # revision in this old code, 060705: if not self.movie.watch_motion: update_cond = False del self.movie.watch_motion # let getattr do it # now do this, however we got update_data and update_cond: self.movie._update_data = update_data # for propogating them to the next sim run self.movie.update_cond = update_cond # used this time # end of 060705 changes suffix = self.suffix tStamp = timeStamp() if self.assy.filename and not errorcode: # filename could be an MMP or PDB file. import shutil dir, fil = os.path.split(self.assy.filename) fil, ext = os.path.splitext(fil) self.movie.filename = os.path.join(partdir, fil + '.' + tStamp + suffix + '.dpb') self.movie.origfile = os.path.join(partdir, fil + '.' + tStamp + '.orig' + ext) shutil.copy(self.assy.filename, self.movie.origfile) else: self.movie.filename = os.path.join(self.assy.w.tmpFilePath, "Untitled.%s%s.dpb" % (tStamp, suffix)) # Untitled parts usually do not have a filename #bruce 060601 fix bug 1840, also make params sticky across opening of new files global _stickyParams _stickyParams = FakeMovie(self.movie) # these will be used as default params next time, whether or not this gets aborted return
def raytrace_scene(self, tmpscene = False): """ Render scene. If tmpscene is False, the INI and pov files are written to the 'POV-Ray Scene Files' directory. If tmpscene is True, the INI and pov files are written to a temporary directory (~/Nanorex/POV-Ray). Callers should set <tmpscene> = True when they want to render the scene but don't need to save the files and create a POV-Ray Scene node in the MT (i.e. 'View > POV-Ray'). The caller is responsible for adding the POV-Ray Scene node (self) to the model tree, if desired. Prints any necessary error messages to history; returns nothing. """ #bruce 060710 corrected inaccuracies in docstring cmd = greenmsg("POV-Ray: ") if env.debug(): #bruce 060707 (after Windows A8, before Linux/Mac A8) # compromise with what's best, so it can be ok for A8 even if only on some platforms env.history.message(_graymsg("POV-Ray: ")) env.history.h_update() env.history.widget.update() ###@@@ will this help? is it safe? should h_update do it? ini, pov, out = self.get_povfile_trio(tmpscene) if not ini: ## return 1, "Problem getting POV-Ray filename trio." # [bruce 060710 replaced the above with the following, since it no longer matches the other return statements, or any calls] env.history.message(cmd + redmsg("Problem getting POV-Ray filename trio.")) ###e should fix this to improve the message, by including errortext from get_povfile_trio retval (which is nim) return if tmpscene or not os.path.isfile(self.povrayscene_file): # write a new .pov file and save its name in self # #bruce 060711 comment (about a bug, not yet reported): ###@@@ # If an existing pov file has unexpectedly gone missing, # this code (I think) rerenders the current model, without even informing the user of the apparent error. # That is extremely bad behavior, IMHO. What it ought to do is put up a dialog to inform the # user that the file is missing, and allow one of three actions: cancel, rerender current model, # or browse for the file to try to find it. If that browse is cancelled, it should offer the other # options, or if that finds the file but it's external, it should offer to copy it or make an # external link (or cancel), and then to continue or do no more. All this is desirable for any kind # of file node, not just PovrayScene. As it is, this won't be fixed for Mac A8; don't know about 8.1. self.povrayscene_file = pov writepovfile(self.assy.part, self.assy.o, self.povrayscene_file) # bruce 060711 question (possible bug): what sets self.width, self.height, self.output_type in this case, # if the ones used by writepovfile differ from last time they were set in this node? # Guess: nothing does (bug, not yet reported). ###@@@ # figure out renderer to use (POV-Ray or MegaPOV), its path, and its include_dir # (note: this contains most of the error checks that used to be inside launch_povray_or_megapov) # [bruce 060711 for Mac A8] win = self.assy.w ask_for_help = True # give user the chance to fix problems in the prefs dialog errorcode, errortext_or_info = decode_povray_prefs(win, ask_for_help, greencmd = cmd) if errorcode: errortext = errortext_or_info env.history.message(cmd + redmsg(errortext)) # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711] return info = errortext_or_info (program_nickname, program_path, include_dir) = info pov = self.povrayscene_file ###k btw, is this already true? #k is out equal to whatever in self might store it, if anything? maybe it's not stored in self. write_povray_ini_file(ini, pov, out, info, self.width, self.height, self.output_type) if tmpscene: msg = "Rendering scene. Please wait..." else: msg = "Rendering raytrace image from POV-Ray Scene file. Please wait..." env.history.message(cmd + msg) env.history.h_update() #bruce 060707 (after Windows A8, before Linux/Mac A8): try to make this message visible sooner # (doesn't work well enough, at least on Mac -- do we need to emit it before write_povray_ini_file?) env.history.widget.update() ###@@@ will this help? is it safe? should h_update do it? ###e these history widget updates fail to get it to print. Guess: we'd need qapp process events. Fix after Mac A8. # besides, we need this just before the launch call, not here. if os.path.exists(out): #bruce 060711 in Mac A8 not Windows A8 (probably all of Mac A8 code will also be in Linux A8) #e should perhaps first try moving the file to a constant name, so user could recover it manually if they wanted to #e (better yet, we should also try to avoid this situation when choosing the filename) msg = "Warning: image file already exists; removing it first [%s]" % out env.history.message(cmd + orangemsg(msg)) try: os.remove(out) except: # this code was tested with a fake exception [060712 1041am] msg1 = "Problem removing old image file" msg2a = " [%s]" % out msg2b = "-- will try to overwrite it, "\ "but undetected rendering errors might leave it unchanged [%s]" % out print_compact_traceback("%s: " % (msg1 + msg2a)) msg = redmsg(msg1) + msg2b #e should report the exception text in the history, too env.history.message(msg) pass # Launch raytrace program (POV-Ray or MegaPOV) errorcode, errortext = launch_povray_or_megapov(win, info, ini) if errorcode: env.history.message(cmd + redmsg(errortext)) # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711] return #bruce 060707 (after Windows A8, before Linux/Mac A8): make sure the image file exists. # (On Mac, on that date [not anymore, 060710], we get this far (no error return, or maybe another bug hid one), # but the file is not there.) if not os.path.exists(out): msg = "Error: %s program finished, but failed to produce expected image file [%s]" % (program_nickname, out) env.history.message(cmd + redmsg(msg)) return env.history.message(cmd + "Rendered image: " + out) # Display image in a window. imageviewer = ImageViewer(out, win) #bruce 060707 comment: if the file named <out> doesn't exist, on Mac, # this produces a visible and draggable tiny window, about 3 pixels wide and maybe 30 pixels high. imageviewer.display() return # from raytrace_scene out
def raytrace_scene(self, tmpscene=False): """ Render scene. If tmpscene is False, the INI and pov files are written to the 'POV-Ray Scene Files' directory. If tmpscene is True, the INI and pov files are written to a temporary directory (~/Nanorex/POV-Ray). Callers should set <tmpscene> = True when they want to render the scene but don't need to save the files and create a POV-Ray Scene node in the MT (i.e. 'View > POV-Ray'). The caller is responsible for adding the POV-Ray Scene node (self) to the model tree, if desired. Prints any necessary error messages to history; returns nothing. """ #bruce 060710 corrected inaccuracies in docstring cmd = greenmsg("POV-Ray: ") if env.debug(): #bruce 060707 (after Windows A8, before Linux/Mac A8) # compromise with what's best, so it can be ok for A8 even if only on some platforms env.history.message(_graymsg("POV-Ray: ")) env.history.h_update() env.history.widget.update( ) ###@@@ will this help? is it safe? should h_update do it? ini, pov, out = self.get_povfile_trio(tmpscene) if not ini: ## return 1, "Problem getting POV-Ray filename trio." # [bruce 060710 replaced the above with the following, since it no longer matches the other return statements, or any calls] env.history.message( cmd + redmsg("Problem getting POV-Ray filename trio.")) ###e should fix this to improve the message, by including errortext from get_povfile_trio retval (which is nim) return if tmpscene or not os.path.isfile(self.povrayscene_file): # write a new .pov file and save its name in self # #bruce 060711 comment (about a bug, not yet reported): ###@@@ # If an existing pov file has unexpectedly gone missing, # this code (I think) rerenders the current model, without even informing the user of the apparent error. # That is extremely bad behavior, IMHO. What it ought to do is put up a dialog to inform the # user that the file is missing, and allow one of three actions: cancel, rerender current model, # or browse for the file to try to find it. If that browse is cancelled, it should offer the other # options, or if that finds the file but it's external, it should offer to copy it or make an # external link (or cancel), and then to continue or do no more. All this is desirable for any kind # of file node, not just PovrayScene. As it is, this won't be fixed for Mac A8; don't know about 8.1. self.povrayscene_file = pov writepovfile(self.assy.part, self.assy.o, self.povrayscene_file) # bruce 060711 question (possible bug): what sets self.width, self.height, self.output_type in this case, # if the ones used by writepovfile differ from last time they were set in this node? # Guess: nothing does (bug, not yet reported). ###@@@ # figure out renderer to use (POV-Ray or MegaPOV), its path, and its include_dir # (note: this contains most of the error checks that used to be inside launch_povray_or_megapov) # [bruce 060711 for Mac A8] win = self.assy.w ask_for_help = True # give user the chance to fix problems in the prefs dialog errorcode, errortext_or_info = decode_povray_prefs(win, ask_for_help, greencmd=cmd) if errorcode: errortext = errortext_or_info env.history.message( cmd + redmsg(errortext) ) # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711] return info = errortext_or_info (program_nickname, program_path, include_dir) = info pov = self.povrayscene_file ###k btw, is this already true? #k is out equal to whatever in self might store it, if anything? maybe it's not stored in self. write_povray_ini_file(ini, pov, out, info, self.width, self.height, self.output_type) if tmpscene: msg = "Rendering scene. Please wait..." else: msg = "Rendering raytrace image from POV-Ray Scene file. Please wait..." env.history.message(cmd + msg) env.history.h_update( ) #bruce 060707 (after Windows A8, before Linux/Mac A8): try to make this message visible sooner # (doesn't work well enough, at least on Mac -- do we need to emit it before write_povray_ini_file?) env.history.widget.update( ) ###@@@ will this help? is it safe? should h_update do it? ###e these history widget updates fail to get it to print. Guess: we'd need qapp process events. Fix after Mac A8. # besides, we need this just before the launch call, not here. if os.path.exists( out ): #bruce 060711 in Mac A8 not Windows A8 (probably all of Mac A8 code will also be in Linux A8) #e should perhaps first try moving the file to a constant name, so user could recover it manually if they wanted to #e (better yet, we should also try to avoid this situation when choosing the filename) msg = "Warning: image file already exists; removing it first [%s]" % out env.history.message(cmd + orangemsg(msg)) try: os.remove(out) except: # this code was tested with a fake exception [060712 1041am] msg1 = "Problem removing old image file" msg2a = " [%s]" % out msg2b = "-- will try to overwrite it, "\ "but undetected rendering errors might leave it unchanged [%s]" % out print_compact_traceback("%s: " % (msg1 + msg2a)) msg = redmsg(msg1) + msg2b #e should report the exception text in the history, too env.history.message(msg) pass # Launch raytrace program (POV-Ray or MegaPOV) errorcode, errortext = launch_povray_or_megapov(win, info, ini) if errorcode: env.history.message( cmd + redmsg(errortext) ) # redmsg in Mac A8, orangemsg in Windows A8 [bruce 060711] return #bruce 060707 (after Windows A8, before Linux/Mac A8): make sure the image file exists. # (On Mac, on that date [not anymore, 060710], we get this far (no error return, or maybe another bug hid one), # but the file is not there.) if not os.path.exists(out): msg = "Error: %s program finished, but failed to produce expected image file [%s]" % ( program_nickname, out) env.history.message(cmd + redmsg(msg)) return env.history.message(cmd + "Rendered image: " + out) # Display image in a window. imageviewer = ImageViewer(out, win) #bruce 060707 comment: if the file named <out> doesn't exist, on Mac, # this produces a visible and draggable tiny window, about 3 pixels wide and maybe 30 pixels high. imageviewer.display() return # from raytrace_scene out
def begin(self): ## if 1: # 060121 debug code ## try: ## se = self.sender() # this can only be tried when we inherit from QObject, but it always had this exception. ## except RuntimeError: # underlying C/C++ object has been deleted [common, don't yet know why, but have a guess] ## print "no sender" ## pass ## else: ## print "sender",se ## cp_fn = None # None, or a true thing enabling us to call undo_checkpoint_after_command if 1: #060127 in_event_loop = env._in_event_loop mc = env.begin_op("(wr)") # should always change env._in_event_loop to False (or leave it False) assert not env._in_event_loop if in_event_loop: #060121, revised 060127 and cond changed from 1 to in_event_loop #e if necessary we could find out whether innermost op_run in changes.py's stack still *wants* a cmdname to be guessed... # this would be especially important if it turns out this runs in inner calls and guesses it wrong, # overwriting a correct guess from somewhere else... # also don't we need to make sure that the cmd_seg we're guessing for is the right one, somehow??? # doesn't that mean the same as, this begin_op is the one that changed the boundary? (ie call came from event loop?) sender = self.__sender ##print "sender",sender # or could grab its icon for insertion into history from foundation.whatsthis_utilities import map_from_id_QAction_to_featurename fn = map_from_id_QAction_to_featurename.get(id(sender)) # When we used sender rather than id(sender), the UI seemed noticably slower!! # Possible problem with using id() is for temporary items -- when they're gone, # newly allocated ones with same id might seem to have those featurenames. # Perhaps we need to verify the name is still present in the whatsthis text? # But we don't have the item itself here! We could keep it in the value, and then # it would stick around forever anyway so its id wouldn't be reused, # but we'd have a memory leak for dynamic menus. Hmm... maybe we could add our own # key attribute to these items? And also somehow remove temporary ones from this dict # soon after they go away, or when new temp items are created for same featurename? # ... Decision [nim]: use our own key attr, don't bother removing old items from dict, # the leak per-cmenu is smaller than others we have per-user-command. ####@@@@ DOIT if fn: if 1: #experiment 060121 from utilities.debug import print_compact_traceback try: win = env.mainwindow() assert win.initialised # make sure it's not too early assy = win.assy except: if debug_flags.atom_debug: print_compact_traceback("atom_debug: fyi: normal exception: ") pass # this is normal during init... or at least I thought it would be -- I never actually saw it yet. else: ## begin_retval = assy.undo_checkpoint_before_command(fn) ## cp_fn = fn, begin_retval #e this should include a retval from that method, but must also always be true if 1: #060127 # note, ideally this assy and the one that subscribes to command_segment changes # should be found in the same way (ie that one should sub to this too) -- could this just iterate over # same list and call it differently, with a different flag?? ##e assy.current_command_info(cmdname = fn) #e cmdname might be set more precisely by the slot we're wrapping if 0: print " featurename =", fn # This works! prints correct names for toolbuttons and main menu items. # Doesn't work for glpane cmenu items, but I bet it will when we fix them to have proper WhatsThis text. # Hmm, how will we do that? There is presently no formal connection between them and the usual qactions # or toolbuttons or whatsthis features for the main UI for the same method. We might have to detect the # identity of the bound method they call as a slot! Not sure if this is possible. If not, we have to set # command names from inside the methods that implement them (not the end of the world), or grab them from # history text (doable). else: #060320 debug code; note, this shows signals that might not need undo cp's, but for almost all signals, # they might in theory need them in the future for some recipients, so it's not usually safe to exclude them. # Instead, someday we'll optimize this more when no changes actually occurred (e.g. detect that above). if 0 and env.debug(): print "debug: wrappedslot found no featurename, signal = %r, sender = %r" % (self.__signal, sender) ## return cp_fn, mc #060123 revised retval return mc
sys.path.append("./experimental/pyrex-opengl") binPath = os.path.normpath(os.path.dirname(os.path.abspath(sys.argv[0])) + '/../bin') if binPath not in sys.path: sys.path.append(binPath) quux_module_import_succeeded = False try: import quux # can't be toplevel quux_module_import_succeeded = True if "experimental" in os.path.dirname(quux.__file__): # Should never happen for end users, but if it does we want to print the # warning. if env.debug() or not EndUser.enableDeveloperFeatures(): print "debug: fyi:", \ "Loaded experimental version of C rendering code:", \ quux.__file__ except: quux = None quux_module_import_succeeded = False if env.debug(): #bruce 060323 added condition print "WARNING: unable to import C rendering code (quux module).", \ "Only Python rendering will be available." pass # == class ShapeList_inplace: """
def _capitalize_first_word(words): #bruce 060705 res = words[0].upper() + words[1:] if res == words: if env.debug(): print "debug warning: %r did not change in _capitalize_first_word" % (words,) return res
def dismissed(self): if env.debug(): print "debug fyi: dismissed -- should we tell owner to destroy us? is it even still there?" ####@@@@ self.destroy( ) # let's just take the initiative ourselves, though it might cause bugs, maybe we should do it later...
return mgr.undo_cmds_menuspec(widget) register_debug_menu_command_maker( "undo_cmds", undo_cmds_maker) # fyi: this runs once when the first assy is being created, but undo_cmds_maker runs every time the debug menu is put up. # == # some global private state (which probably ought to be undo manager instance vars) try: _editAutoCheckpointing_recursing except: _editAutoCheckpointing_recursing = False # only if we're not reloading -- otherwise, bug when setChecked calls MWsem slot which reloads else: if _editAutoCheckpointing_recursing and env.debug(): pass # print "note: _editAutoCheckpointing_recursing true during reload" # this happens! try: _AutoCheckpointing_enabled # on reload, use old value unchanged (since we often reload automatically during debugging) except: _AutoCheckpointing_enabled = True # this might be changed based on env.prefs whenever an undo_manager gets created [060314] # older comment about that, not fully obs: #e this might be revised to look at env.prefs sometime during app startup, # and to call editAutoCheckpointing (or some part of it) with the proper initial state; # the current code is designed, internally, for checkpointing to be enabled except # for certain intervals, so we might start out True and set this to False when # an undo_manager is created... we'll see; maybe it won't even (or mainly or only) be a global? [060309] def _set_AutoCheckpointing_enabled( enabled): global _AutoCheckpointing_enabled
sys.path.append("./experimental/pyrex-opengl") binPath = os.path.normpath( os.path.dirname(os.path.abspath(sys.argv[0])) + '/../bin') if binPath not in sys.path: sys.path.append(binPath) quux_module_import_succeeded = False try: import quux # can't be toplevel quux_module_import_succeeded = True if "experimental" in os.path.dirname(quux.__file__): # Should never happen for end users, but if it does we want to print the # warning. if env.debug() or not EndUser.enableDeveloperFeatures(): print "debug: fyi:", \ "Loaded experimental version of C rendering code:", \ quux.__file__ except: quux = None quux_module_import_succeeded = False if env.debug(): #bruce 060323 added condition print "WARNING: unable to import C rendering code (quux module).", \ "Only Python rendering will be available." pass # == class ShapeList_inplace: