diff options
author | Bruce Smith <bruce@nanorex.com> | 2008-12-16 20:27:10 +0000 |
---|---|---|
committer | Bruce Smith <bruce@nanorex.com> | 2008-12-16 20:27:10 +0000 |
commit | f2a10793ac38f970651f55d82d7ace035d572c5c (patch) | |
tree | ad3b12187ba2e6ccaa9d4168a769abe58def0d78 | |
parent | 4587cbf6d0f0d6e3fbf7dbe218f46a3d01247f56 (diff) | |
download | nanoengineer-f2a10793ac38f970651f55d82d7ace035d572c5c.tar.gz nanoengineer-f2a10793ac38f970651f55d82d7ace035d572c5c.zip |
code cleanup and minor refactoring in modelTree
-rwxr-xr-x | cad/src/commands/BuildAtoms/BuildAtoms_GraphicsMode.py | 3 | ||||
-rwxr-xr-x | cad/src/foundation/undo_manager.py | 6 | ||||
-rwxr-xr-x | cad/src/modelTree/ModelTree.py | 239 | ||||
-rwxr-xr-x | cad/src/modelTree/modelTreeGui.py | 98 | ||||
-rwxr-xr-x | cad/src/ne1_ui/MWsemantics.py | 9 | ||||
-rwxr-xr-x | cad/src/ne1_ui/Ui_PartWindow.py | 4 | ||||
-rwxr-xr-x | cad/src/operations/chem_patterns.py | 18 | ||||
-rwxr-xr-x | cad/src/operations/ops_select.py | 2 | ||||
-rw-r--r-- | cad/src/operations/update_select_mode.py | 153 | ||||
-rwxr-xr-x | cad/src/outtakes/modelTreePrototype.py | 29 | ||||
-rw-r--r-- | cad/src/outtakes/test_modelTreeGui.py | 8 |
11 files changed, 296 insertions, 273 deletions
diff --git a/cad/src/commands/BuildAtoms/BuildAtoms_GraphicsMode.py b/cad/src/commands/BuildAtoms/BuildAtoms_GraphicsMode.py index 1a9ef75b2..ae29c8194 100755 --- a/cad/src/commands/BuildAtoms/BuildAtoms_GraphicsMode.py +++ b/cad/src/commands/BuildAtoms/BuildAtoms_GraphicsMode.py @@ -1202,12 +1202,9 @@ class BuildAtoms_basicGraphicsMode(SelectAtoms_basicGraphicsMode): new = selatom.molecule.copy_single_chunk(None) # None means no dad yet #bruce 050531 removing centering: ## new.move(-new.center) # perhaps no longer needed [bruce 041206] - #bruce 041124: open clipboard, so user can see new pastable there - self.w.mt.open_clipboard() # now add new to the clipboard - # bruce 041124 change: add new after the other members, not before. # [bruce 050121 adds: see cvs for history (removed today from this # code)of how this code changed as the storage order of diff --git a/cad/src/foundation/undo_manager.py b/cad/src/foundation/undo_manager.py index ac42e8e58..a9d2b103f 100755 --- a/cad/src/foundation/undo_manager.py +++ b/cad/src/foundation/undo_manager.py @@ -501,7 +501,9 @@ class AssyUndoManager(UndoManager): self.do_main_menu_op('Redo') def do_main_menu_op(self, optype): - "optype should be Undo or Redo" + """ + @note: optype should be Undo or Redo + """ op_was_available = not not self._current_main_menu_ops.get(optype) global _disable_UndoRedo if _disable_UndoRedo: #060414 @@ -524,7 +526,7 @@ class AssyUndoManager(UndoManager): undo_xxx = op.menu_desc() # note: menu_desc includes history sernos env.history.message(u"%s" % undo_xxx) #e say Undoing rather than Undo in case more msgs?? ######@@@@@@ TEST u"%s" self.archive.do_op(op) - self.assy.w.mt.update_select_mode() #bruce 060227 try to fix bug 1576 + self.assy.w.update_select_mode() #bruce 060227 try to fix bug 1576 self.assy.w.win_update() #bruce 060227 not positive this isn't called elsewhere, or how we got away without it if not else: if not disabled: diff --git a/cad/src/modelTree/ModelTree.py b/cad/src/modelTree/ModelTree.py index 00af714cf..05e917700 100755 --- a/cad/src/modelTree/ModelTree.py +++ b/cad/src/modelTree/ModelTree.py @@ -42,7 +42,7 @@ from PyQt4 import QtCore import foundation.env as env from utilities import debug_flags from platform_dependent.PlatformDependent import fix_plurals -import modelTree.modelTreeGui as modelTreeGui # defines ModelTreeGui (note case difference), Ne1Model_api +from modelTree.modelTreeGui import ModelTreeGui, ModelTree_api from model.chunk import Chunk from model.jigs import Jig @@ -50,26 +50,20 @@ from utilities.Log import orangemsg from foundation.Group import Group from utilities.debug import print_compact_traceback -from utilities.GlobalPreferences import permit_atom_chunk_coselection from utilities.GlobalPreferences import pref_show_highlighting_in_MT from utilities.constants import gensym -from utilities.constants import SELWHAT_ATOMS -from utilities.constants import SELWHAT_CHUNKS -from utilities.constants import SELWHAT_NAMES from utilities.constants import noop -from utilities.qt4transition import qt4here - -_debug_preftree = False # bruce 050602 experiment; do not commit with True +_DEBUG_PREFTREE = False # bruce 050602 experiment; do not commit with True # helpers for making context menu commands -class statsclass: # todo: rename, and move to its own utility module? +class _statsclass: # todo: rename, and move to its own utility module? """ class for holding and totalling counts of whatever you want, in named attributes """ - def __getattr__(self, attr): # in class statsclass + def __getattr__(self, attr): # in class _statsclass if not attr.startswith('_'): return 0 # no need to set it raise AttributeError, attr @@ -103,7 +97,7 @@ def _accumulate_stats(node, stats): this is run once on every topselected node (note: they are all picked) and once on every node under those (whether or not they are picked). """ - # todo (refactoring): could be a method on our own subclass of statsclass + # todo (refactoring): could be a method on our own subclass of _statsclass stats.n += 1 stats.ngroups += int(isinstance(node, Group)) @@ -121,18 +115,21 @@ def _accumulate_stats(node, stats): stats.nopen += int(node.open) return +# === -############################################################ - -# main widget class +class ModelTree(ModelTree_api): #bruce 081216 renamed modelTree -> ModelTree + """ + NE1's main model tree, serving as owner of the widget (ModelTreeGUI) + and also as the tree model shown by that. -class modelTree(modelTreeGui.Ne1Model_api): + @note: this is a public class name. + """ def __init__(self, parent, win, name = "modelTreeView", size = (200, 560)): """ #doc """ ###@@@ review all init args & instvars, here vs subclasses - self.modelTreeGui = modelTreeGui.ModelTreeGui(win, name, self, parent) + self.modelTreeGui = ModelTreeGui(win, name, self, parent) # WARNING: self.modelTreeGui is a PUBLIC MEMBER which is accessed by MWsemantics (at least) # for use in building the Qt widget layout. For public access purposes it can be considered # "the Qt widget containing the model tree" and it ought to have a special name (or get-method) @@ -140,7 +137,6 @@ class modelTree(modelTreeGui.Ne1Model_api): # Worse, the code before late on 070509 sometimes stored self.modelTreeGui rather than self # in win.mt (maybe) and assy.mt (definitely), but other times stored self! # And lots of files call various methods on assy.mt and/or win.mt, namely: - # - update_select_mode # - resetAssy_and_clear # - mt_update # - open_clipboard @@ -159,127 +155,7 @@ class modelTree(modelTreeGui.Ne1Model_api): self.mt_update() return - - def statusbar_message(self, text): #bruce 070531 # note: not presently used; untested - self.modelTreeGui.statusbar_message(text) - return - - def update_select_mode(self): #bruce 050124; should generalize and refile; should be used for more or for all events ###@@@ - #bruce 060403 revised this but didn't update docstring; now it can change from *Chunk modes to Build, only, I think - """ - This should be called at the end of event handlers which might have - changed the current internal selection mode (atoms vs chunks), - to resolve disagreements between that and the visible selection mode - iff it's one of the Select modes [or more generally, i assume as of 060403, - if the current mode wants to be ditched if selwhat has to have certain values it dislikes]. - If the current mode is not one of Select Atoms or Select Chunks, this routine has no effect. - (In particular, if selwhat changed but could be changed back to what it was, - it does nothing to correct that [obs? see end of docstring], and indeed it doesn't know the old value of - selwhat unless the current mode (being a selectMode) implies that.) - [We should generalize this so that other modes could constrain the selection - mode to just one of atoms vs chunks if they wanted to. However, the details of this - need design, since for those modes we'd change the selection whereas for the - select modes we change which mode we're in and don't change the selection. ###@@@] - If possible, we leave the visible mode the same (even changing assy.selwhat - to fit, if nothing is actually selected [that part was NIM until 050519]). - But if forced to, by what is currently selected, then we change the visible - selection mode to fit what is actually selected. (We always assert that selwhat - permitted whatever was selected to be selected.) - """ - if permit_atom_chunk_coselection(): #bruce 060721 - return - from commands.SelectChunks.SelectChunks_Command import SelectChunks_Command - - #bruce 050519 revised docstring and totally rewrote code. - assy = self.assy - win = self.win - commandSequencer = self.win.commandSequencer #bruce 071008 - mode = commandSequencer.currentCommand - #bruce 071008; note, I'm not sure it's right to ask the currentCommand - # for selwhat_from_mode, as opposed to the current graphicsMode! - # This whole thing needs total revamping (along with everything - # related to what can be selected at a given time), so I'm not going - # to worry about it for now. - del self - part = assy.part - # 0. Appraise the situation. - # 0a: assy.selwhat is what internal code thinks selection restriction is, currently. - selwhat = assy.selwhat - assert selwhat in (SELWHAT_CHUNKS, SELWHAT_ATOMS) # any more choices, or change in rules, requires rewriting this method - # 0b. What does current mode think it needs to be? - # (Someday we might distinguish modes that constrain this, - # vs modes that change to fit it or to fit the actual selection. - # For now we only handle modes that change to fit the actual selection.) - selwhat_from_mode = None # most modes don't care - if isinstance( mode, SelectChunks_Command): - # TODO: replace this by a method call or getattr on mode - selwhat_from_mode = SELWHAT_CHUNKS - #bruce 060403 commenting out the following, in advance of proposed removal of Select Atoms mode entirely: -## elif isinstance( mode, SelectAtoms_Command) and mode.commandName == SelectAtoms_Command.commandName: -## #bruce 060210 added commandName condition to fix bug when current mode is Build (now a subclass of Select Atoms) -## selwhat_from_mode = SELWHAT_ATOMS - change_mode_to_fit = (selwhat_from_mode is not None) # used later; someday some modes won't follow this - # 0c. What does current selection itself think it needs to be? - # (If its desires are inconsistent, complain and fix them.) - if assy.selatoms and assy.selmols: - if debug_flags.atom_debug: - #bruce 060210 made this debug-only, since what it reports is not too bad, and it happens routinely now in Build mode - # if atoms are selected and you then select a chunk in MT - print "atom_debug: bug, fyi: there are both atoms and chunks selected. Deselecting some of them to fit current mode or internal code." - new_selwhat_influences = ( selwhat_from_mode, selwhat) # old mode has first say in this case, if it wants it - #e (We could rewrite this (equivalently) to just use the other case with selwhat_from_sel = None.) - else: - # figure out what to do, in this priority order: actual selection, old mode, internal code. - if assy.selatoms: - selwhat_from_sel = SELWHAT_ATOMS - elif assy.selmols: - selwhat_from_sel = SELWHAT_CHUNKS - else: - selwhat_from_sel = None - new_selwhat_influences = ( selwhat_from_sel, selwhat_from_mode, selwhat) - if selwhat_from_sel is not None and selwhat_from_sel != selwhat: - # following code will fix this with no harm, so let's not consider it a big deal, - # but it does indicate a bug -- so just print a debug-only message. - # (As of 050519 740pm, we get this from the jig cmenu command "select this jig's atoms" - # when the current mode is more compatible with selecting chunks. But I think this causes - # no harm, so I might as well wait until we further revise selection code to fix it.) - if debug_flags.atom_debug: - print "atom_debug: bug, fyi: actual selection (%s) inconsistent " \ - "with internal variable for that (%s); will fix internal variable" % \ - (SELWHAT_NAMES[selwhat_from_sel], SELWHAT_NAMES[selwhat]) - # Let the strongest (first listed) influence, of those with an opinion, - # decide what selmode we'll be in now, and make everything consistent with that. - for opinion in new_selwhat_influences: - if opinion is not None: - # We have our decision. Carry it out (on mode, selection, and assy.selwhat) and return. - selwhat = opinion - if change_mode_to_fit and selwhat_from_mode != selwhat: - #bruce 050520 fix bug 644 by only doing this if needed (i.e. if selwhat_from_mode != selwhat). - # Without this fix, redundantly changing the mode using these tool buttons - # immediately cancels (or completes?) any node-renaming-by-dblclick - # right after it gets initiated (almost too fast to see). - if selwhat == SELWHAT_CHUNKS: - win.toolsSelectMolecules() - print "fyi: forced mode to Select Chunks" # should no longer ever happen as of 060403 - elif selwhat == SELWHAT_ATOMS: - win.toolsBuildAtoms() #bruce 060403 change: toolsSelectAtoms -> toolsBuildAtoms - ## win.toolsSelectAtoms() #bruce 050504 making use of this case for the first time; seems to work - # that might have fixed the following too, but never mind, we'll just always do it -- sometimes it's needed. - if selwhat == SELWHAT_CHUNKS: - part.unpickatoms() - assy.set_selwhat(SELWHAT_CHUNKS) - elif selwhat == SELWHAT_ATOMS: - if assy.selmols: # only if needed (due to a bug), since this also desels Groups and Jigs - # (never happens if no bug, since then the actual selection has the strongest say -- as of 050519 anyway) - part.unpickparts() - assy.set_selwhat(SELWHAT_ATOMS) # (this by itself does not deselect anything, as of 050519) - return - assert 0, "new_selwhat_influences should not have ended in None: %r" % (new_selwhat_influences,) - # scratch comments: - # if we had been fixing selwhat in the past, it would have fixed bug 500 in spite of permit_pick_parts in cm_hide/cm_unhide. - # So why aren't we? let's find out with some debug code... (now part of the above, in theory) - return - + def resetAssy_and_clear(self): #bruce 050201 for Alpha, part of Huaicai's bug 369 fix """ This method should be called from the end of MWsemantics._make_and_init_assy @@ -323,7 +199,7 @@ class modelTree(modelTreeGui.Ne1Model_api): def setGeometry(self, w, h): #k might not be needed return self.modelTreeGui.setGeometry(QtCore.QRect(0,0,w,h)) - # callbacks from self.modelTreeGui to help it update the display + # == callbacks from self.modelTreeGui to help it update the display def get_topnodes(self): self.assy = self.win.assy #k need to save it like this? @@ -335,7 +211,7 @@ class modelTree(modelTreeGui.Ne1Model_api): # [not anymore, as of some time before 050417] inserts assy.viewdata.members into assy.tree self.tree_node, self.shelf_node = self.assy.tree, self.assy.shelf topnodes = [self.assy.tree, self.assy.shelf] - if _debug_preftree: #bruce 050602 + if _DEBUG_PREFTREE: #bruce 050602 try: from foundation.Utility import Node ## print "reloading prefsTree" @@ -377,47 +253,9 @@ class modelTree(modelTreeGui.Ne1Model_api): def get_current_part_topnode(self): #bruce 070509 added this to the API return self.win.assy.part.topnode -## def node_to_item(self, node): -## return self.modelTreeGui.item_to_node_dict.get(node, None) -## #bruce 070503 comment: likely ###BUG, should use node_to_item_dict -- but this is not called anywhere! -## # (Where was it called in Qt3?) - - def open_clipboard(self): #bruce 050108, probably temporary - ###REVIEW: do we need the effect that's disabled here? - if 0: - # self.toggle_open( self.shelf_item, openflag = True) # what we did in Qt 3 - # shelf_item should be the item for the self.assy.shelf node - shelf_item = self.modelTreeGui.item_to_node_dict[self.assy.shelf] - #bruce 070503 comment: likely ###BUG, should use node_to_item_dict -- that explains the KeyError... - # update, bruce 070531: node_to_item_dict and/or node_to_item are not part of modelTreeGui API - # (or at least are no longer part of it if they were) and are not present in the new implem. - # They could be added back, but "items" are internal to the MT so that would probably be misguided. - # typically that gives a KeyError - self.toggle_open(shelf_item, openflag = True) - # toggle_open is defined in TreeView.py in the Qt 3 code - if False: - qt4here(show_traceback = True) - def grep_dash_il(str, substr): - str = str.upper() - substr = substr.upper() - try: - str.index(substr) - return True - except ValueError: - return False - print filter(lambda x: grep_dash_il(x, "mmkit"), dir(self.win)) - return - - def post_update_topitems(self): ###REVIEW: is this still needed? - self.tree_item, self.shelf_item = self.topitems[0:2] # ignore 3rd element (prefsTree when that's enabled) - # the actual items are different each time this is called - ###@@@ as of 050602 the only uses of these are: - # tree_item, in some debug code in TreeView; - # shelf_item, in our open_clipboard method. - ##e so I should replace those with something else and remove these. - - ############################################################ - # Everything else in this class is context menu stuff + # === + + # methods related to context menu -- menu maker, and handlers for each op def make_cmenuspec_for_set(self, nodeset, optflag): # [see also the term Menu_spec] """ @@ -465,10 +303,10 @@ class modelTree(modelTreeGui.Ne1Model_api): # which *might* be included in both strands and segments for this purpose (in the future; # shared members are NIM now).] - allstats = statsclass() + allstats = _statsclass() for node in nodeset: - node_stats = statsclass() + node_stats = _statsclass() node.apply2all( lambda node1: _accumulate_stats( node1, node_stats) ) allstats += node_stats # totals to allstats @@ -702,7 +540,7 @@ class modelTree(modelTreeGui.Ne1Model_api): # for very initial experiment let's provide it only for single items. # Do we ask them what can be customized about them? I guess so. ##unfinished... -## if _debug_preftree and len(nodeset) == 1: +## if _DEBUG_PREFTREE and len(nodeset) == 1: ## mspec = nodeset[0].customize_menuspec() ## submenu = [] @@ -830,7 +668,7 @@ class modelTree(modelTreeGui.Ne1Model_api): def cm_hide(self): env.history.message("Hide: %d selected items or groups" % \ - len(self.modelTreeGui.topmost_selected_nodes())) + len(self.topmost_selected_nodes())) #####@@@@@ bruce 050517 comment: the following line (of unknown reason or date, but by me) causes bug 500; # that method was added 050125 and used in chunk.pick on same date, so adding it here must be then or later. # Let's see what happens if I remove it? @@ -839,13 +677,13 @@ class modelTree(modelTreeGui.Ne1Model_api): def cm_unhide(self): env.history.message("Unhide: %d selected items or groups" % \ - len(self.modelTreeGui.topmost_selected_nodes())) + len(self.topmost_selected_nodes())) ## self.assy.permit_pick_parts() #e should not be needed here [see same comment above] self.assy.Unhide() # includes win_update def cm_set_node(self): #bruce 050604, for debugging import utilities.debug as debug - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() if len(nodeset) == 1: debug._node = nodeset[0] print "set debug._node to", debug._node @@ -855,7 +693,7 @@ class modelTree(modelTreeGui.Ne1Model_api): return def cm_properties(self): - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() if len(nodeset) != 1: env.history.message("error: cm_properties called on no or multiple items") # (internal error, not user error) @@ -957,7 +795,7 @@ class modelTree(modelTreeGui.Ne1Model_api): return def cm_ungroup(self): - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() assert len(nodeset) == 1 # caller guarantees this node = nodeset[0] assert node.permits_ungrouping() # ditto @@ -1000,7 +838,7 @@ class modelTree(modelTreeGui.Ne1Model_api): return def cm_remove_empty_groups(self): #bruce 080207 - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() empties = [] def func(group): if not group.members and group.permits_ungrouping(): @@ -1034,7 +872,7 @@ class modelTree(modelTreeGui.Ne1Model_api): """ Selects all the atoms preseent in the selected chunk(s) """ - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() self.assy.part.permit_pick_atoms() for m in nodeset: for a in m.atoms.itervalues(): @@ -1047,7 +885,7 @@ class modelTree(modelTreeGui.Ne1Model_api): """ from widgets.widget_helpers import RGBf_to_QColor - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() chunkList = [] #Find the chunks in the selection and store them temporarily for m in nodeset: @@ -1072,7 +910,7 @@ class modelTree(modelTreeGui.Ne1Model_api): Context menu entry for chunks. Turns on the showOverlayText flag in each chunk which has overlayText in some of its atoms. """ - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() for m in nodeset: if isinstance(m, Chunk): m.showOverlayText = True @@ -1082,7 +920,7 @@ class modelTree(modelTreeGui.Ne1Model_api): Context menu entry for chunks. Turns off the showOverlayText flag in each chunk which has overlayText in some of its atoms. """ - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() for m in nodeset: if isinstance(m, Chunk): m.showOverlayText = False @@ -1092,14 +930,14 @@ class modelTree(modelTreeGui.Ne1Model_api): """ Put up a dialog to let the user rename the selected node. (Only one node for now.) """ - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() assert len(nodeset) == 1 # caller guarantees this node = nodeset[0] self.modelTreeGui.rename_node_using_dialog( node) # note: this checks node.rename_enabled() first return def cm_disable(self): #bruce 050421 - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() assert len(nodeset) == 1 # caller guarantees this node = nodeset[0] jig = node # caller guarantees this is a jig; if not, this silently has no effect @@ -1107,7 +945,7 @@ class modelTree(modelTreeGui.Ne1Model_api): self.win.win_update() def cm_enable(self): #bruce 050421 - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() assert len(nodeset) == 1, "len nodeset should be 1, but nodeset is %r" % nodeset node = nodeset[0] jig = node @@ -1115,7 +953,7 @@ class modelTree(modelTreeGui.Ne1Model_api): self.win.win_update() def cm_select_jigs_atoms(self): #bruce 050504 - nodeset = self.modelTreeGui.topmost_selected_nodes() + nodeset = self.topmost_selected_nodes() otherpart = {} #bruce 050505 to fix bug 589 did_these = {} nprior = len(self.assy.selatoms) @@ -1150,7 +988,8 @@ class modelTree(modelTreeGui.Ne1Model_api): msg = orangemsg(msg) # the whole thing, I guess env.history.message(msg) self.win.win_update() - # note: caller (which puts up context menu) does self.update_select_mode(); we depend on that. + # note: caller (which puts up context menu) does + # self.win.update_select_mode(); we depend on that. [### still true??] return def cm_new_clipboard_item(self): #bruce 050505 @@ -1168,6 +1007,6 @@ class modelTree(modelTreeGui.Ne1Model_api): node.kill() # will this be safe even if one of these is presently displayed? ###k self.mt_update() - pass # end of class modelTree + pass # end of class ModelTree # end diff --git a/cad/src/modelTree/modelTreeGui.py b/cad/src/modelTree/modelTreeGui.py index 9a520cd9a..a79ed27da 100755 --- a/cad/src/modelTree/modelTreeGui.py +++ b/cad/src/modelTree/modelTreeGui.py @@ -171,10 +171,25 @@ class Api: ############################################# -class Ne1Model_api(Api): +class ModelTree_api(Api): """ API (and some default method implementations) for a Model Tree object. + + @warning: the object conforming to this API has two roles, which really + ought to be split among two distinct objects/classes: + * overall owner of Model Tree widget and its internal model, + serving as interface to it from the rest of NE1 + (i.e. as the value of win.mt) + * tree model, to be displayed and edited by a ModelTreeGUI + (i.e. by self.view, with self == self.view.treemodel) + (this might be called class TreeModel_api if it was split out) + Presently, this API has methods of both kinds. + + @warning: not all API methods are documented here. """ + # TODO: split this into a ModelTree (owning a ModelTreeGUI) + # and a TreeModel (owned by the ModelTree, shown by the ModelTreeGUI) + # [bruce 081216 comment; also added docstring warnings about this] def get_topnodes(self): """ Return a list of the top-level nodes, typically assy.tree and assy.shelf for an assembly. @@ -199,7 +214,10 @@ class Ne1Model_api(Api): Subclasses should override this to provide an actual menu spec. The subclass implementation can directly examine the selection status of nodes below those in nodeset, if desired, and can assume every node in nodeset is picked, - and every node not in it or under something in it is not picked. + and every node not in it or under something in it is not picked + (or at least, will be unpicked when the operation occurs, + which happens for picked nodes inside unpicked leaf-like Groups + such as DnaStrand). [all subclasses should override this] """ raise Exception("overload me") @@ -259,7 +277,7 @@ class Ne1Model_api(Api): pass return - def topmost_selected_nodes(self): # in class Ne1Model_api + def topmost_selected_nodes(self): # in class ModelTree_api """ @return: a list of all selected nodes which are not inside selected Groups """ @@ -274,7 +292,7 @@ class Ne1Model_api(Api): """ raise Exception("overload me") - pass # end of class Ne1Model_api + pass # end of class ModelTree_api class Node_api(Api): # REVIEW: maybe refile this into model/Node_API and inherit from Node?? [bruce 080107 comment] """ @@ -311,7 +329,7 @@ class Node_api(Api): # REVIEW: maybe refile this into model/Node_API and inherit # - in Qt3 there *is* a Node API call to support renaming ("try_rename" or so). # Why isn't it used or listed here? # - # See also my comments in modelTree.__init__. + # See also my comments in class ModelTree.__init__. def __init__(self): """ @@ -423,6 +441,7 @@ class ModelTreeGui_api(Api): raise Exception("overload me") def topmost_selected_nodes(self): # in ModelTreeGui_api + ####### REVIEW: should this be in this api, or is it just an implem convenience func? """ @return: a list of all selected nodes which are not inside selected Groups """ @@ -492,7 +511,7 @@ def _paintnode(node, painter, x, y, widget, option_holder = None): text_color = None if pref_show_node_color_in_MT(): # [bruce 080507 new feature, mainly for testing] - # review: should the modelTree itself (ne1model) test this pref + # review: should the modelTree itself (treemodel) test this pref # and tell us the text_color for each node? ### is this debug_pref test too slow? if it is, so is the debug_pref lower down... # could fix using option_holder to know the pref values @@ -738,10 +757,10 @@ class ModelTreeGui_common(ModelTreeGui_api): """ # not private, but only used in this file so far [bruce 080306 comment] #bruce 070529 split this out of class ModelTreeGui - def __init__(self, win, ne1model): + def __init__(self, win, treemodel): self.win = win - self.ne1model = ne1model - ne1model.view = self #e should rename this attr of ne1model + self.treemodel = treemodel #bruce 081216 renamed this from ne1model + treemodel.view = self #e should rename this attr of treemodel self._mousepress_info_for_move = None # set by any mousePress that supports mouseMove not being a noop, to info about what that move should do #bruce 070509 self._ongoing_DND_info = None # set to a tuple of a private format, during a DND drag (not used during a selection-drag #k) @@ -763,18 +782,18 @@ class ModelTreeGui_common(ModelTreeGui_api): """ @return: a list of all selected nodes which are not inside selected Groups """ - #bruce 070529 moved method body into self.ne1model + #bruce 070529 moved method body into self.treemodel #REVIEW: should this be removed from ModelTreeGui_api, - # always accessed via self.ne1model? Pro: many accesses come - # from methods in self.ne1model anyway, which also defines it. + # always accessed via self.treemodel? Pro: many accesses come + # from methods in self.treemodel anyway, which also defines it. # Con: there are a lot of accesses from methods of self, too. - # Note that we could make accesses from self.ne1model not + # Note that we could make accesses from self.treemodel not # depend on this class, without preventing this class from # having its own def; in that case, review whether this class's # def belongs in its api class or is just a convenience of - # this implementation. [bruce 081212 comment] + # this implementation. [bruce 081212 comment] ######## - return self.ne1model.topmost_selected_nodes() + return self.treemodel.topmost_selected_nodes() def MT_debug_prints(self): return debug_pref("MT debug: debug prints", Choice_boolean_False, prefs_key = True) @@ -803,8 +822,7 @@ class ModelTreeGui_common(ModelTreeGui_api): return # message already emitted (if one is desired) # work around visual bug due to unwanted deselection of some of these nodes (during mousePressEvent) - for node in self.topmost_selected_nodes(): - node.unpick() + self.unpick_all() for node in nodes: node.pick() self.mt_update() @@ -833,7 +851,7 @@ class ModelTreeGui_common(ModelTreeGui_api): dragobj.setPixmap(pixmap) sbar_text = self.get_whatting_n_items_text( drag_type, nodes) - self.statusbar_msg( sbar_text) + self.statusbar_message( sbar_text) dragobj.setHotSpot(QPoint(-18, 8)) #bruce 070514 tweak hotspot requested_action = {'copy':Qt.CopyAction, 'move':Qt.MoveAction}[drag_type] @@ -895,12 +913,14 @@ class ModelTreeGui_common(ModelTreeGui_api): #e might become more different in the future if we include the class # when they're all nodes of the same subclass... - def statusbar_msg(self, msg): - #e should store the current one for this widget, to share sbar with other widgets; - # or better, the method we're calling should do that for all widgets (or their parts) in a uniform way + def statusbar_message(self, msg): + # note: spelling difference. + # maybe todo: rename all methods of that name like this. + # [bruce 070531 & 081216] + # todo: store current msg for this widget, so we can share statusbar + # with other widgets; or better, the method we're calling should do that + # for all widgets (or their parts) in a uniform way env.history.statusbar_msg( msg) - - statusbar_message = statusbar_msg #bruce 070531; we should rename all methods of that name like this, after A9 goes out # == Qt3 code for drag graphic, not yet ported, used only if debug_pref set [copied from Qt3/TreeWidget.py, bruce 070511] # == [update: I think this is now fully ported and used by default, long before 080507] @@ -1077,13 +1097,13 @@ class ModelTreeGui_common(ModelTreeGui_api): Print a message, but do appropriate updates only if we succeed. """ #bruce 070511 brought in several error messages from Qt3/TreeWidget.py, - # modified some of them, made them use statusbar_msg rather than history + # modified some of them, made them use statusbar_message rather than history # redmsg item, rectjunk = self.item_and_rect_at_event_pos(event) if item is None: msg = "drop into empty space ignored (drops under groups " \ "not supported; drop onto them instead)" - self.statusbar_msg( msg) + self.statusbar_message( msg) raise DoNotDrop() nodes, drag_type = self._ongoing_DND_info targetnode = item.node @@ -1100,7 +1120,7 @@ class ModelTreeGui_common(ModelTreeGui_api): msg = "drop refused by %s" % quote_html(targetnode.name) if whynot: msg += ": %s" % (whynot,) #bruce 080303 - self.statusbar_msg( msg ) + self.statusbar_message( msg ) raise DoNotDrop() event.acceptProposedAction() # this should come after any DoNotDrop we might raise @@ -1173,7 +1193,7 @@ class ModelTreeGui_common(ModelTreeGui_api): #e should be more specific about what happened to them... # ask the target node itself? have drop_on return this info? msg = fix_plurals(msg) - self.statusbar_msg( msg) + self.statusbar_message( msg) #bruce 050203: mt_update is not enough, in case selection changed # (which can happen as a side effect of nodes moving under new dads in the tree) self.win.win_update() @@ -1289,7 +1309,7 @@ class ModelTreeGui_common(ModelTreeGui_api): eventInRect = False # might be changed below; says whether event hits rect including icon(??) and name/label if item is not None: - self.statusbar_msg( quote_html( item.node.name)) #bruce 070511 -- no other way to see long node names! + self.statusbar_message( quote_html( item.node.name)) #bruce 070511 -- no other way to see long node names! # (at least for now -- tooltips are off for some reason, MT can't be widened, and it has no horizontal scrollbar, # though it did a few days ago -- don't know why.) alreadySelected = item.node.picked #bruce 070509 @@ -1560,7 +1580,7 @@ class ModelTreeGui_common(ModelTreeGui_api): ###TODO: we should consolidate the several checks for the optflag condition into one place, maybe mousePressEvent. optflag = (((self.mouse_press_buttons & Qt.MidButton) or (self.mouse_press_modifiers & Qt.AltModifier)) and 'Option' or None) - cmenu_spec = self.ne1model.make_cmenuspec_for_set(nodeset, optflag) + cmenu_spec = self.treemodel.make_cmenuspec_for_set(nodeset, optflag) menu = makemenu_helper(self, cmenu_spec) #bruce 070514 fix bug 2374 and probably others by using makemenu_helper menu.exec_(event.globalPos()) @@ -1650,13 +1670,13 @@ def x_for_indent(n): class MT_View(QtGui.QWidget): """ - ModelTree contents view + contents view for ModelTreeGUI """ def __init__(self, parent, palette_widget, modeltreegui): QtGui.QWidget.__init__(self, parent) self.palette_widget = palette_widget #e rename? self.modeltreegui = modeltreegui - self.ne1model = self.modeltreegui.ne1model ###KLUGE? not sure. consider passing this directly? + self.treemodel = self.modeltreegui.treemodel ###KLUGE? not sure. consider passing this directly? self.get_icons() return @@ -1744,7 +1764,7 @@ class MT_View(QtGui.QWidget): painter = QtGui.QPainter() painter.begin(self) try: - topnodes = self.ne1model.get_topnodes() + topnodes = self.treemodel.get_topnodes() x, y = x_for_indent(0), MT_CONTENT_TOP_Y for node in topnodes: y = self.paint_subtree(node, painter, x, y) @@ -1757,7 +1777,7 @@ class MT_View(QtGui.QWidget): self._setup_openclose_style() # todo: optim: implem this: self._setup_prefs(), # so _paintnode needn't repeatedly test the same debug_prefs for each node - self._f_nodes_to_highlight = self.ne1model.get_nodes_to_highlight() + self._f_nodes_to_highlight = self.treemodel.get_nodes_to_highlight() # a dictionary from node to an arbitrary value; # in future, could store highlight color, etc self._painted = {} # modified in paint_subtree when we call _paintnode @@ -1914,7 +1934,7 @@ class MT_View(QtGui.QWidget): d = 0 # indent level if y < y0: return None, None, None - for child in self.ne1model.get_topnodes(): + for child in self.treemodel.get_topnodes(): resnode, resdepth, resy0, y0 = self.look_for_y_recursive( child, y0, d, y) if resnode: return resnode, resdepth, resy0 @@ -2021,11 +2041,11 @@ class ModelTreeGui(QScrollArea, ModelTreeGui_common): The GUI part of the NE1 model tree widget. """ #bruce 070529-30 rewrite of some of [now-removed] class ModelTreeGui_QTreeView - def __init__(self, win, name, ne1model, parent = None): - ## print "what are these args?", win, name, ne1model, parent + def __init__(self, win, name, treemodel, parent = None): + ## print "what are these args?", win, name, treemodel, parent # win = <MWsemantics.MWsemantics object at 0x4ce8a08> # name = modelTreeView - # ne1model = <modelTree.modelTree instance at 0x4cfb3a0> + # treemodel = <modelTree.ModelTree instance at 0x4cfb3a0> # parent = <PyQt4.QtGui.QWidget object at 0x4cff468> del name QScrollArea.__init__(self, parent) @@ -2037,7 +2057,7 @@ class ModelTreeGui(QScrollArea, ModelTreeGui_common): self.setContextMenuPolicy(Qt.PreventContextMenu) #bruce 070509 change; I hope it will prevent direct calls of contextMenuEvent # this has to be done after QScrollArea.__init__, since it assumes QWidget init has occurred - ModelTreeGui_common.__init__(self, win, ne1model) ## mouse and DND methods -- better off in view widget or here?? + ModelTreeGui_common.__init__(self, win, treemodel) ## mouse and DND methods -- better off in view widget or here?? # will this intercept events meant for the scrollbars themselves??? need to use the contents event methods? self.view = MT_View( self, self, self) # args are: parent, palette_widget, modeltreegui @@ -2089,7 +2109,7 @@ class ModelTreeGui(QScrollArea, ModelTreeGui_common): self.__count = 0 def func(node): self.__count += 1 # using a local var doesn't work, due to Python scoping - self.ne1model.recurseOnNodes(func, visible_only = True) + self.treemodel.recurseOnNodes(func, visible_only = True) NUMBER_OF_BLANK_ITEMS_AT_BOTTOM = 1 # not 0, to let user be certain they are at the end # [bruce 080306 new feature] diff --git a/cad/src/ne1_ui/MWsemantics.py b/cad/src/ne1_ui/MWsemantics.py index 0fac137da..34529f67e 100755 --- a/cad/src/ne1_ui/MWsemantics.py +++ b/cad/src/ne1_ui/MWsemantics.py @@ -1320,6 +1320,15 @@ class MWsemantics(QMainWindow, def toolsSelectMolecules(self):# note: this can also be called from update_select_mode [bruce 060403 comment] self.commandSequencer.userEnterCommand('SELECTMOLS', always_update = True) + def update_select_mode(self): + """ + change currentCommand or assy.selwhat or selection to make them consistent + """ + #bruce 081216 moved this here, from a method on self.mt + # (where it made no sense) + from operations.update_select_mode import update_select_mode # todo: move this to toplevel + update_select_mode(self) + # get into Move Chunks (or Translate Components) command def toolsMoveMolecule(self): self.ensureInCommand('MODIFY') diff --git a/cad/src/ne1_ui/Ui_PartWindow.py b/cad/src/ne1_ui/Ui_PartWindow.py index f94d0cf07..21c56dba3 100755 --- a/cad/src/ne1_ui/Ui_PartWindow.py +++ b/cad/src/ne1_ui/Ui_PartWindow.py @@ -31,7 +31,7 @@ from PyQt4.Qt import QTabWidget, QScrollArea, QSizePolicy, SIGNAL from graphics.widgets.GLPane import GLPane from PM.PM_Constants import PM_DEFAULT_WIDTH, PM_MAXIMUM_WIDTH, PM_MINIMUM_WIDTH from utilities.icon_utilities import geticon -from modelTree.ModelTree import modelTree +from modelTree.ModelTree import ModelTree from utilities.qt4transition import qt4warnDestruction from utilities import debug_flags import foundation.env as env @@ -239,7 +239,7 @@ class Ui_PartWindow(QWidget): modelTreeTabLayout.setSpacing(0) # Create the model tree (GUI) and add it to the tab layout. - self.modelTree = modelTree(self.modelTreeTab, parent) + self.modelTree = ModelTree(self.modelTreeTab, parent) self.modelTree.modelTreeGui.setObjectName("modelTreeGui") modelTreeTabLayout.addWidget(self.modelTree.modelTreeGui) diff --git a/cad/src/operations/chem_patterns.py b/cad/src/operations/chem_patterns.py index 947d42eae..3e71da0a9 100755 --- a/cad/src/operations/chem_patterns.py +++ b/cad/src/operations/chem_patterns.py @@ -1,10 +1,12 @@ -# Copyright 2006-2007 Nanorex, Inc. See LICENSE file for details. +# Copyright 2006-2008 Nanorex, Inc. See LICENSE file for details. """ -chem_patterns.py +chem_patterns.py -- finding simple patterns of bonded atoms -Commands and other code related to finding simple patterns of bonded atoms. +see also ND-1's pattern matching facility, which is faster and more general -$Id$ +@author: Bruce +@version: $Id$ +@copyright: 2006-2008 Nanorex, Inc. See LICENSE file for details. Todo later: - really turn O into 8 and reverse rules as needed @@ -13,8 +15,6 @@ Todo later: - might need a few more kinds of patterns, like one for just 2 atoms... wait and see what's suggested. """ -__author__ = "bruce" - import foundation.env as env import utilities.debug as debug @@ -39,7 +39,9 @@ def compile_patterns(): return bad_patterns_dict, root_eltnums, other_eltnums def select_bad_atoms_cmd(widget): #bruce 060615 demo of simple "spelling checker" with hardcoded rules - """Out of the selected atoms or chunks, select the atoms which have "bad spelling".""" + """ + Out of the selected atoms or chunks, select the atoms which have "bad spelling". + """ from utilities.Log import orangemsg, redmsg, greenmsg greencmd = greenmsg("%s: " % cmdname) orangecmd = orangemsg("%s: " % cmdname) # used when bad atoms are found, even though no error occurred in the command itself @@ -111,7 +113,7 @@ def select_bad_atoms_cmd(widget): #bruce 060615 demo of simple "spelling checker env.history.message( orangemsg("Warning: ") + fix_plurals( "%d bad atom(s) were/was not selected due to the selection filter." % \ (len(bad_atoms) - reallypicked) )) - win.mt.update_select_mode() + win.update_select_mode() return def initialize(): diff --git a/cad/src/operations/ops_select.py b/cad/src/operations/ops_select.py index ca919daed..848617bff 100755 --- a/cad/src/operations/ops_select.py +++ b/cad/src/operations/ops_select.py @@ -1153,7 +1153,7 @@ def topmost_selected_nodes(nodes): not including any node which is inside any selected Group in or under the given list - @see: same-named method in class ModelTreeGui_api and class Ne1Model_api + @see: same-named method in class ModelTreeGui_api and class ModelTree_api """ #bruce 050523 split this out from the same-named TreeWidget method, # and optimized it diff --git a/cad/src/operations/update_select_mode.py b/cad/src/operations/update_select_mode.py new file mode 100644 index 000000000..63204de3c --- /dev/null +++ b/cad/src/operations/update_select_mode.py @@ -0,0 +1,153 @@ +# Copyright 2004-2008 Nanorex, Inc. See LICENSE file for details. +""" +update_select_mode.py - change currentCommand or assy.selwhat or selection +to make them consistent + +@author: Bruce +@version: $Id$ +@copyright: 2004-2008 Nanorex, Inc. See LICENSE file for details. + +Note: this can still have an effect, but ought to be generalized +and refactored. It is accessed as a method on the main window, +but is in its own file since it probably doesn't belong there +after refactoring and since that module is too long. + +History: + +bruce 050124 wrote this [as a method of the model tree]; +should generalize and refile; +should be used for more or for all events + +bruce 060403 revised this but didn't update docstring [of method in modeltree]; +now it can change from *Chunk modes to Build, only, I think + +bruce 081216 moved this out of class ModelTree (where it made no sense) +into its own file, to be called via a same-named method on MWsemantics +""" + +from utilities.GlobalPreferences import permit_atom_chunk_coselection + +from utilities.constants import SELWHAT_ATOMS +from utilities.constants import SELWHAT_CHUNKS +from utilities.constants import SELWHAT_NAMES + +from utilities import debug_flags + +def update_select_mode(win): + """ + Warning: this docstring is partly obsolete. + + This should be called at the end of event handlers which might have + changed the current internal selection mode (atoms vs chunks), + to resolve disagreements between that and the visible selection mode + iff it's one of the Select modes [or more generally, i assume as of 060403, + if the current mode wants to be ditched if selwhat has to have certain values it dislikes]. + If the current mode is not one of Select Atoms or Select Chunks (or a subclass), + this routine has no effect. + (In particular, if selwhat changed but could be changed back to what it was, + it does nothing to correct that [obs? see end of docstring], and indeed it doesn't know the old value of + selwhat unless the current mode (being a selectMode) implies that.) + [We should generalize this so that other modes could constrain the selection + mode to just one of atoms vs chunks if they wanted to. However, the details of this + need design, since for those modes we'd change the selection whereas for the + select modes we change which mode we're in and don't change the selection. ###@@@] + If possible, we leave the visible mode the same (even changing assy.selwhat + to fit, if nothing is actually selected [that part was NIM until 050519]). + But if forced to, by what is currently selected, then we change the visible + selection mode to fit what is actually selected. (We always assert that selwhat + permitted whatever was selected to be selected.) + """ + if permit_atom_chunk_coselection(): #bruce 060721, experimental + return + + from commands.SelectChunks.SelectChunks_Command import SelectChunks_Command # todo: move to toplevel + + #bruce 050519 revised docstring and totally rewrote code. + assy = win.assy + commandSequencer = win.commandSequencer #bruce 071008 + mode = commandSequencer.currentCommand + #bruce 071008; note, I'm not sure it's right to ask the currentCommand + # for selwhat_from_mode, as opposed to the current graphicsMode! + # This whole thing needs total revamping (along with everything + # related to what can be selected at a given time), so I'm not going + # to worry about it for now. + part = assy.part + # 0. Appraise the situation. + # 0a: assy.selwhat is what internal code thinks selection restriction is, currently. + selwhat = assy.selwhat + assert selwhat in (SELWHAT_CHUNKS, SELWHAT_ATOMS) # any more choices, or change in rules, requires rewriting this method + # 0b. What does current mode think it needs to be? + # (Someday we might distinguish modes that constrain this, + # vs modes that change to fit it or to fit the actual selection. + # For now we only handle modes that change to fit the actual selection.) + selwhat_from_mode = None # most modes don't care + if isinstance( mode, SelectChunks_Command): + # TODO: replace this by a method call or getattr on mode + selwhat_from_mode = SELWHAT_CHUNKS + #bruce 060403 commenting out the following, in advance of proposed removal of Select Atoms mode entirely: +## elif isinstance( mode, SelectAtoms_Command) and mode.commandName == SelectAtoms_Command.commandName: +## #bruce 060210 added commandName condition to fix bug when current mode is Build (now a subclass of Select Atoms) +## selwhat_from_mode = SELWHAT_ATOMS + change_mode_to_fit = (selwhat_from_mode is not None) # used later; someday some modes won't follow this + # 0c. What does current selection itself think it needs to be? + # (If its desires are inconsistent, complain and fix them.) + if assy.selatoms and assy.selmols: + if debug_flags.atom_debug: + #bruce 060210 made this debug-only, since what it reports is not too bad, and it happens routinely now in Build mode + # if atoms are selected and you then select a chunk in MT + print "atom_debug: bug, fyi: there are both atoms and chunks selected. Deselecting some of them to fit current mode or internal code." + new_selwhat_influences = ( selwhat_from_mode, selwhat) # old mode has first say in this case, if it wants it + #e (We could rewrite this (equivalently) to just use the other case with selwhat_from_sel = None.) + else: + # figure out what to do, in this priority order: actual selection, old mode, internal code. + if assy.selatoms: + selwhat_from_sel = SELWHAT_ATOMS + elif assy.selmols: + selwhat_from_sel = SELWHAT_CHUNKS + else: + selwhat_from_sel = None + new_selwhat_influences = ( selwhat_from_sel, selwhat_from_mode, selwhat) + if selwhat_from_sel is not None and selwhat_from_sel != selwhat: + # following code will fix this with no harm, so let's not consider it a big deal, + # but it does indicate a bug -- so just print a debug-only message. + # (As of 050519 740pm, we get this from the jig cmenu command "select this jig's atoms" + # when the current mode is more compatible with selecting chunks. But I think this causes + # no harm, so I might as well wait until we further revise selection code to fix it.) + if debug_flags.atom_debug: + print "atom_debug: bug, fyi: actual selection (%s) inconsistent " \ + "with internal variable for that (%s); will fix internal variable" % \ + (SELWHAT_NAMES[selwhat_from_sel], SELWHAT_NAMES[selwhat]) + # Let the strongest (first listed) influence, of those with an opinion, + # decide what selmode we'll be in now, and make everything consistent with that. + for opinion in new_selwhat_influences: + if opinion is not None: + # We have our decision. Carry it out (on mode, selection, and assy.selwhat) and return. + selwhat = opinion + if change_mode_to_fit and selwhat_from_mode != selwhat: + #bruce 050520 fix bug 644 by only doing this if needed (i.e. if selwhat_from_mode != selwhat). + # Without this fix, redundantly changing the mode using these tool buttons + # immediately cancels (or completes?) any node-renaming-by-dblclick + # right after it gets initiated (almost too fast to see). + if selwhat == SELWHAT_CHUNKS: + win.toolsSelectMolecules() + print "fyi: forced mode to Select Chunks" # should no longer ever happen as of 060403 + elif selwhat == SELWHAT_ATOMS: + win.toolsBuildAtoms() #bruce 060403 change: toolsSelectAtoms -> toolsBuildAtoms + ## win.toolsSelectAtoms() #bruce 050504 making use of this case for the first time; seems to work + # that might have fixed the following too, but never mind, we'll just always do it -- sometimes it's needed. + if selwhat == SELWHAT_CHUNKS: + part.unpickatoms() + assy.set_selwhat(SELWHAT_CHUNKS) + elif selwhat == SELWHAT_ATOMS: + if assy.selmols: # only if needed (due to a bug), since this also desels Groups and Jigs + # (never happens if no bug, since then the actual selection has the strongest say -- as of 050519 anyway) + part.unpickparts() + assy.set_selwhat(SELWHAT_ATOMS) # (this by itself does not deselect anything, as of 050519) + return + assert 0, "new_selwhat_influences should not have ended in None: %r" % (new_selwhat_influences,) + # scratch comments: + # if we had been fixing selwhat in the past, it would have fixed bug 500 in spite of permit_pick_parts in cm_hide/cm_unhide. + # So why aren't we? let's find out with some debug code... (now part of the above, in theory) + return + +# end diff --git a/cad/src/outtakes/modelTreePrototype.py b/cad/src/outtakes/modelTreePrototype.py index 33d149a03..e4744d58f 100755 --- a/cad/src/outtakes/modelTreePrototype.py +++ b/cad/src/outtakes/modelTreePrototype.py @@ -125,7 +125,7 @@ seeing is how the name gets from the Q3ListViewItem back to the Node. So renamin me, and I'll ask Bruce about it later. """ -class Ne1Model_api: +class ModelTree_api: #bruce 081216 renamed this from Ne1Model_api to ModelTree_api def get_topnodes(self): """Return a list of the top-level nodes, typically assy.tree and assy.shelf for an Assembly. """ @@ -225,8 +225,9 @@ class Node_api: """ raise Exception('overload me') -class ModelTree_api(QTreeView): - """This should be a Qt4 widget that can be put into a layout. +class ModelTreeGUI_api(QTreeView): #bruce 081216 renamed this from ModelTree_api to ModelTreeGUI_api + """ + This should be a Qt4 widget that can be put into a layout. """ def pick(self, item, group_select_kids=True): "select the given item (actually the node or group it shows)" @@ -446,11 +447,11 @@ class _QtTreeModel(QAbstractItemModel): #################################################################### -class ModelTree(ModelTree_api): - def __init__(self, name, ne1model, parent=None): +class ModelTree(ModelTreeGUI_api): + def __init__(self, name, treemodel, parent=None): QTreeView.__init__(self, parent) - self.ne1model = ne1model - ne1model.view = self + self.treemodel = treemodel #bruce 081216 renamed this from ne1model + treemodel.view = self self.setSelectionMode(self.ExtendedSelection) #bruce 070507 MultiSelection -> ExtendedSelection self.qtmodel = None self.drag = None @@ -496,7 +497,7 @@ class ModelTree(ModelTree_api): def node_icon(self, display_prefs): return None - rootNode = FakeTopNode("Model tree", self.ne1model.get_topnodes()) + rootNode = FakeTopNode("Model tree", self.treemodel.get_topnodes()) rootItem = self.make_new_subtree_for_node(rootNode) self.qtmodel = model = _QtTreeModel(rootItem) self.setModel(model) @@ -640,7 +641,7 @@ class ModelTree(ModelTree_api): node = self.item_to_node_dict[item] nodeset = [ node ] # ? ? ? ? optflag = False # ? ? ? ? - cmenu_spec = self.ne1model.make_cmenuspec_for_set(nodeset, optflag) + cmenu_spec = self.treemodel.make_cmenuspec_for_set(nodeset, optflag) for x in cmenu_spec: if x is not None: str, thunk = x[:2] @@ -744,7 +745,7 @@ class TestClipboardNode(TestNode): else: return self.iconEmpty -class TestNe1Model(Ne1Model_api): +class TestNe1Model(ModelTree_api): def __init__(self): self.untitledNode = TestNode("Untitled", None, QPixmap("../images/part.png")) @@ -809,9 +810,9 @@ class TestWrapper(QGroupBox): def __init__(self): QGroupBox.__init__(self) - self.ne1model = ne1model = TestNe1Model() + self.treemodel = treemodel = TestNe1Model() - self.view = view = ModelTree("Model tree", ne1model, self) + self.view = view = ModelTree("Model tree", treemodel, self) view.mt_update() def thunk(str): @@ -856,7 +857,7 @@ class TestWrapper(QGroupBox): icon = QPixmap('../images/measuredistance.png') icon_h = QPixmap('../images/measuredistance-hide.png') chunk = TestNode("%s-%d" % (what, self.chunkNum), - self.ne1model.untitledNode, icon, icon_h) + self.treemodel.untitledNode, icon, icon_h) self.chunkNum += 1 self.view.mt_update() @@ -880,7 +881,7 @@ def test_api(): # Test API compliance. If we remove all the functionality, pushing buttons shouldn't raise any # exceptions. global ModelTree, _QtTreeItem, _QtTreeModel - ModelTree = ModelTree_api + ModelTree = ModelTreeGUI_api del _QtTreeModel del _QtTreeItem diff --git a/cad/src/outtakes/test_modelTreeGui.py b/cad/src/outtakes/test_modelTreeGui.py index be3e62447..016e0fb58 100644 --- a/cad/src/outtakes/test_modelTreeGui.py +++ b/cad/src/outtakes/test_modelTreeGui.py @@ -164,7 +164,7 @@ class TestClipboardNode(TestNode): else: return self.iconEmpty -class TestNe1Model(Ne1Model_api): +class TestNe1Model(ModelTree_api): def __init__(self): self.untitledNode = TestNode("Untitled", None, QPixmap("../images/part.png")) @@ -244,9 +244,9 @@ class TestWrapper(QGroupBox): def __init__(self): QGroupBox.__init__(self) - self.ne1model = ne1model = TestNe1Model() + self.treemodel = treemodel = TestNe1Model() - self.view = view = ModelTreeGui(TestMainWindow(), "Model tree", ne1model, self) + self.view = view = ModelTreeGui(TestMainWindow(), "Model tree", treemodel, self) view.mt_update() self.chunkNum = 2 self.gbox = QGroupBox() @@ -285,7 +285,7 @@ class TestWrapper(QGroupBox): icon = QPixmap('../images/measuredistance.png') icon_h = QPixmap('../images/measuredistance-hide.png') chunk = TestNode("%s-%d" % (what, self.chunkNum), - self.ne1model.untitledNode, icon, icon_h) + self.treemodel.untitledNode, icon, icon_h) self.chunkNum += 1 self.view.mt_update() |