Esempio n. 1
0
class SimpleColumn(Widget2D): #061115
    #e or use InstanceMacro using Overlay & Translate? Could work, but slower and not needed... might help in CL with fancier gaps.
    ## a0 = Arg(Widget2D) # note: this probably doesn't preclude caller from passing None, and even if it does, nothing enforces that yet;
        # if caller does pass None (to this or to Overlay), best thing is probably to replace this with Pass = Rect(0,0,white)
        # and use it as a drawable, but have special case to use no gap under it -- or the equiv, as a simple change
        # to our btop formula so it's 0 if not a0 -- which is already done, so maybe there's no need to worry about a0 = None.
        # Maybe it should be an optional arg like the others. [061115]
    a0 = Arg(Widget2D, None) # even the first arg can be missing, as when applying it to a list of no elts [061205]
    a1 = Arg(Widget2D, None)
    a2 = Arg(Widget2D, None)
    a3 = Arg(Widget2D, None)
    a4 = Arg(Widget2D, None)
    a5 = Arg(Widget2D, None) # use ArgList here when that works
    a6 = Arg(Widget2D, None)
    a7 = Arg(Widget2D, None)
    a8 = Arg(Widget2D, None)
    a9 = Arg(Widget2D, None)
    a10 = Arg(Widget2D, None)
    a11 = Arg(Widget2D, None) # the 12th arg is 1 too many
    toomany = Instance(TextRect("too many args to SimpleColumn"))
        #070212 added Instance to fix mysterious bug manifesting as this debug output:
        ## getattr_debugprint: <lexenv_ipath_Expr... <TextRect#2331(a)>> has no 'bleft'
    args = list_Expr(a0,a1,a2,a3,a4,a5, a6,a7,a8,a9,a10, # could say or_Expr(a0, Spacer(0)) but here is not where it matters
                     and_Expr(a11, toomany)
                     )
    
    ## gap = Option(Width, 3 * PIXELS)
    pixelgap = Option(float, 3) # 070104 int -> float
    gap = pixelgap * PIXELS

    print_lbox = Option(bool, False) #061127 for debugging; should be harmless; never tested (target bug got diagnosed in another way)
    
    drawables = call_Expr(lambda args: filter(None, args) , args)
    ## empty = not drawables ###e BUG: needs more Expr support, I bet; as it is, likely to silently be a constant False; not used internally
    empty = not_Expr(drawables)
    bleft = call_Expr(lambda drawables: max([arg.bleft for arg in drawables] + [0]) , drawables)
        # 070211 arg.bleft -> getattr_debugprint(arg, 'bleft')
    bright = call_Expr(lambda drawables: max([arg.bright for arg in drawables] + [0]) , drawables)
    height = call_Expr(lambda drawables, gap: sum([arg.height for arg in drawables]) + gap * max(len(drawables)-1,0) , drawables, gap)
    ## btop = a0 and a0.btop or 0  # bugfix -- use _Expr forms instead; i think this form silently turned into a0.btop [061205]
    btop = or_Expr( and_Expr( a0, a0.btop), 0)
    bbottom = height - btop
    def draw(self):
        if self.print_lbox:
            print "print_lbox: %r lbox attrs are %r" % (self, (self.bleft, self.bright, self.bbottom, self.btop))
        glPushMatrix()
        prior = None
        for a in self.drawables:
            if prior:
                # move from prior to a
                dy = prior.bbottom + self.gap + a.btop
                glTranslatef(0,-dy,0) # positive is up, but Column progresses down
            prior = a
            self.drawkid(a) ## a.draw()
        glPopMatrix()
        return
    pass # end of class SimpleColumn or SimpleColumn_OLD
Esempio n. 2
0
class SimpleRow(Widget2D):
    # copy of SimpleColumn, but bbottom <-> bright, btop <-> bleft, width <- height, and 0,-dy -> dx,0, basically
    a0 = Arg(Widget2D, None)
    a1 = Arg(Widget2D, None)
    a2 = Arg(Widget2D, None)
    a3 = Arg(Widget2D, None)
    a4 = Arg(Widget2D, None)
    a5 = Arg(Widget2D, None)  # use ArgList here when that works
    a6 = Arg(Widget2D, None)
    a7 = Arg(Widget2D, None)
    a8 = Arg(Widget2D, None)
    a9 = Arg(Widget2D, None)
    a10 = Arg(Widget2D, None)
    a11 = Arg(Widget2D, None)
    toomany = Instance(TextRect("too many args to SimpleRow"))
    args = list_Expr(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
                     and_Expr(a11, toomany))

    pixelgap = Option(int, 3)
    gap = pixelgap * PIXELS

    drawables = call_Expr(lambda args: filter(None, args), args)
    empty = not_Expr(drawables)
    btop = call_Expr(
        lambda drawables: max([arg.btop for arg in drawables] + [0]),
        drawables)
    bbottom = call_Expr(
        lambda drawables: max([arg.bbottom for arg in drawables] + [0]),
        drawables)
    width = call_Expr(
        lambda drawables, gap: sum([arg.width for arg in drawables]) + gap *
        max(len(drawables) - 1, 0), drawables, gap)
    bleft = or_Expr(and_Expr(a0, a0.bleft), 0)
    bright = width - bleft

    def draw(self):
        glPushMatrix()
        prior = None
        for a in self.drawables:
            if prior:
                # move from prior to a
                dx = prior.bright + self.gap + a.bleft
                glTranslatef(dx, 0,
                             0)  # positive is right, and Row progresses right
            prior = a
            self.drawkid(a)  ## a.draw()
        glPopMatrix()
        return

    pass  # end of class SimpleRow
Esempio n. 3
0
class checkbox_v3(
        InstanceMacro
):  ##e rename # note: this can do something checkbox_pref can't yet do -- take an external stateref
    stateref = Arg(StateRef,
                   None)  ### default? might not work with a default val yet
    ### IMPLEM: specify what external state to use, eg a prefs variable, PrefsKey_StateRef(displayOriginAxis_prefs_key)
    defaultValue = Option(bool, False)  ###BUG -- not used! [noticed 061215]
    ## var = State(bool, defaultValue)
    var = stateref.value
    ##    # print "var = %r" % (var,) # TypeError: 'module' object is not callable - on line that says on_press = Set(var, not_Expr(var) )
    ##        # solved: it's probably from above: import Set; from Set import something else but not Set
    _value = Highlightable(
        If(
            var,
            checkbox_image('mac_checkbox_on.png'),
            checkbox_image('mac_checkbox_off.png'),
        ),
        ## on_press = Set(var, not_Expr(var) ) # wanted (sort of), but doesn't work yet, as explained here:
        ## AssertionError:... <C_rule_for_formula at 0xff2de10 for 'var' in 'checkbox_v3'> should implement set_for_our_cls
        # ah, I was assuming "var = stateref.value" would alias var to obj.attr permitting set of obj.attr by set of var,
        # but of course, that should probably never be permitted to work, since it looks like we're changing self.var instead!
        # Unless, I either:
        # - implem a variant of "var = stateref.value"
        # - decide that since var would normally be unsettable when defined by a formula, that always aliasing it like that
        #   would be ok (often wanted, not too confusing).
        # PROBLEM WITH PERMITTING IT: you might start out var = State(), and it works to change self.var,
        # then change var to a formula, and forget you were directly setting it... causing an unwanted actual working set
        # of other state which you thought you were not touching or even able to touch.
        # SOLUTION:
        # - implem a variant of "var = stateref.value, sort of a "state alias"
        # - have a clear error message from this Set, suggesting to change that assignment to that alias form.
        # as of 061204 418p: tentatively decided I like that, but the alias variant syntax is not yet designed.
        # Maybe: ####@@@@
        # - var = Alias(stateref.value) ?? [sounds good]
        # - or just var = State(stateref.value), or is that too confusing?
        #   [yes, too confusing, that arg is for the type, and State is for new state, not an alias... latter is lesser problem]
        # - or var = StateRef(stateref.value)? no, it's an lval, not a stateref.
        on_press=Set(stateref.value, not_Expr(var))
        # kluge: see if this works (I predict it will) -- workaround of not yet having that alias form of "var = stateref.value"
    )
    pass  # end of class checkbox_v3
Esempio n. 4
0
class World(InstanceOrExpr
            ):  #070205 revised, public nodelist -> private _nodeset
    """maintains the set of objects in the model; provides general operations on them
    """
    #k is this 070401 change ok: ModelObject -> InstanceOrExpr? At least it doesn't seem to have caused trouble.
    ###e Q: Is it ok for this to inherit _CoordsysHolder and have .draw save the coords? Or to own one of those to hold them?
    # The goal is for some external thing to be able to copy the coords we will draw a newly made thing under.
    # that's partly misguided given that we might in theory draw things in more than one place
    # and/or in a different place than in the prior frame. The external thing that wants to do this
    # is self._newobj.copy_saved_coordsys_from( self.world) in class PalletteWell.on_press as of 070401.
    # It wants to use the right coordsys to turn a mouseray into that obj's initial position in the World. Hmm.
    #    A: In principle, if we're drawing the world twice (eg in stereo), we'd better be able to tell from a mouseray
    # used to put something into it, which world-instance we're putting it into! and each of those needs to know
    # where mouse events would drop things (based on where it was last drawn -- no ambiguity there).
    # So it's correct for something to save that and reveal it -- it just needs to be a world-drawn-instance
    # which would somehow show up (as selobj?) when interpreting that mouseray. For now, it's ok to kluge this
    # by assuming this class is taking the role of world-drawn-instance and being drawn only once. Alternatively,
    # we could say that the recipient of the mouseray should also be told what background it's over (whether or not it
    # hit the background or some object drawn over it) (this might be "one of several background objects" for stereo)
    # so it would know how to find "absolute model coords" in a canonical way. Or a third way is for the World client code
    # to include an invisible object drawn just to capture the coords. In the multi-world-drawn-instance case, we'd have to combine
    # this object with our knowledge of which worldinstance was drawn, by storing the drawn coords under the crossproduct
    # of that and the object...
    # ... Trying one of these solutions with self._coordsys_holder below. Seems to work & fix the pallette bug. Comment needs cleanup.
    # [070401]
    #
    ###FLAW: logically, this should store exprs needed to remake its instances, merely caching the instances,
    # but in fact, stores the instances themselves. This will make save/load harder, and means instances survive code-reloading.
    # Probably it should be fixed before trying to do save/load. [070206 comment]

    _nodeset = State(
        Anything, {}
    )  # self._nodeset is private; it's changetracked but only when entirely replaced (not just modified!)
    # (###e optim: can that be fixed? we need a way to access the LvalForState and inval it)
    ###e we'll need to put that State on a different state-layer (i.e. kind of StatePlace) when we start saving/loading this

    _coordsys_holder = Instance(SavedCoordsys(
    ))  #070401 -- see long comment above, about _CoordsysHolder

    def _init_instance(self):
        super(World, self)._init_instance()
        set_default_attrs(self.untracked_model_state, _index_counter=4000
                          )  #070213; could State() do this for us instead? #e

    def _C_number_of_objects(self):
        """Compute self.number_of_objects, defined as the total number of objects explicitly stored in self,
        of all types, in all Parts and configurations (but not including objects removed by Undo but present in Undo history).
           WARNING: Parts, configurations, and Undo are nim for this class as of 070206.
           Note: probably not very useful, due to that lack of qualification by anything.
        Perhaps useful to know whether self is entirely empty,
        or as a ballpark estimate of how much data it might contain.
        """
        return len(self._nodeset)

    def list_all_objects_of_type(self, model_type):
        """Return a nominally read-only list of all objects in self of the given model_type (or IorE class, as a special case),
        in a deterministic order not yet decided on (might be creation order, world-index order, MT order).
        [Current implem uses creation order as an Instance, as told by _e_serno; this order will become unideal as new kinds of
         object ops are added, like potential objs or moving them from other worlds (if possible). ##e 070206]
        ##e needs options for "not just this config", "not just this Part" (and revised implem, once we support configs or Parts)
        #e optim: extend API to be able to specify ordering -- otherwise it has to be sorted twice (here, and often in caller).
        """
        type_predicate = model_type_predicate(
            model_type
        )  # this permits model_type to be an IorE subclass (or its name)
        ###BAD: not enough kinds of type exprs can be passed, and error checking on model_type (not being garbage) is nim
        return filter(
            type_predicate, self._sorted_objects
        )  ##e optim: filter first (or keep them already type-separated), then sort

    def _append_node(self, index,
                     node):  #070201 new feature ###e SHOULD RENAME [070205]
        "not sure if this should be private... API revised 070205, ought to rename it more (and #doc it)"
        ##        self._nodeset = dict(self._nodeset)
        ##            ###KLUGE: copy it first, to make sure it gets change-tracked. Inefficient when long!
        ###BUG in the above:
        # doesn't work, since the change to it is not tracked, since the old and new values compare equal! (guess)
        #k note that LvalForState may also not keep a deep enough copy of the old value in doing the compare,
        # to not be fooled by the direct modification of the dict (in the sense of that mod escaping its notice, even later)!
        # (i'm not sure -- needs review, e.g. about whether bugs of this kind will go undetected and be too hard to catch)
        ##        self._nodeset[index] = node
        newset = dict(self._nodeset)
        newset[index] = node
        self._nodeset = newset  # this ought to compare different and cause changetracking (assuming the index was in fact new)
        ###e OPTIM IDEA: can we modify it, then set it to itself? No, because LvalForState will compare saved & new value --
        # the same mutable object! We need to fix that, but a workaround is to set it to None and then set it to itself again.
        # That ought to work fine. ####TRYIT but then fix LvalForState so we can tell it we modified its mutable contents. [070228]
        return

    def _C__sorted_objects(self):
        """compute private self._sorted_objects (a list, ordered by something not yet decided,
         probably creation time; probably should be the same order used by list_all_objects_of_type)
        """
        res = self._nodeset.values()
        res = sorted_by(res, lambda obj: obj._e_serno)
        ###KLUGE: use _e_serno in place of .creation_order, not yet defined --
        # works now, but wrong in general due to potential objects or moved-from-elsewhere objects.
        # (Q: Would index-in-world be ordered the same way? A: Yes for now, but not good to require this in the future.)
        return res

    def draw(self):
        # draw all the nodes [#e 070228 ###e in future we're more likely to draw X(node) for X supplied from caller & subset of nodes]
        # [optim idea 070103 late: have caller put this in a DisplayListChunk; will it actually work?
        #  the hope is, yes for animating rotation, with proper inval when nodelist changes. It ought to work! Try it. It works!]
        self._coordsys_holder.save_coords_if_safe()  #070401
        for node in self._sorted_objects:
            # print "%r is drawing %r at %r" % (self, node, node.pos) # suspicious: all have same pos ... didn't stay true, nevermind
            self.drawkid(node)  ## node.draw()
            # this assumes the items in the list track their own posns, which might not make perfect sense;
            # otoh if they didn't we'd probably replace them with container objs for our view of them, which did track their pos;
            # so it doesn't make much difference in our code. we can always have a type "Vertex for us" to coerce them to
            # which if necessary adds the pos which only we see -- we'd want this if one Vertex could be in two Worlds at diff posns.
            # (Which is likely, due to Configuration Management.)
            if 0 and node is self._sorted_objects[-1]:
                print "drew last node in list, %r, ipath[0] %r, pos %r" % (
                    node, node.ipath[0], node.pos)
        ###e see comment above: "maybe World needs to wrap all it draws with glue to add looks and behavior to it"
        return

    def make_and_add(
        self,
        expr,
        type=None
    ):  #070206 added type option -- required for now (temporary); semantics not fully defined
        """Make a new model object instance from expr, add it to the world at a local index we choose, and return it.
        This is the main public method for making new model objects.
           [As a temporary ###KLUGE (070206), the type needs to be passed, for purposes of methods like self.list_all_objects_of_type.
        This will no longer be needed once we can reliably infer the type (including supertypes) from the made instance of expr.
        But that probably requires altering delegation to specify which attrs to delegate (based on which interface they're part of),
        including a new attr for model object type. ###e Even once that's done, the caller may want to pass type-like info, perhaps
        "tags" -- e.g. one current caller passes type = "dot" which could not be inferred from expr it passes. ###e Callers may also
        want to pass desired relations, like a parent object, for convenience, or to influence details of how we make and index
        the new instance. ##e callers will also want to wrap objects with type-nicknames (see code comment for details).
           [WARNING: the API may be revised to also return the index. Or, we might store that in object, or store a back-dict here.]
        """
        index, node = self._make(expr)
        # that picks a unique index (using a counter in transient_state); I think that's ok
        # (but its change/usage tracking needs review ####k)
        self._append_node(index, node)  # revised 070205
        if 'model_type_on_object_kluge_070206':  ###KLUGE -- store type on object (simulates later inferring it from object)
            # update 070213: the right thing to do is probably not this, but to wrap the object itself with a type-nickname
            # (before passing it to us), so not only this world (self), but everything that sees it (MT default label, commands/ops,
            # selobj checks, etc), can react to its type-nickname.
            if type is not None:
                # node._i_model_type is defined on all Instances, and node should be one
                ##                if not hasattr(node, '_i_model_type'):
                ##                    node._i_model_type = None # this is how much of a kluge it is -- we're not even sure which classes need this attr
                if node._i_model_type is not None:
                    assert node._i_model_type == type, "make_and_add type %r inconsistent with existing type %r on %r" % \
                           (type, node._i_model_type, node)
                node._i_model_type = type
            pass
        return node

    def _make(self, expr):  # moved here from demo_drag 070202
        """[private]
        Allocate a new index, use it as the localmost component of ipath while making
        [or finding?? I THINK NOT ####k] expr,
        and return (index, new-expr-instance).
           Note that it's up to expr whether to actually make use of the suggested ipath
        in the new instance. The case of one instance stored with different indices in World
        is not reviewed, and not recommended until it is, but it's up to the caller to
        worry about. I don't know it's wrong, just never thought about it and assumed it would not happen
        when analyzing the code.
        """
        index = None
        #e rename? #e move to some superclass
        #e worry about lexenv, eg _self in the expr, _this or _my in it... is expr hardcoded or from an arg?
        #e revise implem in other ways eg eval vs instantiate
        #e default unique index?? (unique in the saved stateplace, not just here)
        # (in fact, is reuse of index going to occur from a Command and be a problem? note *we* are acting as command...
        #e use in other recent commits that inlined it
        if index is None:
            # usual case I hope (due to issues mentioned above [maybe here or maybe in demo_drag.py]): allocate one
            index = self.untracked_model_state._index_counter
            if 'index should be modified [070203]':
                # see comments above dated 070203
                index = index + 1
                self.untracked_model_state._index_counter = index
                # 070213 changed implem of _index_counter to make sure it's not change/usage-tracked.
                # (Fyi, I don't know whether or not it was before (using State()), in a way that mattered.
                #  The tracking danger would be that whenever you make any new object,
                #  the old objects see that the index they used is different
                #  and think they too need remaking, or something like that! This needs thinking through
                #  since it probably covers all make-data, not just the index. All make-data is being snapshotted.
                #  For that matter, things used by "commands" are in general different than things used to recompute.
                #  Maybe entire commands need to have tracked usage discarded or kept in a new kind of place. #####FIGURE OUT)
                #
                # Note (language design flaw): to set this attr (using current code),
                # you have to refer to it directly in the stateplace,
                # rather than using the kind of abbrev that (in Highlightable glname) seems to work for get:
                #   _index_counter = _self.untracked_model_state._index_counter
                # This could possibly be fixed in C_rule_for_formula (not sure), or by making the abbrev in a fancier manner.
                # (If you try to set the abbrev, you get
                # "AssertionError: subclass [C_rule_for_formula] ... should implement set_for_our_cls".)
                #
                ###BUG: even for get, the abbreviated form (self._index_counter) is not updated when the spelled out form is!!
                # See this debug print from when we tried that here:
                ## print "after set to %r, self._index_counter = %r, self.untracked_model_state._index_counter = %r" % \
                ##   (index, self._index_counter, self.untracked_model_state._index_counter )
                ## # it prints: after set to 4002, self._index_counter = 4001, self.untracked_model_state._index_counter = 4002
                # This is weird, since Highlightable seems to succeed using a similar glname abbrev for get.
                # (Unless it doesn't, and I never noticed?? To check, I just added debug code for that -- so far untriggered.)
                pass
            ###e LOGIC ISSUE: should assert the resulting ipath has never been used,
            # or have a more fundamental mechanism to guarantee that
        env = self.env  # maybe wrong, esp re _self
        ipath = (index, self.ipath)
        return index, env.make(expr,
                               ipath)  # note: does eval before actual make

    # ==

    def _C_mt_kids(
            self):  # 070206 experiment related to ModelTreeNodeInterface (sp?)
        ###e how do we depend on the mt display prefs? note we need to be drawn twice, once in graphics area using .draw
        # and once in MT using the mt_* methods, but with independent envs provided! Self.env might have room in attr namespace,
        # but it has a single origin. Besides there might be two indep MT views or graphics views -- each of those also needs
        # separate envs (of the same kind). But that makes us two instances! I guess we need separate World data obj vs World viewer,
        # to handle that! I'm not even sure it makes sense to put the viewing methods in the same object... but maybe it does
        # with this concept of partial instantiation [nim], in which we could instantiate the viewing layer(?) (interface??) twice,
        # and the data layer just once, letting it (an instance) serve as the expr for instantiating the viewing layer in two places.
        # (But this makes it clear that the env would need to be split into separate parts, one for each partially instantiable
        #  layer -- hopefully these parts would correspond to interfaces (or sets of them), i.e. an interface's attrs would
        #  have to all be instantiated at the same time, and decls would control which ones were instantiated together in which
        #  partial-instantiation layers.)
        #
        # So for now let's pretend self.env can tell us... tho as initial kluge, the global env.prefs (get_pref?) could tell us.
        # But even sooner, just pretend we don't care and always show all the kids.
        return self._sorted_objects

    mt_name = "testmode"  #e or maybe something like State(str, "Untitled"), or a stateref # or "Untitled" as it was before 070208
    mt_openable = True
    ## mt_node_id = getattr_Expr( _self, '_e_serno')
    mt_node_id = getattr_Expr(
        _self, 'ipath'
    )  #e optim: should intern the ipath ###e move this to IorE? btw redundant w/ def mt_node_id

    # 070218 -- by test, using ipath (a bugfix) makes world.open persistent (as hoped/predicted);
    # probably doesn't affect open-MT newnode slowness (but now that's fixed in different way in demo_MT)

    # ==

    def _cmd_Clear(
        self
    ):  #070106 experimental naming convention for a "cmd method" -- a command on this object (requiring no args/opts, by default)
        if self._nodeset:
            # avoid gratuitous change-track by only doing it if it does something (see also _cmd_Clear_nontrivial)
            # NOTE: this cond is probably not needed, since (IIRC) LvalForState only invalidates if a same_vals comparison fails. ###k
            self._nodeset = {}
        return

    # related formulae for that command
    # (names are related by convention, only used in this file, so far; prototype for wider convention, but not yet well thought through)
    _cmd_Clear_nontrivial = not_Expr(
        not_Expr(_nodeset)
    )  #KLUGE: need a more direct boolean coercion (not that it's really needed at all)
    # can be used to enable (vs disable) a button or menu item for this command on this object
    _cmd_Clear_legal = True  # whether giving the command to this object from a script would be an error
    _cmd_Clear_tooltip = "clear all objects"  # a command button or menu item could use this as its tooltip ###e is this client-specific??

    def _cmd_Make(self):
        print "world make is nim"  ###

    _cmd_Make_tooltip = "make something [nim]"  ###e when we know the source of what to make, it can also tell us this tooltip

    pass  # end of class World
Esempio n. 5
0
class _MT_try2_node_helper(DelegatingInstanceOrExpr):
    """
    [private helper expr class for MT_try2]
    One MT item view -- specific to one node, one whole MT, and one (possibly time-varying) position with it.
    """
    # args ####e REORDER THEM
    node = Arg(ModelNode, doc = "any node that needs to be displayed in this MT")
        ###e NOTE: type coercion to this is nim; while that's true, we use helper functions like node_name(node) below;
        # once type coercion is implemented
        # (or simulated by hand by wrapping this arg with a helper expr like ModelTreeNode_trivial_glue),
        #  we could instead use node.mt_name, etc.)
    mt = Arg(MT_try2, doc = "the whole MT view, in which we store MT items for nodes, and keep other central state or prefs if needed")
    name_suffix = Option(str, "")
    initial_open = Option(bool, False, doc = "initial value of boolean state 'open'; only used when this item is first created")
        ##e should ask the node itself for the initial value of open (e.g. so new groups, trying to start open, can do so),
        # and also advise it when we open/close it, in case it wants to make that state persistent in some manner
        
    # WARNING: compare to MT_try1 -- lots of copied code after this point
    # WARNING: the comments are also copied, and not yet reviewed much for their new context! (so they could be wrong or obs) ###k
    
    # state refs
    open = State(bool, initial_open)
    
    # other formulae
    ###e optim: some of these could have shared instances over this class, since they don't depend on _self; should autodetect this
    # Note, + means openable (ie closed), - means closable (ie open) -- this is the Windows convention (I guess; not sure about Linux)
    # and until now I had them reversed. This is defined in two files and in more than one place in one of them. [bruce 070123]
    open_icon   = Overlay(Rect(0.4), TextRect('-',1,1))
    closed_icon = Overlay(Rect(0.4), TextRect('+',1,1))
    openclose_spacer = Spacer(0.4)
        #e or Invisible(open_icon); otoh that's no simpler, since open_icon & closed_icon have to be same size anyway

    # the openclose icon, when open or close is visible (i.e. for openable nodes)
    openclose_visible = Highlightable(
        If( open, open_icon, closed_icon ),
        on_press = Set(open, not_Expr(open)),
        sbar_text = getattr_Expr( _self, '_e_serno') #070301 this permits finding out how often MT gets remade/shared
            # (results as of 070301: remade when main instance is, even if going back to a prior testexpr, out of _19i & _30i)
     )
    
    openclose_slot = If( call_Expr(node_openable, node), openclose_visible, openclose_spacer )


    if 0:
        # cross-highlighting experiment, 070210, but disabled since approach seems wrong (as explained in comment)
        yellow = DZ = 'need to import these'
        indicator_over_obj_center = Center(Rect(0.4, 0.4, yellow))
        position_over_obj_center = node.center + DZ * 3 ###BUG: DZ does not point towards screen if trackballing was done
            ###STUB:
            # - should be drawn in a fixed close-to-screen plane, or cov plane (if obscuring is not an issue),
            #   - so indicator size is constant in pixels, even in perspective view (I guess),
            #   - also so it's not obscured (especially by node itself) -- or, draw it in a way visible behind obscuring things (might be a better feature)
            # - what we draw here should depend on what node is
            # - we also want to draw a line from type icon to node indicator (requires transforming coords differently)
            # - needs to work if node.center is not defined (use getattr_Expr - but what dflt? or use some Ifs about it)
        pointer_to_obj = DrawInCenter( Translate( indicator_over_obj_center, position_over_obj_center))
            #bug: Translate gets neutralized by DrawInCorner [fixed now]
            ###BUG: fundamentally wrong -- wrong coord system. We wanted DrawInAbsCoords or really DrawInThingsCoords,
            # but this is not well-defined (if thing drawn multiply) or easy (see comments about the idea in projection.py).
    else:
        # What we want instead is to set a variable which affects how the obj is drawn.
        # If this was something all objs compared themselves to, then all objs would track its use (when they compared)
        # and therefore want to redraw when we changed it! Instead we need only the involved objs (old & new value) to redraw,
        # so we need a dict from obj to this flag (drawing prefs set by this MT). Maybe the app would pass this dict to MT_try2
        # as an argument. It would be a dict of individually trackable state elements. (Key could be node_id, I guess.)
        # ### TRY IT SOMETIME -- for now, cross-highlighting experiment is disabled.
        pointer_to_obj = None

    # selection indications can use this
    node_is_selected = call_Expr( mt_node_selected, node)
    kluge_icon_color = If( node_is_selected, blue, green)
    sbar_format_for_name = If( node_is_selected, "%s (selected)", "%s")
    
    ###STUB for the type_icon ##e the Highlightable would be useful on the label too
    icon = Highlightable(
        Rect(0.4, 0.4, kluge_icon_color), ##stub; btw, would be easy to make color show hiddenness or type, bfr real icons work
        Overlay( Rect(0.4, 0.4, ave_colors(0.1, white, kluge_icon_color)),
                 #070216 mix white into the color like DraggableObject does
                 pointer_to_obj ),
        sbar_text = format_Expr( sbar_format_for_name, call_Expr(node_name, node) )
     )
    
    ##e selection behavior too

    label = DisplayListChunk(
        # added DisplayListChunk 070213 late -- does it speed it up? not much; big new-item slowness bug remains. retain, since doesn't hurt.
        TextRect( call_Expr(node_name, node) + name_suffix )
     )
        ###e will need revision to Node or proxy for it, so node.name is usage/mod-tracked
        ##e selection behavior too --
        #e probably not in these items but in the surrounding Row (incl invis bg? maybe not, in case model appears behind it!)
        ##e italic for disabled nodes
        ##e support cmenu
    
    delegate = SimpleRow(
        CenterY(openclose_slot),
        SimpleColumn(
            SimpleRow(CenterY(icon), CenterY(label)),
                #070124 added CenterY, hoping to improve text pixel alignment (after drawfont2 improvements) -- doesn't work
            If( open,
                _MT_try2_kids_helper( call_Expr(node_kids, node) , _self.mt ), # 070218 added _self.mt -- always intended, first used now
                None
                    # Note: this None used to be Spacer(0), due to a bug mentioned in a comment in ToggleShow.py
                    # (but unfortunately not explained there -- it just says "I wanted None here, but it exposes a logic bug,
                    # not trivial to fix, discuss in If or Column" -- my recollected bug-theory is described just below).
                    # On 070302 I confirmed that None seems to work (even in testexpr_18i with a group of 2 chunks, plus two more below).
                    # I don't fully know why it works, since I thought the bug was that SimpleColumn's None specialcase
                    # didn't run, since the element was not None but the If, and then delegating lbox attrs to None didn't work.
                    # (Fixable by using the newer If that evals, but for some reason that's not yet standard, I guess just because
                    # I didn't have time to test it enough or think it through fully re ipath or instance caching or something.)
                    # But as long as it works, use it -- ask Qs later. A recent perhaps-related change: None is allowed in drawkid.
                    # (A memory scrap -- does instantiating None conceivably produce a spacer?? ###k)
             )
         )
     )
    pass # end of class _MT_try2_node_helper
Esempio n. 6
0
class SimpleColumn_NEW(Widget2D): #061115, revised 070321 to use new ArgList -- but disabled 070322,
    # since too slow for routine use (eg in testexpr_19g),
    # until we fix that update speed bug which means a change in any element remakes them all.
    ### fyi: a test that puts too many elts for SimpleColumn_OLD into the MT: _30i, dna x 3, rects x 2

    #e or use InstanceMacro using Overlay & Translate? Could work, but slower and not needed... might help in CL with fancier gaps.
    ## a0 = Arg(Widget2D) # note: this probably doesn't preclude caller from passing None, and even if it does, nothing enforces that yet;
        # if caller does pass None (to this or to Overlay), best thing is probably to replace this with Pass = Rect(0,0,white)
        # and use it as a drawable, but have special case to use no gap under it -- or the equiv, as a simple change
        # to our btop formula so it's 0 if not a0 -- which is already done, so maybe there's no need to worry about a0 = None.
        # Maybe it should be an optional arg like the others. [061115]
    args = ArgList(Widget2D, (), doc = "0 or more things (that can participate in 2d layout) to show in a column")
    def _C_a0(self):
        args = self.args
        args[0:] # make sure this works (i.e. args is a sequence)
        if len(args):
            return args[0]
        else:
            return None
        pass
    a0 = _self.a0 # kluge, but legal (due to special case in ExprsMeta, documented in comments there):
        # get a0 into the class-def namespace for direct use below

    def _init_instance(self):##### debug only
        super(SimpleColumn, self)._init_instance()
        try:
            args = 'bug' # for use in error message, in case of exception
            args = self.args
            len(args) # make sure this works!
            if debug070321:
                print "fyi: this SimpleColumn has %d args" % len(args)
                print "fyi: the args i mentioned are: %r" % (args,) #####
        except:
            print "following exception concerns self = %r, args = %r" % (self, args)
            raise
        return
    
    ## gap = Option(Width, 3 * PIXELS)
    pixelgap = Option(float, 3) # 070104 int -> float
    gap = pixelgap * PIXELS

    print_lbox = Option(bool, False) #061127 for debugging; should be harmless; never tested (target bug got diagnosed in another way)
    
    drawables = call_Expr(lambda args: filter(None, args) , args)
    ## empty = not drawables ###e BUG: needs more Expr support, I bet; as it is, likely to silently be a constant False; not used internally
    empty = not_Expr(drawables)
    bleft = call_Expr(lambda drawables: max([arg.bleft for arg in drawables] + [0]) , drawables)
        # 070211 arg.bleft -> getattr_debugprint(arg, 'bleft')
    bright = call_Expr(lambda drawables: max([arg.bright for arg in drawables] + [0]) , drawables)
    height = call_Expr(lambda drawables, gap: sum([arg.height for arg in drawables]) + gap * max(len(drawables)-1,0) , drawables, gap)
    ## btop = a0 and a0.btop or 0  # bugfix -- use _Expr forms instead; i think this form silently turned into a0.btop [061205]
    btop = or_Expr( and_Expr( a0, a0.btop), 0)
    bbottom = height - btop
    def draw(self):
        if self.print_lbox:
            print "print_lbox: %r lbox attrs are %r" % (self, (self.bleft, self.bright, self.bbottom, self.btop))
        glPushMatrix()
        prior = None
        for a in self.drawables:
            if prior:
                # move from prior to a
                dy = prior.bbottom + self.gap + a.btop
                glTranslatef(0,-dy,0) # positive is up, but Column progresses down
            prior = a
            self.drawkid(a) ## a.draw()
        glPopMatrix()
        return
    pass # end of class SimpleColumn_NEW or SimpleColumn