class test_polyline_drag(State_preMixin, ExampleCommand): # class constants needed by mode API for example commands commandName = 'test_polyline_drag-commandName' default_mode_status_text = "test_polyline_drag" featurename = "Prototype: Example Polyline Drag Command" ## PM_class = test_polyline_drag_PM # tracked state rubberBand_enabled = State( bool, False, doc="whether we're rubberbanding a new segment now") # TODO: ### grab a polyline object from an example file in exprs ### (the one that has a closed boolean and can draw in 3d) ### have a list of them, we're rubberbanding last segment of last one... lastSegmentStart = ORIGIN + 6 * DY # stub lastSegmentEnd = State( Point, doc="endpoint of last segment; only meaningful when rubberBand_enabled" ) # note: these might be redundant with some points in a polyline object; # should they be aliases into one? rubberBandDrawable = If_expr( rubberBand_enabled, Line(lastSegmentStart, lastSegmentEnd, red, thickness=4), NullDrawable) whatWeDraw = Instance(rubberBandDrawable) # init methods def __init__(self, glpane): # not sure why this method is needed, see comment in test_connectWithState.__init__ super(test_polyline_drag, self).__init__(glpane) ExampleCommand.__init__(self, glpane) return def Draw(self): super(test_polyline_drag, self).Draw() #k self.whatWeDraw.draw() return def leftDown(self, event): print "leftDown" # TODO: # get the point (mouseray in plane) # make a real point there (or mark it as real, if we have a point object for it already) # (so it has a blue dot of its own) # update self.rubberBand_enabled # STUB: self.rubberBand_enabled = True self.lastSegmentEnd = self.lastSegmentStart + 3 * DX ExampleCommand.leftDown(self, event) ### will this prevent this error in mouse release: ## AttributeError: 'test_polyline_drag' object has no attribute 'LMB_press_event' ## [GLPane.py:1805] [selectAtomsMode.py:899] return pass
class test_connectWithState(State_preMixin, ExampleCommand): # class constants needed by mode API for example commands commandName = 'test_connectWithState-commandName' default_mode_status_text = "test_connectWithState" featurename = "Prototype: Test connectWithState" PM_class = test_connectWithState_PM # tracked state -- this initializes specially defined instance variables # which will track all their uses and changes so that connectWithState # works for them: cylinderVertical = State(bool, False) cylinderWidth = State(float, CYLINDER_WIDTH_DEFAULT_VALUE) # TODO: soon this will be the only use of this constant, so it can be inlined cylinderColor = State('color-stub', pink) # type should be Color (nim), but type is not yet used # note: you can add _e_debug = True to one or more of these State definitions # to see debug prints about some accesses to this state. GraphicsMode_class = _test_connectWithState_GM # init methods def __init__(self, glpane): # I don't know why this method is needed. ##### REVIEW (super semantics), FIX or clean up super(test_connectWithState, self).__init__(glpane) # State_preMixin.__init__ ExampleCommand.__init__(self, glpane) # (especially this part) return ## def __init__(self, glpane): ## super(test_connectWithState, self).__init__(glpane) #### # that only calls some mode's init method, #### # so (for now) call this separately: #### IorE_guest_mixin.__init__(self, glpane) ## return # exprs-based formulae (and some compute methods) direction = If_expr( cylinderVertical, DY, DX ) def _C_width_direction(self): """ compute self.width_direction """ # Note: to do this with a formula expr instead # would require cross_Expr to be defined, # and glpane.lineOfSight to be tracked. return cross( self.direction, self.env.glpane.lineOfSight ) width_direction = _self.width_direction # so it can be used in formulae below # stub for handle test code [070912] widthHandleEnabled = True # stub ## widthHandle = Instance(Rect()) # stub h_offset = 0.5 + 0.2 # get it from handle? nah (not good if that changes with time); just make it fit. # or we could decide that handles ought to have useful fixed bounding boxes... ## widthHandle = Instance(Translate(Center(Rect(0.5)), ## width_direction * (cylinderWidth / 2.0 + h_offset) )) #stub widthHandle = Instance( DraggableHandle_AlongLine( appearance = Center(Rect(0.5, 0.5, white)), ### REVIEW: # Can't we just replace the following with something based on the formula for the position, # width_direction * (cylinderWidth / 2.0 + h_offset) # ? # As it is, I have to manually solve that formula for origin and direction to pass in, # i.e. rewrite it as # position = origin + direction * cylinderWidth ## height_ref = cylinderWidth, ###WRONG ## height_ref = ObjAttr_StateRef( _self, 'cylinderWidth'), ## ## AssertionError: ObjAttr_StateRef fallback is nim -- needed for S._self height_ref = call_Expr( ObjAttr_StateRef, _self, 'cylinderWidth'), # guess at workaround; #e we need a more principled way! ### REVIEW: efficient enough? (guess: overhead only happens once, so yes) # could we say instead something like: height_ref = Variable(cylinderWidth) ?? Or VariableRef? Or StateRef_to ? origin = width_direction * h_offset, # note: also includes cylinder center, but that's hardcoded at ORIGIN direction = width_direction / 2.0, sbar_text = "cylinder width", ### TODO: make it a formula, include printed value of width? range = (0.1, 10), ### TODO: DraggableHandle_AlongLine should take values from the stateref if this option is not provided; # meanwhile, we ought to pass a consistent value! )) # Note: the Instance is required; but I'm not sure if it would be # if we were using a fuller exprs superclass or init code. [bruce 070912] def cmd_Bigger(self): self.cylinderWidth += 0.5 set_cylinder_height( cylinder_height() + 0.5) # TODO: enforce maxima return def cmd_Smaller(self): self.cylinderWidth -= 0.5 set_cylinder_height( cylinder_height() - 0.5) # enforce minima (###BUG: not the same ones as declared in the PM) ### REVISE: min & max should be declared in State macro and (optionally) enforced by it if self.cylinderWidth < 0.1: self.cylinderWidth = 0.1 if cylinder_height() < 0.1: set_cylinder_height(0.1) return pass
class DraggableHandle_AlongLine(DelegatingInstanceOrExpr): ### TODO: all options might need renaming! replace "height" everywhere. """ A kind of draggable handle which can be dragged along a line to change the value of a single floating point parameter (representing position along the line, using a scale and origin determined by our arguments). ### TODO: add epydoc parameters to this docstring? not sure that will work for a class! Maybe we need to synthesize those (from the doc options below) into a fake __init__ method for its sake?? """ # == args and options # options for appearance of handle appearance = Option( Drawable, Center(Rect(0.2, 0.2, white)), doc = "our appearance, when not highlighted") ###e take other args of Highlightable? appearance_highlighted = Option( Drawable, appearance, doc = "our appearance, when highlighted") sbar_text = Option(str, "draggable handle", doc = "our statusbar text on mouseover") # state variable controlled by dragging height_ref = Option(StateRef, doc = "stateref to a height variable") # TODO: rename, redoc range = Option(tuple_Expr, None, doc = "range limit of height (tuple)") ### MAYBE: RENAME to avoid conflict with python range in this code ### TODO: take values from the stateref if this option is not provided # action options, for Highlightable to do after the ones that come from # DragBehavior_AlongLine [new feature of this and Highlightable, 080129] on_press = Option(Action) on_drag = Option(Action) on_release = Option(Action) on_release_in = Option(Action, on_release) on_release_out = Option(Action, on_release) on_doubleclick = Option(Action) # line along which to drag it, and how to interpret the state as a position along that line (origin and scale) origin = Option( Point, ORIGIN) direction = Option( Vector, DX, doc = "vector giving direction and scale") # providing a default value is mainly just for testing #If this is false, the 'highlightable' object i.e. this handle #won't be drawn. The delegate (that defines a Highlightable) #We define an If_expr to check whether to draw the highlightable object. #[by Ninad] # [this should probably be revised, with hasValidParamsForDrawing replaced # with an overridable compute method, for robustness -- current implem # is suspected of causing tracebacks from insufficient update of this # state. Need to review whether methodname needs to be hasValidParamsForDrawing # to conform with any API. -- bruce 080409 comment] should_draw = State(bool, True) # == internal instances and formulae (modified from test_statearray_3.py) _drag_handler = Instance( DragBehavior_AlongLine( _self._delegate, height_ref, call_Expr(Ray, origin, direction), # note: we need call_Expr since Ray is an ordinary class, not an expr! ###FIX range = range )) # note: _drag_handler is also being used to compute the translation from the height, even between drags. #@see: DnaStrand_ResizeHandle.hasValidParamsForDrawing #@see: definition of State attr should_draw delegate = \ If_expr( _self.should_draw, Highlightable( Translate( appearance, _drag_handler._translation #k ok?? only if that thing hangs around even in between drags, i guess! #e #k not sure if this code-commoning is good, but it's tempting. hmm. ), highlighted = Translate( appearance_highlighted, _drag_handler._translation ), sbar_text = sbar_text, behavior = _drag_handler, on_press = _self.on_press, on_drag = _self.on_drag, # (note: no need to pass on_release) on_release_in = _self.on_release_in, on_release_out = _self.on_release_out, on_doubleclick = _self.on_doubleclick ) #end of Highlightable ) #end of If_expr def hasValidParamsForDrawing(self): """ Overridden in subclasses. Default implementation returns True if this object (the highlightable) can be drawn without any known issues @see: DnaStrand_ResizeHandle.hasValidParamsForDrawing for more notes. """ self.should_draw = True return self.should_draw pass # end of class
class DnaStrand_ResizeHandle(DraggableHandle_AlongLine): """ Provides a resize handle for editing the length of an existing Dna Strand. """ #Handle color will be changed depending on whether the handle is grabbed # [bruce 080409 revised some details of this to fix bug 2747] handleColor = Option(Color, purple) # formula from caller # (in current usage, a state variable in caller) handleIsGrabbed = State(Boolean, False) # Note: this might not be needed if we passed more args to # Highlightable (namely, the appearance when pressed); # that would also be more reliable, since as it is, any failure for # on_release to be called would leave the handle stuck in the # grabbed state; client code would be wise to sometimes reset # this state. Also, it seems rare to ever see this selection color # since the handle is usually highlighted and yellow while dragging it. # [Depending on the highlight and selection drawing mode. Russ 080530] # So this state could probably just be removed, with all uses of # _currentHandleColor changes to uses of handleColor. # [bruce 080409] _currentHandleColor = If_expr( handleIsGrabbed, env.prefs[selectionColor_prefs_key], _self.handleColor) #The caller-specified formula that determines the radius (of the sphere) of this handle. #See DnaStrand_EditCommand._determine_resize_handle_radius() for more #details sphereRadius = Option(Width, 1.5) #Appearance of the handle. (note that it uses all the code from exprs module # and needs more documentation there). #See exprs.Rect.Sphere for definition of a drawable 'Sphere' object. appearance = Overlay( Sphere(_self.sphereRadius, _self._currentHandleColor, center = ORIGIN + _self.direction * 3.0 * _self.sphereRadius), Cylinder((ORIGIN, ORIGIN + _self.direction * 2.2 * _self.sphereRadius), 0.6 * _self.sphereRadius, _self._currentHandleColor)) #Handle appearance when highlighted # [this probably doesn't need to be an Option, since the client never # passes it [bruce 080409 comment]] HHColor = env.prefs[hoverHighlightingColor_prefs_key] appearance_highlighted = Option( Drawable, Overlay( Sphere(_self.sphereRadius, HHColor, center = ORIGIN + _self.direction * 3.0 * _self.sphereRadius), Cylinder((ORIGIN, ORIGIN + _self.direction * 2.2 * _self.sphereRadius), 0.6* _self.sphereRadius , HHColor)), doc = "handle appearance when highlighted") #Stateusbar text. Variable needs to be renamed in superclass. sbar_text = Option(str, "Drag the handle to resize the strand", doc = "Statusbar text on mouseover") #Command object specified as an 'Option' during instantiation of the class #see DnaSegment_EditCommand class definition. command = Option(Action, doc = 'The Command which instantiates this handle') #Current position of the handle. i.e. it is the position of the handle #under the mouse. (its differert than the 'orifinal position) #This variable is used in self.command.graphicsMode to draw a rubberband #line and also to specify the endPoint2 of the structure while modifying #it. See DnaSegment_EditCommand.modifyStructure for details. if _self.origin is not None: currentPosition = _self.origin + _self.direction * _self.height_ref.value else: currentPosition = ORIGIN #Fixed end of the structure (self.command.struct) ..meaning that end won't #move while user grabbs and draggs this handle (attached to a the other #'moving endPoint) . This variable is used to specify endPoint1 of the #structure while modifyin it. See DnaSegment_EditCommand.modifyStructure #and self.on_release for details. fixedEndOfStructure = Option(Point, V(0, 0, 0)) #If this is false, the 'highlightable' object i.e. this handle #won't be drawn. See DraggableHandle.py for the declararion of #the delegate(that defines a Highlightable) We define a If_Exprs to check #whether to draw the highlightable object. should_draw = State(bool, True) def ORIG_NOT_USED_hasValidParamsForDrawing(self): """ NOT USED AS OF 2008-04-02 Returns True if the handles origin and direction are not 'None'. @see: DnaStrand_GraphicsMode._draw_handles() where the caller uses this to decide whether this handle can be drawn without a problem. """ #NOTE: Better to do it in the drawing code of this class? #But it uses a delegate to draw stuff (see class Highlightable) #May be we should pass this method to that delegate as an optional #argument -- Ninad 2008-04-02 if self.origin is None or self.direction is None: return False return True def hasValidParamsForDrawing(self): """ Returns True if the handles origin and direction are not 'None'. @see: DnaStrand_GraphicsMode._draw_handles() where the caller uses this to decide whether this handle can be drawn without a problem. """ #NOTE: Better to do it in the drawing code of this class? #But it uses a delegate to draw stuff (see class Highlightable) #May be we should pass this method to that delegate as an optional #argument -- Ninad 2008-04-02 #@Bug: Create a duplex; Enter Dna strand edit command, # then shorten it such that it removes some bases of the strand from the #original duplex. Hit undo; click on the right handle, and shorten it again #sometimes it gives a traceback. in drawing the highlightable #this could be because self.should_draw flag is not getting updated. #NOTES: If this method is used, you will also need to define the #delegate in class DraggableHandle as -- #delegate = If_Exprs(_self.should_draw, Highlightable(....)) if self.origin is None or self.direction is None: self.should_draw = False else: self.should_draw = True return self.should_draw def on_press(self): """ Actions when handle is pressed (grabbed, during leftDown event) @see: B{SelectChunks.GraphicsMode.leftDown} @see: B{DnaStrand_EditCommand.grabbedHandle} @see: B{DnaStrand_GraphicsMode.Draw} (which uses some attributes of the current grabbed handle of the command. @see: B{DragHandle_API} """ #Change the handle color when handle is grabbed. See declaration of #self.handleColor in the class definition. ## self._currentHandleColor = env.prefs[selectionColor_prefs_key] self.handleIsGrabbed = True #assign 'self' as the curent grabbed handle of the command. self.command.grabbedHandle = self def on_drag(self): """ Method called while dragging this handle . @see: B{DragHandle_API} """ pass #The following call is disabled. Instead updating this spinbox #is done by the command.getCursorText method . See that method for #details ##self.command.update_numberOfBases() def on_release(self): """ This method gets called during leftUp (when the handle is released) @see: B{DnaStrand_EditCommand.modifyStructure} @see: self.on_press @see: B{SelectChunks.GraphicsMode.leftUp} @see: B{DragHandle_API} """ ## self._currentHandleColor = self.handleColor self.handleIsGrabbed = False if self.command and hasattr(self.command, 'modifyStructure'): self.command.modifyStructure() #Clear the grabbed handle attribute (the handle is no longer #grabbed) self.command.grabbedHandle = None
class MakeCrossovers_Handle(DelegatingInstanceOrExpr): should_draw = State(bool, True) radius = 0.8 point1 = Arg(Point) point2 = Arg(Point) scale = Arg(float) crossoverSite_marker = Option( Action, doc='The CrossoverSite Marker class which instantiates this handle') #Command object specified as an 'Option' during instantiation of the class #see DnaSegment_EditCommand class definition. command = Option(Action, doc='The Command which instantiates this handle') crossoverPairs = Option(tuple, ()) #Stateusbar text. Variable needs to be renamed in superclass. sbar_text = Option(str, "Click on the handle to create this crossover", doc="Statusbar text on mouseover") delegate = If_expr( _self.should_draw, Highlightable(Overlay( Cylinder((call_Expr(_self.crossoverPairs[0].posn), call_Expr(_self.crossoverPairs[3].posn)), radius=radius, color=silver), Cylinder((call_Expr(_self.crossoverPairs[1].posn), call_Expr(_self.crossoverPairs[2].posn)), radius=radius, color=silver)), Overlay( Cylinder((call_Expr(_self.crossoverPairs[0].posn), call_Expr(_self.crossoverPairs[3].posn)), radius=radius, color=banana), Cylinder((call_Expr(_self.crossoverPairs[1].posn), call_Expr(_self.crossoverPairs[2].posn)), radius=radius, color=banana)), on_press=_self.on_press, on_release=_self.on_release, sbar_text=sbar_text)) ##delegate = If_expr(_self.should_draw, ##Highlightable(Cylinder((point1, point2), ##radius = radius, ##color = silver), ##Cylinder((point1, point2), ##radius = radius, ##color = banana), ##on_press = _self.on_press, ##on_release = _self.on_release)) ##delegate = \ ##If_expr( ##_self.should_draw, ##Highlightable(Arrow( ##color = silver, ##arrowBasePoint = point1, ####tailPoint = norm(vector)*1.0, ##tailPoint = point2, ##radius = radius, ##scale = scale), ##Arrow( ##color = banana, ##arrowBasePoint = point1, ####tailPoint = norm(vector)*1.0, ##tailPoint = point2, ##radius = radius, ##scale = scale), ##on_press = _self.on_press, ##on_release = _self.on_release ) ) # def hasValidParamsForDrawing(self): """ Overridden in subclasses. Default implementation returns True if this object (the highlightable) can be drawn without any known issues @see: DnaStrand_ResizeHandle.hasValidParamsForDrawing for more notes. """ ##self.should_draw = True return self.should_draw def on_press(self): pass def on_release(self): self.command.makeCrossover(self.crossoverPairs) self.crossoverSite_marker.removeHandle(self)