def __init__(self, name, assy, dad, members=(), editCommand=None): Group.__init__(self, name, assy, dad, members=members, editCommand=editCommand) ###BUG: not all callers pass an editCommand. It would be better # to figure out on demand which editCommand would be appropriate. # [bruce 080227 comment] return
def __init__(self, name, assy, dad, members = (), editCommand = None): Group.__init__(self, name, assy, dad, members = members, editCommand = editCommand) ###BUG: not all callers pass an editCommand. It would be better # to figure out on demand which editCommand would be appropriate. # [bruce 080227 comment] return
def build_struct(self, name, params, position): """ Build the DNA helix based on parameters in the UI. @param name: The name to assign the node in the model tree. @type name: str @param params: The list of parameters gathered from the PM. @type params: tuple @param position: The position in 3d model space at which to create the DNA strand. This is always 0, 0, 0. @type position: position """ # No error checking in build_struct, do all your error # checking in gather_parameters numberOfBases, \ dnaForm, \ basesPerTurn, \ endPoint1, \ endPoint2 = params if Veq(endPoint1, endPoint2): raise CadBug("DNA endpoints cannot be the same point.") if numberOfBases < 1: msg = redmsg("Cannot to preview/insert a DNA duplex with 0 bases.") self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False) self.dna = None # Fixes bug 2530. Mark 2007-09-02 return None if dnaForm == 'B-DNA': dna = B_Dna_PAM3() else: raise PluginBug("Unsupported DNA Form: " + dnaForm) self.dna = dna # needed for done msg # Create the model tree group node. dnaGroup = Group(self.name, self.win.assy, self.win.assy.part.topnode) try: # Make the DNA duplex. <dnaGroup> will contain three chunks: # - Strand1 # - Strand2 # - Axis dna.make(dnaGroup, numberOfBases, basesPerTurn, endPoint1, endPoint2) return dnaGroup except (PluginBug, UserError): # Why do we need UserError here? Mark 2007-08-28 rawDnaGroup.kill() raise PluginBug( "Internal error while trying to create DNA duplex.") pass
def readmmp_info_opengroup_setitem(self, key, val, interp): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.Nanotube import Nanotube self.nanotube = Nanotube() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: Group.readmmp_info_opengroup_setitem(self, key, val, interp) return
def readmmp_info_opengroup_setitem( self, key, val, interp ): """ [extends superclass method] """ #bruce 080507 refactoring (split this out of the superclass method) if key == ['nanotube-parameters']: # val includes all the parameters, separated by commas. n, m, type, endings = val.split(",") self.n = int(n) self.m = int(m) self.type = type.lstrip() self.endings = endings.lstrip() # Create the nanotube. from cnt.model.Nanotube import Nanotube self.nanotube = Nanotube() # Returns a 5x5 CNT. self.nanotube.setChirality(self.n, self.m) self.nanotube.setType(self.type) self.nanotube.setEndings(self.endings) # The endpoints are recomputed every time it is edited. else: Group.readmmp_info_opengroup_setitem( self, key, val, interp) return
def __init__(self, assy, name): # arg order is like Node, not like Group dad = None Group.__init__(self, name, assy, dad)
def drop_on(self, drag_type, nodes): ###@@@ needs a big cleanup # todo: also return description of what we did (for statusbar text) # [bruce 080303 comment] """ After a "drag and drop" of type 'move' or 'copy' (according to drag_type), perform the drop of the given list of nodes onto self.node. The list always contains the original nodes -- for drag_type == 'copy', this method should make the copies itself. Exactly how to do this depends on whether self.node is a leaf or group; subclasses of Node can override this to change the UI behavior. (As of 050307, only the Clipboard overrides this. Update 071025: that's now done by an 'if' statement in the following method, which calls self.node.drop_on_should_autogroup, which is what the Clipboard overrides.) @return: list of new nodes made by this operation, which is [] if it didn't make any (making them is normal for copy, but can also happen in some cases for move) @rtype: list of nodes """ will_drop_inside = self.node.MT_DND_can_drop_inside() # if true, nodes or their copies will become new children of self.node; # if false, they will become new siblings. [revised, bruce 080317] autogroup_at_top = will_drop_inside and \ self.node.drop_on_should_autogroup(drag_type, nodes) drop_onto_Group_at_top = pref_drop_onto_Group_puts_nodes_at_top( ) #bruce 080414 if autogroup_at_top: #bruce 050203/080303: # nodes dropped onto the clipboard come from one # "physical space" (Part) and ought to stay that way by default; # user can drag them one-at-a-time if desired. (In theory this # autogrouping need only be done for the subsets of them which # are bonded; for now that's too hard -- maybe not for long, # similar to bug 371. But this simpler behavior might be better # anyway.) if drag_type == 'move': name = self.node.assy.name_autogrouped_nodes_for_clipboard( nodes, howmade=drag_type) new = Group(name, self.node.assy, None) for node in nodes[:]: #bruce 050216 don't reverse the order, it's already correct node.unpick( ) #bruce 050216; don't know if needed or matters; 050307 moved from after to before moveto node.moveto( new ) ####@@@@ guess, same as in super.drop_on (move here, regardless of drag_type? no, not correct!) nodes = [new] # a new length-1 list of nodes env.history.message( "(fyi: Grouped some nodes to keep them in one clipboard item)" ) ###e improve text else: # the above implem is wrong for copy, so we handle it differently below, # when autogroup_at_top and drag_type != 'move' pass pass #e rewrite to use copy_nodes (nim)? (also rewrite the assy methods? not for alpha) res = [ ] #bruce 050203: return any new nodes this creates (toplevel nodes only, for copied groups) #bruce 050216: order is correct if you're dropping on a group, but (for the ops used below) # wrong if you're dropping on a node. This needs a big cleanup, but for now, use this kluge # [revised to fix bug 2403 (most likely, this never worked as intended for copy until now), bruce 070525]: if not will_drop_inside: # drops on nodes which act like leaf nodes are placed after them, # when done by the methods named in the following flags, # so to drop several nodes in a row and preserve order, # drop them in reverse order -- but *when* we reverse them # (as well as which method we use) depends on whether we're # doing move or copy, so these flags are used in the right # place below. ## reverse_moveto = True reverse_addmember = True # for either addchild or addsibling pass else: # will_drop_inside is True # # drops on groups which accept drops inside themselves # go at the end of their members, # when done by those methods, so *don't* reverse node order -- # UNLESS drop_onto_Group_at_top is set, which means, we put nodes dropped # on groups at the beginning of their members list. # REVIEW: are there any other things that need to be changed for that? ### ## assert not debug_pref_DND_drop_at_start_of_groups() ## reverse_moveto = drop_onto_Group_at_top reverse_addmember = drop_onto_Group_at_top #bruce 060203 removing this, to implement one aspect of NFR 932: ## self.node.open = True # open groups which have nodes dropped on them [bruce 050528 new feature] pass if drag_type == 'move': # TODO: this code is now the same as the end of the copy case; # after the release, clean this up by moving the common code # outside of (after) this move/copy 'if' statement. # [bruce 080414 comment] if reverse_addmember: # [bruce 080414 post-rc0 code cleanup: reverse_moveto -> reverse_addmember] nodes = nodes[::-1] for node in nodes[:]: ## node.moveto(self.node) if will_drop_inside: self.node.addchild(node, top=drop_onto_Group_at_top) else: self.node.addsibling(node, before=False) continue pass else: #bruce 050527 [revised 071025] this uses copied_nodes_for_DND, # new code to "copy anything". That code is preliminary # (probably not enough history messages, or maybe sometimes # too many). It would be better to create the Copier object # (done in that subr?) earlier, when the drag is started, # for various reasons mentioned elsewhere. # update 071025: autogroup_at_top is now set at start of this method ## autogroup_at_top = isinstance(self.node, ClipboardShelfGroup) #####@@@@@ kluge! replace with per-group variable or func. #e or perhaps better, a per-group method to process the nodes list, eg to do the grouping # as the comment in copied_nodes_for_DND or its subr suggests. nodes = copied_nodes_for_DND(nodes, autogroup_at_top=autogroup_at_top) # Note: this ignores order within input list of nodes, using only their MT order # to affect the order of copied nodes which it returns. [bruce 070525 comment] if not nodes: # might be None return res # return copied nodes [bruce 080414 post-rc0 code cleanup: [] -> res] res.extend(nodes) # note: the following code is the same in the move/copy cases; # see above for more about this. if reverse_addmember: nodes = nodes[::-1] # note: if autogroup_at_top makes len(nodes) == 1, this has no effect, # but it's harmless then, and logically best to do it whenever using # addmember on list elements. for node in nodes[:]: # node is a "copied node" [bruce 080414 post-rc0 code cleanup: renamed nc -> node] ## self.node.addmember(node) # self.node is sometimes a Group, ## # so this does need to be addmember (not addchild or addsibling) if will_drop_inside: self.node.addchild(node, top=drop_onto_Group_at_top) else: self.node.addsibling(node, before=False) continue pass self.node.assy.update_parts( ) #e could be optimized to scan only what's needed (same for most other calls of update_parts) return res
def build_struct(self, name, params, position): """ Build the DNA helix based on parameters in the UI. @param name: The name to assign the node in the model tree. @type name: str @param params: The list of parameters gathered from the PM. @type params: tuple @param position: The position in 3d model space at which to create the DNA strand. This is always 0, 0, 0. @type position: position """ # No error checking in build_struct, do all your error # checking in gather_parameters theSequence, \ dnaModel, \ dnaType, \ basesPerTurn, \ chunkOption, \ endpoint1, \ endpoint2 = params if Veq(endpoint1, endpoint2): raise CadBug("Dna endpoints cannot be the same point.") return if len(theSequence) < 1: msg = redmsg("Enter a strand sequence to preview/insert DNA") self.MessageGroupBox.insertHtmlMessage(msg, setAsDefault=False) self.dna = None # Fixes bug 2530. Mark 2007-09-02 return None if dnaModel == 'PAM3': dna = B_Dna_PAM3() else: dna = B_Dna_PAM5() self.dna = dna # needed for done msg # Create the model tree group node. rawDnaGroup = Group(self.name, self.win.assy, self.win.assy.part.topnode) try: # Make the DNA duplex. <rawDnaGroup> returns a different # grouping arrangement for atomistic vs. PAM5. This 'issue' # is resolved when we regroup the atoms into strand chunks # below. dna.make(rawDnaGroup, theSequence, basesPerTurn) self._orientRawDnaGroup(rawDnaGroup, endpoint1, endpoint2) # Now group the DNA atoms based on the grouping option selected # (i.e. "Strand chunks" or "Single Chunk"). dnaGroup = self._makePAMStrandAndAxisChunks(rawDnaGroup) if chunkOption == 'Single chunk': return self._makeDuplexChunk(dnaGroup) return dnaGroup except (PluginBug, UserError): # Why do we need UserError here? Mark 2007-08-28 rawDnaGroup.kill() raise PluginBug("Internal error while trying to create DNA duplex.") return None
def cm_new_clipboard_item(self): #bruce 050505 name = self.assy.name_autogrouped_nodes_for_clipboard( []) # will this end up being the part name too? not sure... ###k self.assy.shelf.addchild(Group(name, self.assy, None)) self.assy.update_parts() self.mt_update()
def cm_group( self ): # bruce 050126 adding comments and changing behavior; 050420 permitting exactly one subtree """ put the selected subtrees (one or more than one) into a new Group (and update) """ ##e I wonder if option/alt/middleButton should be like a "force" or "power" flag # for cmenus; in this case, it would let this work even for a single element, # making a 1-item group. That idea can wait. [bruce 050126] #bruce 050420 making this work inside clipboard items too # TEST if assy.part updated in time ####@@@@ -- no, change to selgroup! self.deselect_partly_picked_whole_nodes() sg = self.assy.current_selgroup() node = sg.hindmost() # smallest nodetree containing all picked nodes if not node: env.history.message( "nothing selected to Group") # should never happen return if node.picked: #bruce 050420: permit this case whenever possible (formation of 1-item group); # cmenu constructor should disable or leave out the menu command when desired. if node != sg: assert node.dad # in fact, it'll be part of the same sg subtree (perhaps equal to sg) node = node.dad assert not node.picked # fall through -- general case below can handle this. else: # the picked item is the topnode of a selection group. # If it's the main part, we could make a new group inside it # containing all its children (0 or more). This can't happen yet # so I'll be lazy and save it for later. assert node != self.assy.tree # Otherwise it's a clipboard item. Let the Part take care of it # since it needs to patch up its topnode, choose the right name, # preserve its view attributes, etc. assert node.part.topnode == node newtop = node.part.create_new_toplevel_group() env.history.message( "made new group %s" % newtop.name ) ###k see if this looks ok with autogenerated name self.mt_update() return # (above 'if' might change node and then fall through to here) # node is an unpicked Group inside (or equal to) sg; # more than one of its children (or exactly one if we fell through from the node.picked case above) # are either picked or contain something picked (but maybe none of them are directly picked). # We'll make a new Group inside node, just before the first child containing # anything picked, and move all picked subtrees into it (preserving their order; # but losing their structure in terms of unpicked groups that contain some of them). ###e what do we do with the picked state of things we move? worry about the invariant! ####@@@@ # make a new Group (inside node, same assy) ###e future: require all assys the same, or, do this once per topnode or assy-node. # for now: this will have bugs when done across topnodes! # so the caller doesn't let that happen, for now. [050126] new = Group(gensym("Group", node.assy), node.assy, node) # was self.assy assert not new.picked # put it where we want it -- before the first node member-tree with anything picked in it for m in node.members: if m.haspicked(): assert m != new ## node.delmember(new) #e (addsibling ought to do this for us...) [now it does] m.addsibling(new, before=True) break # (this always happens, since something was picked under node) node.apply2picked(lambda (x): x.moveto(new)) # this will have skipped new before moving anything picked into it! # even so, I'd feel better if it unpicked them before moving them... # but I guess it doesn't. for now, just see if it works this way... seems to work. # ... later [050316], it evidently does unpick them, or maybe delmember does. msg = fix_plurals( "grouped %d item(s) into " % len(new.members)) + "%s" % new.name env.history.message(msg) # now, should we pick the new group so that glpane picked state has not changed? # or not, and then make sure to redraw as well? hmm... # - possibility 1: try picking the group, then see if anyone complains. # Caveat: future changes might cause glpane redraw to occur anyway, defeating the speed-purpose of this... # and as a UI feature I'm not sure what's better. # - possibility 2: don't pick it, do update glpane. This is consistent with Ungroup (for now) # and most other commands, so I'll do it. # # BTW, the prior code didn't pick the group # and orginally didn't unpick the members but now does, so it had a bug (failure to update # glpane to show new picked state), whose bug number I forget, which this should fix. # [bruce 050316] ## new.pick() # this will emit an undesirable history message... fix that? self.win.glpane.gl_update( ) #k needed? (e.g. for selection change? not sure.) self.mt_update() return