def __getitem__(self, key): # KLUGE 1: provide this interface in this class # rather than in a wrapper class. (No harm, AFAIK, but only # because prefs key strings don't overlap any of our # public or private method or attribute names.) # KLUGE 2: get current global graphicsMode # instead of getting it from how we're called. # (See also USE_CURRENT, which also does this, # but in its case it's not a kluge.) ### TODO to fix this kluge: make a "prefs value object" which # wraps this object but stores glpane, to use instead of this object. # But note that this would not work with glpane == a ThumbView # since ThumbView has no graphicsMode attribute! So we'd have to # wrap the "env-providing glpane" instead, i.e. the main one # for any ThumbView in current code. if debug_flags.atom_debug: print "fyi: getting %r from %r" % (key, self) # This happens a lot in Break Strands command, as expected. # See also the historical note about similar code above -- # I mistakenly thought this code had an exception, but it didn't. # (An actual exception here causes Break Strands to not display # strand-ending Ss atoms at all.) # [bruce 081211 comment] win = env.mainwindow() glpane = win.glpane graphicsMode = glpane.graphicsMode context = graphicsMode return self.get_value(key, context)
def statusbar_msg(self, msg_text, repaint=False): """ Show the message I{msg_text} (which must be plain text and short) in the main window's status bar. This only works for plain text messages, not html. If the message is too long, it might make the window become too wide, perhaps off the screen! Thus use this with care. Also, the message might be erased right away by events beyond our control. Thus this is best used only indirectly by self.message with transient_id option, and only for messages coming out almost continuously for some period, e.g. during a drag. @param msg_text: The message for the status bar. @type msg_text: string @param repaint: Forces a repaint of the status bar with the new message. This doesn't work (see Bruce's comments in code). @param repaint: boolean """ win = env.mainwindow() sbar = win.statusBar() if msg_text: sbar.showMessage(msg_text) else: sbar.clearMessage() if repaint: ## this didn't work, so don't do it until I know why it didn't work: ## sbar.repaint() ## #k will this help extrude show its transient msgs upon entry? # so do this instead: print msg_text return
def statusbar_msg(self, msg_text, repaint = False): """ Show the message I{msg_text} (which must be plain text and short) in the main window's status bar. This only works for plain text messages, not html. If the message is too long, it might make the window become too wide, perhaps off the screen! Thus use this with care. Also, the message might be erased right away by events beyond our control. Thus this is best used only indirectly by self.message with transient_id option, and only for messages coming out almost continuously for some period, e.g. during a drag. @param msg_text: The message for the status bar. @type msg_text: string @param repaint: Forces a repaint of the status bar with the new message. This doesn't work (see Bruce's comments in code). @param repaint: boolean """ win = env.mainwindow() sbar = win.statusBar() if msg_text: sbar.showMessage(msg_text) else: sbar.clearMessage() if repaint: ## this didn't work, so don't do it until I know why it didn't work: ## sbar.repaint() ## #k will this help extrude show its transient msgs upon entry? # so do this instead: print msg_text return
def debug_make_BorrowerChunk_raw(do_addmol=True): win = env.mainwindow() atomset = win.assy.selatoms if not atomset: env.history.message( redmsg( "Need selected atoms to make a BorrowerChunk (for debugging only)" )) else: atomset = dict( atomset ) # copy it, since we shouldn't really add singlets to assy.selatoms... for atom in atomset.values( ): # not itervalues, we're changing it in the loop! # BTW Python is nicer about this than I expected: # exceptions.RuntimeError: dictionary changed size during iteration for bp in atom.singNeighbors( ): # likely bugs if these are not added into the set! atomset[bp.key] = bp assy = atom.molecule.assy # these are all the same, and we do this at least once chunk = BorrowerChunk(assy, atomset) if do_addmol: win.assy.addmol(chunk) import __main__ __main__._bc = chunk env.history.message( orangemsg("__main__._bc = %s (for debugging only)" % quote_html(safe_repr(chunk)))) win.win_update() #k is this done by caller? return
def pref_draw_internal_markers(): res = debug_pref("DNA: draw internal DnaMarkers?", #bruce 080317 revised text Choice_boolean_False, non_debug = True, prefs_key = "A10/DNA: draw internal markers?", # changed, bruce 080317 call_with_new_value = (lambda val: env.mainwindow().glpane.gl_update()) ) return res
def __getitem__(self, key): # get the "current glpane" and its graphicsMode # (for purposes of determining modes & prefs) # (how we do it is not necessarily a kluge, given that this is being # used to define a constant value for use when a better one was # not passed) if debug_flags.atom_debug: print "fyi: getting %r from %r" % (key, self) # This happens 5 or 6 times when entering Build Atoms command; # not sure why, but probably ok. # Historical note: On 081003 I fixed what I thought was a typo below # (mainWindow -> mainwindow), here and in another place below, # and was surprised that this had no effect, and wondered why the # prior presumed exception had been silently discarded. In fact, # it was not a typo -- mainWindow is an alias for mainwindow in env. # So there was no exception and there is no mystery. # (An actual exception here causes, at least, a bug when hovering # over a 3' strand end arrowhead, in Select Chunks mode.) # [bruce 081211 comment] win = env.mainwindow() glpane = win.glpane graphicsMode = glpane.graphicsMode # let the graphicsMode interpret the prefs key, # in case it wants to override it with a local pref # (or conceivably, someday, track it in a different way) # (note, ThumbView has no graphicsMode, but that doesn't # affect this code since it uses main glpane even when # drawing into a ThumbView. [bruce 080606 comment]) try: res = graphicsMode.get_prefs_value(key) except: msg = "bug: exception in %r.get_prefs_value(%r), %s" % (graphicsMode, key, "falling back to env.prefs") print_compact_traceback(msg + ": ") res = env.prefs[key] return res
def _dialogToOfferPluginPrefsFixup(caption, text): """ [private helper for _fixPluginProblem()] Offer the user a chance to fix a plugin problem. @param caption: The dialog border caption. @type caption: text @param text: The dialog text. @type text: text @return: 0 if they accept (after letting them try), 1 if they decline. @rtype: int """ win = env.mainwindow() ret = QMessageBox.warning(win, caption, text, "&OK", "Cancel", "", 0, 1 ) if ret == 0: # User clicked "OK" win.userPrefs.show(pagename = 'Plug-ins') # Show Preferences | Plug-in. return 0 # let caller figure out whether user fixed the problem elif ret == 1: # User clicked "Cancel" return 1 pass
def _dialogToOfferPluginPrefsFixup(caption, text): """ [private helper for _fixPluginProblem()] Offer the user a chance to fix a plugin problem. @param caption: The dialog border caption. @type caption: text @param text: The dialog text. @type text: text @return: 0 if they accept (after letting them try), 1 if they decline. @rtype: int """ win = env.mainwindow() ret = QMessageBox.warning(win, caption, text, "&OK", "Cancel", "", 0, 1) if ret == 0: # User clicked "OK" win.userPrefs.show(pagename='Plug-ins') # Show Preferences | Plug-in. return 0 # let caller figure out whether user fixed the problem elif ret == 1: # User clicked "Cancel" return 1 pass
def _master_model_updater(warn_if_needed=False): """ This should be called at the end of every user event which might have changed anything in any loaded model which defers some updates to this function. This can also be called at the beginning of user events, such as redraws or saves, which want to protect themselves from event-processors which should have called this at the end, but forgot to. Those callers should pass warn_if_needed = True, to cause a debug-only warning to be emitted if the call was necessary. (This function is designed to be very fast when called more times than necessary.) This should also be called before taking Undo checkpoints, to make sure they correspond to legal structures, and so this function's side effects (and the effect of assy.changed, done by this function as of 060126(?)) are treated as part of the same undoable operation as the changes that required them to be made. As of 060127 this is done by those checkpoints calling update_parts, which indirectly calls this function. In practice, as of 071115, we have not yet tried to put in calls to this function at the end of user event handlers, so we rely on the other calls mentioned above, and none of them pass warn_if_needed. """ # 0. Don't run while mmp file is being read [###FIX, use one global flag] if 1: # KLUGE; the changedicts and updater should really be per-assy... # this is a temporary scheme for detecting the unanticipated # running of this in the middle of loading an mmp file, and for # preventing errors from that, # but it only works for the main assy -- not e.g. for a partlib # assy. I don't yet know if this is needed. [bruce 080117] #update 080319: just in case, I'm fixing the mmpread code # to also use the global assy to store this. kluge_main_assy = env.mainwindow().assy if not kluge_main_assy.assy_valid: global_model_changedicts.status_of_last_dna_updater_run = LAST_RUN_DIDNT_HAPPEN msg = "deferring _master_model_updater(warn_if_needed = %r) " \ "since not %r.assy_valid" % (warn_if_needed, kluge_main_assy) print_compact_stack(msg + ": ") # soon change to print... return pass env.history.emit_all_deferred_summary_messages() #bruce 080212 (3 places) _run_dna_updater() env.history.emit_all_deferred_summary_messages() _run_bond_updater(warn_if_needed=warn_if_needed) env.history.emit_all_deferred_summary_messages() _autodelete_empty_groups(kluge_main_assy) env.history.emit_all_deferred_summary_messages() return # from _master_model_updater
def _master_model_updater( warn_if_needed = False ): """ This should be called at the end of every user event which might have changed anything in any loaded model which defers some updates to this function. This can also be called at the beginning of user events, such as redraws or saves, which want to protect themselves from event-processors which should have called this at the end, but forgot to. Those callers should pass warn_if_needed = True, to cause a debug-only warning to be emitted if the call was necessary. (This function is designed to be very fast when called more times than necessary.) This should also be called before taking Undo checkpoints, to make sure they correspond to legal structures, and so this function's side effects (and the effect of assy.changed, done by this function as of 060126(?)) are treated as part of the same undoable operation as the changes that required them to be made. As of 060127 this is done by those checkpoints calling update_parts, which indirectly calls this function. In practice, as of 071115, we have not yet tried to put in calls to this function at the end of user event handlers, so we rely on the other calls mentioned above, and none of them pass warn_if_needed. """ # 0. Don't run while mmp file is being read [###FIX, use one global flag] if 1: # KLUGE; the changedicts and updater should really be per-assy... # this is a temporary scheme for detecting the unanticipated # running of this in the middle of loading an mmp file, and for # preventing errors from that, # but it only works for the main assy -- not e.g. for a partlib # assy. I don't yet know if this is needed. [bruce 080117] #update 080319: just in case, I'm fixing the mmpread code # to also use the global assy to store this. kluge_main_assy = env.mainwindow().assy if not kluge_main_assy.assy_valid: global_model_changedicts.status_of_last_dna_updater_run = LAST_RUN_DIDNT_HAPPEN msg = "deferring _master_model_updater(warn_if_needed = %r) " \ "since not %r.assy_valid" % (warn_if_needed, kluge_main_assy) print_compact_stack(msg + ": ") # soon change to print... return pass env.history.emit_all_deferred_summary_messages() #bruce 080212 (3 places) _run_dna_updater() env.history.emit_all_deferred_summary_messages() _run_bond_updater( warn_if_needed = warn_if_needed) env.history.emit_all_deferred_summary_messages() _autodelete_empty_groups(kluge_main_assy) env.history.emit_all_deferred_summary_messages() return # from _master_model_updater
def _kluge_global_mt_update(): import foundation.env as env # note: this doesn't cause a module import cycle, # but it might be an undesirable inter-package import. # (it's also done in a few other places in this file.) win = env.mainwindow() win.mt.mt_update() return
def refix_whatsthis_text_and_links( ): #bruce 060319 part of fixing bug 1421 """ [public] """ win = env.mainwindow() fix_QAction_whatsthis(win.editUndoAction) fix_QAction_whatsthis(win.editRedoAction) return
def _do_callable_for_undo(func, cmdname): #bruce 060324 """ [private helper for wrap_callable_for_undo] """ assy = env.mainwindow().assy # this needs review once we support multiple open files; # caller might have to pass it in begin_retval = external_begin_cmd_checkpoint(assy, cmdname = cmdname) try: res = func() # note: I don't know whether res matters to Qt except: print_compact_traceback("exception in menu command %r ignored: " % cmdname) # REVIEW this message -- is func always a menu command? res = None assy = env.mainwindow().assy # (since it might have changed! (in theory)) external_end_cmd_checkpoint(assy, begin_retval) return res
def pref_draw_internal_markers(): res = debug_pref( "DNA: draw internal DnaMarkers?", #bruce 080317 revised text Choice_boolean_False, non_debug=True, prefs_key="A10/DNA: draw internal markers?", # changed, bruce 080317 call_with_new_value=(lambda val: env.mainwindow().glpane.gl_update())) return res
def refix_whatsthis_text_and_links(): #bruce 060319 part of fixing bug 1421 """ [public] """ win = env.mainwindow() fix_QAction_whatsthis(win.editUndoAction) fix_QAction_whatsthis(win.editRedoAction) return
def _do_callable_for_undo(func, cmdname): #bruce 060324 """ [private helper for wrap_callable_for_undo] """ assy = env.mainwindow().assy # this needs review once we support multiple open files; # caller might have to pass it in begin_retval = external_begin_cmd_checkpoint(assy, cmdname=cmdname) try: res = func() # note: I don't know whether res matters to Qt except: print_compact_traceback("exception in menu command %r ignored: " % cmdname) # REVIEW this message -- is func always a menu command? res = None assy = env.mainwindow().assy # (since it might have changed! (in theory)) external_end_cmd_checkpoint(assy, begin_retval) return res
def debug_prints_as_dna_updater_ends( runcount): # print "\ndebug_prints_as_dna_updater_ends: %d\n" % ( runcount, ) ## global _found, _found_molecule ## if _found is not None and _found_molecule is not _found.molecule: ## print "\nend %d: %r.molecule = %r" % (runcount, _found, _found.molecule) ## _found_molecule = _found.molecule if debug_flags.DNA_UPDATER_SLOW_ASSERTS: win = env.mainwindow() win.assy.checkparts("end dna updater %d" % runcount) return
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 refix_whatsthis_text_and_links( ): #bruce 060319 part of fixing bug 1421 """ [public] """ import foundation.env as env win = env.mainwindow() from platform_dependent.PlatformDependent import is_macintosh mac = is_macintosh() fix_QAction_whatsthis(win.editUndoAction, mac) fix_QAction_whatsthis(win.editRedoAction, mac) return
def progress_msg(self, msg_text): # Bug 1343, wware 060310 """ Display and/or record a progress message in an appropriate place. @note: for now, the place is always a dedicated label in the statusbar, and these messages are never recorded. In the future this might be revised based on user prefs. """ win = env.mainwindow() statusBar = win.statusBar() statusBar._f_progress_msg(msg_text) #bruce 081229 refactoring
def _force_download(): # Don't check if the MD5 matches. Don't check if the XML file is # older than two days old. Just download it unconditionally. env.history.message(orangemsg("FOR DEBUG ONLY! _force_download() " + "does not respect user privacy preferences.")) xmlfile = os.path.join(_sponsordir, "sponsors.xml") win = env.mainwindow() _download_xml_file(xmlfile) if not os.path.exists(xmlfile): raise Exception("_force_download failed") _load_sponsor_info(xmlfile, win) env.history.message(greenmsg("_force_download() is finished"))
def just_before_mainwindow_init_returns(): # note, misnamed now -- called when its "init in spirit" returns, not its __init__ [060223 comment] if 1: #bruce 060223; logically this would be better to call directly from MWsemantics, but I don't want to modify that file right now import foundation.env as env win = env.mainwindow() win.assy.clear_undo_stack() # necessary to tell it it's safe to make its initial checkpoint, and start recording more #k is the following still active? [060223 question] if not _use_hcmi_hack: return _hcmi.debug_print_stats('mwsem init is returning') return
def refix_whatsthis_text_and_links(): #bruce 060319 part of fixing bug 1421 """ [public] """ import foundation.env as env win = env.mainwindow() from platform_dependent.PlatformDependent import is_macintosh mac = is_macintosh() fix_QAction_whatsthis(win.editUndoAction, mac) fix_QAction_whatsthis(win.editRedoAction, mac) return
def just_before_mainwindow_init_returns(): # note, misnamed now -- called when its "init in spirit" returns, not its __init__ [060223 comment] if 1: #bruce 060223; logically this would be better to call directly from MWsemantics, but I don't want to modify that file right now import foundation.env as env win = env.mainwindow() win.assy.clear_undo_stack( ) # necessary to tell it it's safe to make its initial checkpoint, and start recording more #k is the following still active? [060223 question] if not _use_hcmi_hack: return _hcmi.debug_print_stats('mwsem init is returning') return
def register(subclass): # staticmethod win = env.mainwindow() try: instance = subclass(win) if instance.ok_to_install_in_UI: instance.install_in_UI() if debug_install: print "debug: registered", instance else: if debug_install: print "debug: didn't register", instance except: print_compact_traceback("bug in instantiating %r or installing its instance: " % subclass) return
def _force_download(): # Don't check if the MD5 matches. Don't check if the XML file is # older than two days old. Just download it unconditionally. env.history.message( orangemsg("FOR DEBUG ONLY! _force_download() " + "does not respect user privacy preferences.")) xmlfile = os.path.join(_sponsordir, 'sponsors.xml') win = env.mainwindow() _download_xml_file(xmlfile) if not os.path.exists(xmlfile): raise Exception('_force_download failed') _load_sponsor_info(xmlfile, win) env.history.message(greenmsg("_force_download() is finished"))
def open_wiki_help_dialog(featurename, actually_open=True): #e actually_open = False is presently disabled in the implem """ Show a dialog containing a link which can open the wiki help page corresponding to the named nE-1 feature, in ways influenced by user preferences. Assume the featurename might contain blanks, but contains no other characters needing URL-encoding. [In the future, we might also accept options about the context or specific instance of the feature, which might turn into URL-anchors or the like.] If actually_open = False [not yet implemented, probably won't ever be], don't open a web browser, but instead print a history message so the user can open it manually. Intended use is for a user preference setting to pass this, either always or when the feature is invoked in certain ways. """ url = wiki_help_url(featurename) if url: #bruce 051215 experimental: always use the dialog with a link. # When this works, figure out how prefs should influence what to do, how to clean up the code, etc. # Other known issues: # - UI to access this is unfinished # (F1 key, revise "web help" to "context help" in menu commands, access from Help menu) # - text is a stub; # - maybe need checkbox "retain dialog" so it stays open after the click # - doesn't put new dialog fully in front -- at least, closing mmkit brings main window in front of dialog # - dialog might be nonmodal, but if we keep that, we'll need to autoupdate its contents i suppose html = """Click one of the following links to launch your web browser to a NanoEngineer-1 wiki page containing help on the appropriate topic:<br> - The current command/mode: %s<br> - %s </p>""" % (HTML_link(url, featurename), \ HTML_link(wiki_prefix() + "Main_Page", "The NanoEngineer-1 Wiki main page")) #e in real life it'll be various aspects of your current context def clicked_func(url): worked = open_wiki_help_URL(url) ## close_dialog = worked # not good to not close it on error, unless text in dialog is preserved or replaced with error msg close_dialog = True return close_dialog parent = env.mainwindow( ) # WikiHelpBrowser now in a Dialog, so this works. Fixes bug 1235. mark060322 w = WikiHelpBrowser(html, parent, clicked_func=clicked_func, caption="Web Help") w.show() return ## if not actually_open: ## not yet used (and untested) as of 051201 ## env.history.message("Help for %r is available at: %s" % (featurename, url)) return
def set_initial_AutoCheckpointing_enabled( enabled, update_UI = False, print_to_history = False ): """ set autocheckpointing (perhaps for internal use), doing UI updates only if asked, emitting history only if asked """ if update_UI: win = env.mainwindow() else: # kluge: win is not needed in this case, # and I'm not sure it's not too early win = None editAutoCheckpointing(win, enabled, update_UI = update_UI, print_to_history = print_to_history) # we have the same API as this method except for the option default values 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 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 just_before_event_loop(): """ do post-startup, pre-event-loop, non-profiled things, if any (such as run optional startup commands for debugging) """ #bruce 081003 from utilities.debug_prefs import debug_pref, Choice_boolean_False if debug_pref("startup in Test Graphics command (next session)?", Choice_boolean_False, prefs_key = True ): import foundation.env as env win = env.mainwindow() from commands.TestGraphics.TestGraphics_Command import enter_TestGraphics_Command_at_startup enter_TestGraphics_Command_at_startup( win) pass return
def debug_prints_as_dna_updater_starts( runcount, changed_atoms): # print "\ndebug_prints_as_dna_updater_starts: %d, len %d\n" % \ # (runcount, len(changed_atoms)) ## global _found, _found_molecule ## if _found is None: ## win = env.mainwindow() ## _found = find_atom_by_name(win.assy, 37) ## if _found is not None: ## print "\nfound atom", _found ## if _found is not None and _found_molecule is not _found.molecule: ## print "\nstart %d: %r.molecule = %r" % (runcount, _found, _found.molecule) ## _found_molecule = _found.molecule if debug_flags.DNA_UPDATER_SLOW_ASSERTS: win = env.mainwindow() win.assy.checkparts("start dna updater %d" % runcount) return
def just_before_event_loop(): """ do post-startup, pre-event-loop, non-profiled things, if any (such as run optional startup commands for debugging) """ #bruce 081003 from utilities.debug_prefs import debug_pref, Choice_boolean_False if debug_pref("startup in Test Graphics command (next session)?", Choice_boolean_False, prefs_key=True): import foundation.env as env win = env.mainwindow() from commands.TestGraphics.TestGraphics_Command import enter_TestGraphics_Command_at_startup enter_TestGraphics_Command_at_startup(win) pass return
def open_wiki_help_dialog(featurename, actually_open=True): # e actually_open = False is presently disabled in the implem """ Show a dialog containing a link which can open the wiki help page corresponding to the named nE-1 feature, in ways influenced by user preferences. Assume the featurename might contain blanks, but contains no other characters needing URL-encoding. [In the future, we might also accept options about the context or specific instance of the feature, which might turn into URL-anchors or the like.] If actually_open = False [not yet implemented, probably won't ever be], don't open a web browser, but instead print a history message so the user can open it manually. Intended use is for a user preference setting to pass this, either always or when the feature is invoked in certain ways. """ url = wiki_help_url(featurename) if url: # bruce 051215 experimental: always use the dialog with a link. # When this works, figure out how prefs should influence what to do, how to clean up the code, etc. # Other known issues: # - UI to access this is unfinished # (F1 key, revise "web help" to "context help" in menu commands, access from Help menu) # - text is a stub; # - maybe need checkbox "retain dialog" so it stays open after the click # - doesn't put new dialog fully in front -- at least, closing mmkit brings main window in front of dialog # - dialog might be nonmodal, but if we keep that, we'll need to autoupdate its contents i suppose html = """Click one of the following links to launch your web browser to a NanoEngineer-1 wiki page containing help on the appropriate topic:<br> - The current command/mode: %s<br> - %s </p>""" % ( HTML_link(url, featurename), HTML_link(wiki_prefix() + "Main_Page", "The NanoEngineer-1 Wiki main page"), ) # e in real life it'll be various aspects of your current context def clicked_func(url): worked = open_wiki_help_URL(url) ## close_dialog = worked # not good to not close it on error, unless text in dialog is preserved or replaced with error msg close_dialog = True return close_dialog parent = env.mainwindow() # WikiHelpBrowser now in a Dialog, so this works. Fixes bug 1235. mark060322 w = WikiHelpBrowser(html, parent, clicked_func=clicked_func, caption="Web Help") w.show() return ## if not actually_open: ## not yet used (and untested) as of 051201 ## env.history.message("Help for %r is available at: %s" % (featurename, url)) return
def set_initial_AutoCheckpointing_enabled(enabled, update_UI=False, print_to_history=False): """ set autocheckpointing (perhaps for internal use), doing UI updates only if asked, emitting history only if asked """ if update_UI: win = env.mainwindow() else: # kluge: win is not needed in this case, # and I'm not sure it's not too early win = None editAutoCheckpointing(win, enabled, update_UI=update_UI, print_to_history=print_to_history) # we have the same API as this method except for the option default values return
def mousePressEvent(self, event): """ Dispatches mouse press events depending on shift and control key state. """ ## Huaicai 2/25/05. This is to fix item 2 of bug 400: make this rendering context ## as current, otherwise, the first event will get wrong coordinates self.makeCurrent() buttons, modifiers = event.buttons(), event.modifiers() #print "Button pressed: ", but if 1: #bruce 060328 kluge fix of undo part of bug 1775 (overkill, but should be ok) (part 1 of 2) import foundation.undo_manager as undo_manager main_assy = env.mainwindow().assy self.__begin_retval = undo_manager.external_begin_cmd_checkpoint( main_assy, cmdname="(mmkit)") if buttons & Qt.LeftButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.leftShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.leftCntlDown(event) else: self.leftDown(event) if buttons & Qt.MidButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.middleShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.middleCntlDown(event) else: self.middleDown(event) if buttons & Qt.RightButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.rightShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.rightCntlDown(event) else: pass # self.rightDown(event)
def mousePressEvent(self, event): """ Dispatches mouse press events depending on shift and control key state. """ ## Huaicai 2/25/05. This is to fix item 2 of bug 400: make this rendering context ## as current, otherwise, the first event will get wrong coordinates self.makeCurrent() buttons, modifiers = event.buttons(), event.modifiers() # print "Button pressed: ", but if 1: # bruce 060328 kluge fix of undo part of bug 1775 (overkill, but should be ok) (part 1 of 2) import foundation.undo_manager as undo_manager main_assy = env.mainwindow().assy self.__begin_retval = undo_manager.external_begin_cmd_checkpoint(main_assy, cmdname="(mmkit)") if buttons & Qt.LeftButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.leftShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.leftCntlDown(event) else: self.leftDown(event) if buttons & Qt.MidButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.middleShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.middleCntlDown(event) else: self.middleDown(event) if buttons & Qt.RightButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.rightShiftDown(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.rightCntlDown(event) else: pass # self.rightDown(event)
def enablePeptideGenerator(enable): """ This function enables/disables the Peptide Generator command by hiding or showing it in the Command Manager toolbar and menu. The enabling/disabling is done by the user via the "secret" NE1 debugging menu. To display the secret debugging menu, hold down Shift+Ctrl+Alt keys (or Shift+Cmd+Alt on Mac) and right click over the graphics area. Select "debug prefs submenu > Peptide Generator" and set the value to True. The "Peptide" option will then appear on the "Build" Command Manager toolbar/menu. @param enable: If true, the Peptide Generator is enabled. Specifically, it will be added to the "Build" Command Manager toolbar and menu. @type enable: bool """ win = env.mainwindow() win.insertPeptideAction.setVisible(enable)
def enableAtomGenerator(enable): """ This function enables/disables the Atom Generator command by hiding or showing it in the Command Manager toolbar and menu. The enabling/disabling is done by the user via the "secret" NE1 debugging menu. To display the secret debugging menu, hold down Shift+Ctrl+Alt keys (or Shift+Cmd+Alt on Mac) and right click over the graphics area. Select "debug prefs submenu > Atom Generator example code" and set the value to True. The "Atom" option will then appear on the "Build" Command Manager toolbar/menu. @param enable: If true, the Atom Generator is enabled. Specifically, it will be added to the "Build" Command Manager toolbar and menu. @type enable: bool """ win = env.mainwindow() win.insertAtomAction.setVisible(enable)
def debug_make_BorrowerChunk_raw(do_addmol = True): win = env.mainwindow() atomset = win.assy.selatoms if not atomset: env.history.message(redmsg("Need selected atoms to make a BorrowerChunk (for debugging only)")) else: atomset = dict(atomset) # copy it, since we shouldn't really add singlets to assy.selatoms... for atom in atomset.values(): # not itervalues, we're changing it in the loop! # BTW Python is nicer about this than I expected: # exceptions.RuntimeError: dictionary changed size during iteration for bp in atom.singNeighbors(): # likely bugs if these are not added into the set! atomset[bp.key] = bp assy = atom.molecule.assy # these are all the same, and we do this at least once chunk = BorrowerChunk(assy, atomset) if do_addmol: win.assy.addmol(chunk) import __main__ __main__._bc = chunk env.history.message(orangemsg("__main__._bc = %s (for debugging only)" % quote_html(safe_repr(chunk)))) win.win_update() #k is this done by caller? return
def mouseReleaseEvent(self, event): """ Only used to detect the end of a freehand selection curve. """ buttons, modifiers = event.buttons(), event.modifiers() #print "Button released: ", but if buttons & Qt.LeftButton: if modifiers & Qt.ShiftModifier: pass # self.leftShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.leftCntlUp(event) else: self.leftUp(event) if buttons & Qt.MidButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.middleShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.middleCntlUp(event) else: self.middleUp(event) if buttons & Qt.RightButton: if modifiers & Qt.ShiftModifier: pass # self.rightShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.rightCntlUp(event) else: pass # self.rightUp(event) if 1: #bruce 060328 kluge fix of undo part of bug 1775 (part 2 of 2) import foundation.undo_manager as undo_manager main_assy = env.mainwindow().assy undo_manager.external_end_cmd_checkpoint(main_assy, self.__begin_retval) return
def mouseReleaseEvent(self, event): """ Only used to detect the end of a freehand selection curve. """ buttons, modifiers = event.buttons(), event.modifiers() # print "Button released: ", but if buttons & Qt.LeftButton: if modifiers & Qt.ShiftModifier: pass # self.leftShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.leftCntlUp(event) else: self.leftUp(event) if buttons & Qt.MidButton: if modifiers & Qt.ShiftModifier: pass # self.graphicsMode.middleShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.graphicsMode.middleCntlUp(event) else: self.middleUp(event) if buttons & Qt.RightButton: if modifiers & Qt.ShiftModifier: pass # self.rightShiftUp(event) elif modifiers & Qt.ControlModifier: pass # self.rightCntlUp(event) else: pass # self.rightUp(event) if 1: # bruce 060328 kluge fix of undo part of bug 1775 (part 2 of 2) import foundation.undo_manager as undo_manager main_assy = env.mainwindow().assy undo_manager.external_end_cmd_checkpoint(main_assy, self.__begin_retval) return
def __getitem__(self, key): # get the "current glpane" and its graphicsMode # (for purposes of determining modes & prefs) # (how we do it is not necessarily a kluge, given that this is being # used to define a constant value for use when a better one was # not passed) if debug_flags.atom_debug: print "fyi: getting %r from %r" % (key, self) # This happens 5 or 6 times when entering Build Atoms command; # not sure why, but probably ok. # Historical note: On 081003 I fixed what I thought was a typo below # (mainWindow -> mainwindow), here and in another place below, # and was surprised that this had no effect, and wondered why the # prior presumed exception had been silently discarded. In fact, # it was not a typo -- mainWindow is an alias for mainwindow in env. # So there was no exception and there is no mystery. # (An actual exception here causes, at least, a bug when hovering # over a 3' strand end arrowhead, in Select Chunks mode.) # [bruce 081211 comment] win = env.mainwindow() glpane = win.glpane graphicsMode = glpane.graphicsMode # let the graphicsMode interpret the prefs key, # in case it wants to override it with a local pref # (or conceivably, someday, track it in a different way) # (note, ThumbView has no graphicsMode, but that doesn't # affect this code since it uses main glpane even when # drawing into a ThumbView. [bruce 080606 comment]) try: res = graphicsMode.get_prefs_value(key) except: msg = "bug: exception in %r.get_prefs_value(%r), %s" % \ (graphicsMode, key, "falling back to env.prefs") print_compact_traceback(msg + ": ") res = env.prefs[key] return res
def _readpdb(assy, filename, isInsert = False, showProgressDialog = False, chainId = None): """ Read a Protein DataBank-format file into a single new chunk, which is returned unless there are no atoms in the file, in which case a warning is printed and None is returned. (The new chunk (if returned) is in assy, but is not yet added into any Group or Part in assy -- caller must do that.) Unless isInsert = True, set assy.filename to match the file we read, even if we return None. @param assy: The assembly. @type assy: L{assembly} @param filename: The PDB filename to read. @type filename: string @param isInsert: If True, the PDB file will be inserted into the current assembly. If False (default), the PDB is opened as the assembly. @param isInsert: boolean @param showProgressDialog: if True, display a progress dialog while reading a file. @type showProgressDialog: boolean @return: A chunk containing the contents of the PDB file. @rtype: L{Chunk} @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>} """ fi = open(filename,"rU") lines = fi.readlines() fi.close() dir, nodename = os.path.split(filename) if not isInsert: assy.filename = filename ndix = {} mol = Chunk(assy, nodename) numconects = 0 atomname_exceptions = { "HB":"H", #k these are all guesses -- I can't find this documented # anywhere [bruce 070410] ## "HE":"H", ### REVIEW: I'm not sure about this one -- ### leaving it out means it's read as Helium, # but including it erroneously might prevent reading an actual Helium # if that was intended. # Guess for now: include it for ATOM but not HETATM. (So it's # specialcased below, rather than being included in this table.) # (Later: can't we use the case of the 'E' to distinguish it from He?) "HN":"H", } # Create and display a Progress dialog while reading the MMP file. # One issue with this implem is that QProgressDialog always displays # a "Cancel" button, which is not hooked up. I think this is OK for now, # but later we should either hook it up or create our own progress # dialog that doesn't include a "Cancel" button. --mark 2007-12-06 if showProgressDialog: _progressValue = 0 _progressFinishValue = len(lines) win = env.mainwindow() win.progressDialog.setLabelText("Reading file...") win.progressDialog.setRange(0, _progressFinishValue) _progressDialogDisplayed = False _timerStart = time.time() for card in lines: key = card[:6].lower().replace(" ", "") if key in ["atom", "hetatm"]: ## sym = capitalize(card[12:14].replace(" ", "").replace("_", "")) # bruce 080508 revision (guess at a bugfix for reading NE1-saved # pdb files): # get a list of atomnames to try; use the first one we recognize. # Note that full atom name is in columns 13-16 i.e. card[12:16]; # see http://www.wwpdb.org/documentation/format2.3-0108-us.pdf, # page 156. The old code only looked at two characters, # card[12:14] == columns 13-14, and discarded ' ' and '_', # and capitalized (the first character only). The code as I revised # it on 070410 also discarded digits, and handled HB, HE, HN # (guesses) using the atomname_exceptions dict. name4 = card[12:16].replace(" ", "").replace("_", "") name3 = card[12:15].replace(" ", "").replace("_", "") name2 = card[12:14].replace(" ", "").replace("_", "") def nodigits(name): for bad in "0123456789": name = name.replace(bad, "") return name atomnames_to_try = [ name4, # as seems best according to documentation name3, name2, # like old code nodigits(name4), nodigits(name3), nodigits(name2) # like code as revised on 070410 ] foundit = False for atomname in atomnames_to_try: atomname = atomname_exceptions.get(atomname, atomname) if atomname == "HE" and key == "atom": atomname = "H" # see comment in atomname_exceptions sym = capitalize(atomname) # turns either 'he' or 'HE' into 'He' try: PeriodicTable.getElement(sym) except: # note: this typically fails with AssertionError # (not e.g. KeyError) [bruce 050322] continue else: foundit = True break pass if not foundit: msg = "Warning: Pdb file: will use Carbon in place of unknown element %s in: %s" \ % (name4, card) print msg #bruce 070410 added this print env.history.message( redmsg( msg )) ##e It would probably be better to create a fake atom, so the # CONECT records would still work. #bruce 080508 let's do that: sym = "C" # Better still might be to create a fake element, # so we could write out the pdb file again # (albeit missing lots of info). [bruce 070410 comment] # Note: an advisor tells us: # PDB files sometimes encode atomtypes, # using C_R instead of C, for example, to represent sp2 # carbons. # That particular case won't trigger this exception, since we # only look at 2 characters [eventually, after trying more, as of 080508], # i.e. C_ in that case. It would be better to realize this means # sp2 and set the atomtype here (and perhaps then use it when # inferring bonds, which we do later if the file doesn't have # any bonds). [bruce 060614/070410 comment] # Now the element name is in sym. xyz = map(float, [card[30:38], card[38:46], card[46:54]] ) n = int(card[6:11]) a = Atom(sym, A(xyz), mol) ndix[n] = a elif key == "conect": try: a1 = ndix[int(card[6:11])] except: #bruce 050322 added this level of try/except and its message; # see code below for at least two kinds of errors this might # catch, but we don't try to distinguish these here. BTW this # also happens as a consequence of not finding the element # symbol, above, since atoms with unknown elements are not # created. env.history.message( redmsg( "Warning: Pdb file: can't find first atom in CONECT record: %s" % (card,) )) else: for i in range(11, 70, 5): try: a2 = ndix[int(card[i:i+5])] except ValueError: # bruce 050323 comment: # we assume this is from int('') or int(' ') etc; # this is the usual way of ending this loop. break except KeyError: #bruce 050322-23 added history warning for this, # assuming it comes from ndix[] lookup. env.history.message( redmsg( "Warning: Pdb file: can't find atom %s in: %s" % (card[i:i+5], card) )) continue bond_atoms(a1, a2) numconects += 1 if showProgressDialog: # Update the progress dialog. _progressValue += 1 if _progressValue >= _progressFinishValue: win.progressDialog.setLabelText("Building model...") elif _progressDialogDisplayed: win.progressDialog.setValue(_progressValue) else: _timerDuration = time.time() - _timerStart if _timerDuration > 0.25: # Display progress dialog after 0.25 seconds win.progressDialog.setValue(_progressValue) _progressDialogDisplayed = True if showProgressDialog: # Make the progress dialog go away. win.progressDialog.setValue(_progressFinishValue) #bruce 050322 part of fix for bug 433: don't return an empty chunk if not mol.atoms: env.history.message( redmsg( "Warning: Pdb file contained no atoms")) return None if numconects == 0: msg = orangemsg("PDB file has no bond info; inferring bonds") env.history.message(msg) # let user see message right away (bond inference can take significant # time) [bruce 060620] env.history.h_update() inferBonds(mol) return mol
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 __init__(self, stuff, name = None): assy = env.mainwindow().assy #k wrongheaded?? Node.__init__(self, assy, name)###WRONG now that Node is no longer a superclass self.stuff = stuff # exprs for what to draw (list of objects)
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 _movie_is_playing(self): #bruce 090224 split this out of ChunkDrawer """ [can be overridden in subclasses, but isn't so far] """ # as of 090317, defined and used only in this class return env.mainwindow().movie_is_playing #bruce 051209
def wikiHelp(self): parent = env.mainwindow() w = WikiHelpBrowser(self.text,parent,caption=self.name) w.show()
def _readpdb_new(assy, filename, isInsert = False, showProgressDialog = False, chainId = None): """ Read a Protein DataBank-format file into a single new chunk, which is returned unless there are no atoms in the file, in which case a warning is printed and None is returned. (The new chunk (if returned) is in assy, but is not yet added into any Group or Part in assy -- caller must do that.) Unless isInsert = True, set assy.filename to match the file we read, even if we return None. @param assy: The assembly. @type assy: L{assembly} @param filename: The PDB filename to read. @type filename: string @param isInsert: If True, the PDB file will be inserted into the current assembly. If False (default), the PDB is opened as the assembly. @param isInsert: boolean @param showProgressDialog: if True, display a progress dialog while reading a file. @type showProgressDialog: boolean @return: A chunk containing the contents of the PDB file. @rtype: L{Chunk} @see: U{B{PDB File Format}<http://www.wwpdb.org/documentation/format23/v2.3.html>} """ from protein.model.Protein import is_water def _finish_molecule(): """ Perform some operations after reading entire PDB chain: - rebuild (infer) bonds - rename molecule to reflect a chain ID - delete protein object if this is not a protein - append the molecule to the molecule list """ if mol == water: # Skip water, to be added explicitly at the end. return if mol.atoms: ###print "READING PDB ", (mol, numconects, chainId) mol.name = pdbid.lower() + chainId ###idzialprint "SEQ = ", mol.protein.get_sequence_string() ###print "SEC = ", mol.protein.get_secondary_structure_string() if mol.protein.count_c_alpha_atoms() == 0: # If there is no C-alpha atoms, consider the chunk # as a non-protein. But! Split it into individual # hetero groups. res_list = mol.protein.get_amino_acids() assy.part.ensure_toplevel_group() hetgroup = Group("Heteroatoms", assy, assy.part.topnode) for res in res_list: hetmol = Chunk(assy, res.get_three_letter_code().replace(" ", "") + \ "[" + str(res.get_id()) + "]") for atom in res.get_atom_list(): newatom = Atom(atom.element.symbol, atom.posn(), hetmol) # New chunk - infer the bonds anyway (this is not # correct, should first check connectivity read from # the PDB file CONECT records). inferBonds(hetmol) hetgroup.addchild(hetmol) mollist.append(hetgroup) else: #if numconects == 0: # msg = orangemsg("PDB file has no bond info; inferring bonds") # env.history.message(msg) # # let user see message right away (bond inference can take significant # # time) [bruce 060620] # env.history.h_update() # For protein - infer the bonds anyway. inferBonds(mol) mol.protein.set_chain_id(chainId) mol.protein.set_pdb_id(pdbid) if mol.atoms: mollist.append(mol) else: env.history.message( redmsg( "Warning: Pdb file contained no atoms")) env.history.h_update() fi = open(filename,"rU") lines = fi.readlines() fi.close() mollist = [] # Lists of secondary structure tuples (res_id, chain_id) helix = [] sheet = [] turn = [] dir, nodename = os.path.split(filename) if not isInsert: assy.filename = filename ndix = {} mol = Chunk(assy, nodename) mol.protein = Protein() # Create a chunk for water molecules. water = Chunk(assy, nodename) numconects = 0 comment_text = "" _read_rosetta_info = False # Create a temporary PDB ID - it should be later extracted from the # file header. pdbid = nodename.replace(".pdb","").lower() atomname_exceptions = { "HB":"H", #k these are all guesses -- I can't find this documented # anywhere [bruce 070410] "CA":"C", #k these are all guesses -- I can't find this documented ## "HE":"H", ### REVIEW: I'm not sure about this one -- ### leaving it out means it's read as Helium, # but including it erroneously might prevent reading an actual Helium # if that was intended. # Guess for now: include it for ATOM but not HETATM. (So it's # specialcased below, rather than being included in this table.) # (Later: can't we use the case of the 'E' to distinguish it from He?) "HN":"H", } # Create and display a Progress dialog while reading the MMP file. # One issue with this implem is that QProgressDialog always displays # a "Cancel" button, which is not hooked up. I think this is OK for now, # but later we should either hook it up or create our own progress # dialog that doesn't include a "Cancel" button. --mark 2007-12-06 if showProgressDialog: _progressValue = 0 _progressFinishValue = len(lines) win = env.mainwindow() win.progressDialog.setLabelText("Reading file...") win.progressDialog.setRange(0, _progressFinishValue) _progressDialogDisplayed = False _timerStart = time.time() for card in lines: key = card[:6].lower().replace(" ", "") if key in ["atom", "hetatm"]: ## sym = capitalize(card[12:14].replace(" ", "").replace("_", "")) # bruce 080508 revision (guess at a bugfix for reading NE1-saved # pdb files): # get a list of atomnames to try; use the first one we recognize. # Note that full atom name is in columns 13-16 i.e. card[12:16]; # see http://www.wwpdb.org/documentation/format2.3-0108-us.pdf, # page 156. The old code only looked at two characters, # card[12:14] == columns 13-14, and discarded ' ' and '_', # and capitalized (the first character only). The code as I revised # it on 070410 also discarded digits, and handled HB, HE, HN # (guesses) using the atomname_exceptions dict. name4 = card[12:16].replace(" ", "").replace("_", "") name3 = card[12:15].replace(" ", "").replace("_", "") name2 = card[12:14].replace(" ", "").replace("_", "") chainId = card[21] resIdStr = card[22:26].replace(" ", "") if resIdStr != "": resId = int(resIdStr) else: resId = 0 resName = card[17:20] sym = card[77:78] alt = card[16] # Alternate location indicator if alt != ' ' and \ alt != 'A': # Skip non-standard alternate location # This is not very safe test, it should preserve # the remaining atoms. piotr 080715 continue ###ATOM 131 CB ARG A 18 104.359 32.924 58.573 1.00 36.93 C def nodigits(name): for bad in "0123456789": name = name.replace(bad, "") return name atomnames_to_try = [ name4, # as seems best according to documentation name3, name2, # like old code nodigits(name4), nodigits(name3), nodigits(name2) # like code as revised on 070410 ] # First, look at 77-78 field - it should include an element symbol. foundit = False try: PeriodicTable.getElement(sym) except: pass else: foundit = True if not foundit: for atomname in atomnames_to_try: atomname = atomname_exceptions.get(atomname, atomname) if atomname[0] == 'H' and key == "atom": atomname = "H" # see comment in atomname_exceptions sym = capitalize(atomname) # turns either 'he' or 'HE' into 'He' try: PeriodicTable.getElement(sym) except: # note: this typically fails with AssertionError # (not e.g. KeyError) [bruce 050322] continue else: foundit = True break pass if not foundit: msg = "Warning: Pdb file: will use Carbon in place of unknown element %s in: %s" \ % (name4, card) print msg #bruce 070410 added this print env.history.message( redmsg( msg )) ##e It would probably be better to create a fake atom, so the # CONECT records would still work. #bruce 080508 let's do that: sym = "C" # Better still might be to create a fake element, # so we could write out the pdb file again # (albeit missing lots of info). [bruce 070410 comment] # Note: an advisor tells us: # PDB files sometimes encode atomtypes, # using C_R instead of C, for example, to represent sp2 # carbons. # That particular case won't trigger this exception, since we # only look at 2 characters [eventually, after trying more, as of 080508], # i.e. C_ in that case. It would be better to realize this means # sp2 and set the atomtype here (and perhaps then use it when # inferring bonds, which we do later if the file doesn't have # any bonds). [bruce 060614/070410 comment] _is_water = is_water(resName, name4) if _is_water: tmpmol = mol mol = water # Now the element name is in sym. xyz = map(float, [card[30:38], card[38:46], card[46:54]] ) n = int(card[6:11]) a = Atom(sym, A(xyz), mol) ndix[n] = a if not _is_water: mol.protein.add_pdb_atom(a, name4, resId, resName) # Assign secondary structure. if (resId, chainId) in helix: mol.protein.assign_helix(resId) if (resId, chainId) in sheet: mol.protein.assign_strand(resId) if (resId, chainId) in turn: mol.protein.assign_turn(resId) if mol == water: mol = tmpmol elif key == "conect": try: a1 = ndix[int(card[6:11])] except: #bruce 050322 added this level of try/except and its message; # see code below for at least two kinds of errors this might # catch, but we don't try to distinguish these here. BTW this # also happens as a consequence of not finding the element # symbol, above, since atoms with unknown elements are not # created. env.history.message( redmsg( "Warning: Pdb file: can't find first atom in CONECT record: %s" % (card,) )) else: for i in range(11, 70, 5): try: a2 = ndix[int(card[i:i+5])] except ValueError: # bruce 050323 comment: # we assume this is from int('') or int(' ') etc; # this is the usual way of ending this loop. break except KeyError: #bruce 050322-23 added history warning for this, # assuming it comes from ndix[] lookup. env.history.message( redmsg( "Warning: Pdb file: can't find atom %s in: %s" % (card[i:i+5], card) )) continue bond_atoms(a1, a2) numconects += 1 elif key == "ter": # Finish the current molecule. _finish_molecule() # Discard the original molecule and create a new one. mol = Chunk(assy, nodename) mol.protein = Protein() numconects = 0 elif key == "header": # Extract PDB ID from the header string. pdbid = card[62:66].lower() comment_text += card elif key == "compnd": comment_text += card elif key == "remark": comment_text += card elif key == "model": # Check out the MODEL record, ignore everything other than MODEL 1. # This behavior has to be optional and set via User Preference. # piotr 080714 model_id = int(card[6:20]) if model_id > 1: # Skip remaining part of the file. break elif key in ["helix", "sheet", "turn"]: # Read secondary structure information. if key == "helix": begin = int(card[22:25]) end = int(card[34:37]) chainId = card[19] for s in range(begin, end+1): helix.append((s, chainId)) elif key == "sheet": begin = int(card[23:26]) end = int(card[34:37]) chainId = card[21] for s in range(begin, end+1): sheet.append((s, chainId)) elif key == "turn": begin = int(card[23:26]) end = int(card[34:37]) chainId = card[19] for s in range(begin, end+1): turn.append((s, chainId)) else: if card[7:15] == "ntrials:": _read_rosetta_info = True comment_text += "Rosetta Scoring Analysis\n" if _read_rosetta_info: comment_text += card if showProgressDialog: # Update the progress dialog. _progressValue += 1 if _progressValue >= _progressFinishValue: win.progressDialog.setLabelText("Building model...") elif _progressDialogDisplayed: win.progressDialog.setValue(_progressValue) else: _timerDuration = time.time() - _timerStart if _timerDuration > 0.25: # Display progress dialog after 0.25 seconds win.progressDialog.setValue(_progressValue) _progressDialogDisplayed = True if showProgressDialog: # Make the progress dialog go away. win.progressDialog.setValue(_progressFinishValue) _finish_molecule() if water.atoms: # Check if there are any water molecules water.name = "Solvent" # The water should be hidden by default. water.hide() mollist.append(water) return (mollist, comment_text)