Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Is there a good explanation of deserialization?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    #16
    Originally posted by koganam View Post
    I am well aware of the caching mechanism, which has its own issues, by the way. Object creation is a different kettle of fish: the object must be created or exist before it can be cached.
    Yep, which is why the cache lookup comes first.

    If the cache lookup fails to find a suitable previous reference matching the arguments supplied, then a new instance will be created ... and added to the cache.

    Comment


      #17
      Originally posted by bltdavid View Post
      No, if an instance to the indicator is found in the cache, it is the cache lookup code that returns the existing reference. If an instance is not found, a new one is created with 'new', it is initialized and added to the cache, and then returned. Class inheritance has nothing much to do with it, most of the magic comes from the cache feature built into the generated middleman SMA method, which is added to the Indicator (not IndicatorBase) class as a method.

      (Edit: More magic is supplied by the Indicator class being defined as "partial".)

      (Edit2: I think by "inheritance" you mean "access to". Since SMA is a class which inherits from class Indicator, it gains access to all the public & protected members of the Indicator class -- but the generated SMA public method is still supplying all the cache lookup and object creation magic behind the scenes -- you don't get that for free, someone in Denver had to invent that.]

      There is only one actual "premade" object,
      Code:
              private static SMA checkSMA = new SMA();
      which is used for lock() to protect the cache management and object creation code.

      All other objects are either created inside that lock() code as needed (because the cache lookup fails) or a reference to a previous instance (aka, your "premade") is found and returned.

      I think we're more close to saying the same thing than not, but I like the term "reference to a previous instance", since it is bit more exact than "premade object".

      Also, I'm looking at NT7 generated code in SMA.cs, perhaps NT8 is different?
      I am also talking from NT7. I seem to remember that the wrappers were a bit more complex than that earlier. Did they simplify? Do not quote me: I could very well be misremembering, and I am not going to install an old version just to see if I am.

      However, here is the relevant code for calling from a strategy. I have annotated it in blue.
      Code:
      // This namespace holds all strategies and is required. Do not change it.
      namespace NinjaTrader.Strategy
      {
          public partial class Strategy : StrategyBase [COLOR="Blue"]//make the code part of the Strategy class, that inherits from StrategyBase[/COLOR]
          {
              /// <summary>
              /// The SMA (Simple Moving Average) is an indicator that shows the average value of a security's price over a period of time.
              /// </summary>
              /// <returns></returns>
              [Gui.Design.WizardCondition("Indicator")]
              public Indicator.SMA SMA(int period) //a method called SMA that takes one parameter and returns an SMA object from the Indicator class
              {
                  return _indicator.SMA(Input, period); [COLOR="blue"]//the SMA object that is returned, from an object called [B]_[/B]indicator[/COLOR]. Note the preceding underscore
              }
      
              /// <summary>
              /// The SMA (Simple Moving Average) is an indicator that shows the average value of a security's price over a period of time.
              /// </summary>
              /// <returns></returns>
              public Indicator.SMA SMA(Data.IDataSeries input, int period) [COLOR="blue"]//an overload of the SMA method, that also returns an object from [COLOR="blue"]_[/COLOR]indicator[/COLOR]
              {
                  if (InInitialize && input == null)
                      throw new ArgumentException("You only can access an indicator with the default input/bar series from within the 'Initialize()' method");
      
                  return _indicator.SMA(input, period);
              }
          }
      }
      Finally, yes, you are right about the IndicatorBase issue. I should have looked again before writing. The methods at the top are made methods of the Indicator class, which inherits from IndicatorBase. Those methods are not made a part of the IndicatorBase class. My mistake. Silly. I should have referenced the file before spouting off.

      Comment


        #18
        Originally posted by koganam View Post
        I am also talking from NT7. I seem to remember that the wrappers were a bit more complex than that earlier. Did they simplify?
        Not sure, I'm not that focused on NT8 just yet.

        That "_indicator" object is defined in bin/Custom/Strategy/@Strategy.cs.

        I think "_indicator" is the single master global variable for the Strategy class, similar to how "Indicators" is a single master global variable of the Indicator class.

        (Edit: Hi Koganam, I figure you know all or most of this, but I am mixing in useful tidbits for newbies to follow along, so don't take offense if my writing style drops into a teaching / essay mode. I'm just trying to be complete, esp for newcomers and future readers.]

        As you pointed out, when calling SMA(n) from a strategy, it uses the SMA() method defined in this generated code,

        Code:
        namespace NinjaTrader.Strategy
        {
            public partial class Strategy : StrategyBase
            {
                public Indicator.SMA SMA(int period)
                {
                    return _indicator.SMA(Input, period);
                }
        
                public Indicator.SMA SMA(Data.IDataSeries input, int period)
                {
                    if (InInitialize && input == null)
                        throw new ArgumentException("You only can access an indicator with the default input/bar series from within the 'Initialize()' method");
        
                    return _indicator.SMA(input, period);
                }
            }
        }
        The "_indicator" instance is actually a reference to an ADX instance (created with 'new ADX()', see @Strategy.cs), but that very specific little factoid (of "_indicator" being an object of the ADX class, versus an object of a different class, such as EMA class) is actually ignored. The designers had to choose a class inherited from Indicator class, it doesn't matter which one (since they're only concerned with the base class), so they used ADX class as a way to get to the base Indicator class methods.

        Here's why.

        The great thing about having "_indicator" of class ADX is that (sure, you can call methods from the ADX class) but you get to call all the methods from the Indicator class as well. Why? Duh, because ADX class inherits from Indicator class. (That last sentence sounds very obvious, but it's a very big deal to making this magic work.)

        Which means, that special SMA() method, which was added to the Indicator class by the generated code from the compiler (see bottom of @SMA.cs), is available for the "_indicator" object to call.

        That is, when "_indicator.SMA()" is called from inside a strategy, it eventually calls the special SMA method which is part of the Indicator class (because we all know that the ADX class does not have a method named SMA, right?) So, inheritance says we skip the ADX class and call the SMA method from the Indicator class.

        [Edit: Why "eventually"? Because, calling SMA(n) inside a strategy first calls the SMA method in the Strategy namespace. This method uses the "_indicator" object as a clever kludge to call the SMA method in the Indicator namespace, which is what we really want. It seems like a complex and indirect way to do it, and it is, but the code needs access to the right SMA method, and they do that through a dummy "_indicator" object. Same trick is used for MarketAnalyzer namespace.]

        So, when "_indicator.SMA(input, period)" is being called, it's actually calling this code from the special SMA.cs generated code (located above the code you cited, at the bottom of SMA.cs, my explanatory code comments in red),

        Code:
        [COLOR=Red]// note the different namespace, this is very important
        // the SMA() method in the Strategy namespace is [U][B][I]completely different[/I][/B][/U] from
        // the SMA() method in the Indicator namespace[/COLOR]
        namespace NinjaTrader.Indicator
        {
            [COLOR=Red]// the [I]Indicator[/I] class [U][I][B]must be[/B][/I][/U] partial, so we can add stuff to it
            // [I]Indicator[/I] class inherits from [I]IndicatorBase[/I] class
            // [I]SMA[/I] class inherits from [I]Indicator [/I]class
            // this generated code creates an SMA [U][I][B]method[/B][/I][/U]
            // which returns an instance of an SMA [U][I][B]class[/B][/I][/U]
        [/COLOR]    public partial class Indicator : IndicatorBase
            {
                [COLOR=Red]// permanent cache array[/COLOR]
                private SMA[] cacheSMA = null;
        
                [COLOR=Red]// mutual exclusive lock object for critical code section[/COLOR]
                private static SMA checkSMA = new SMA();
        
                [COLOR=Red]// convenient overload method to call the big guy below[/COLOR]
                public SMA SMA(int period)
                {
                    return SMA(Input, period);
                }
        
                [COLOR=Red]// this method is the secret sauce, the missing link, all the magic happens here
                // this SMA [I][B]method[/B][/I] returns a reference to an object of the SMA [I][B]class
                // uses the cache first, otherwise creates a new instance
        [/B][/I][/COLOR]        public SMA SMA(Data.IDataSeries input, int period)
                {
                    [COLOR=Red]// Step 1: look in cache for previous instance w/[U][I][B]exact[/B][/I][/U] same parameters[/COLOR]
                    [COLOR=Red]// if found, return immediately with reference to that instance[/COLOR]
                    if (cacheSMA != null)
                        for (int idx = 0; idx < cacheSMA.Length; idx++)
                            if (cacheSMA[idx].Period == period && cacheSMA[idx].EqualsInput(input))
                                return cacheSMA[idx];
        
                    [COLOR=Red]// Step 2: a suitable previous instance was not located
                    // so we create a new instance and [/COLOR][COLOR=Red]initialize it
                    // add it the cache, then return it[/COLOR]
                    lock (checkSMA)
                    {
                        [COLOR=Red]// Step 3: Update properties of mutual exclusive lock SMA object[/COLOR]
                        [COLOR=Red]// I'm a little perplexed, not exactly sure why this is done[/COLOR]
                        checkSMA.Period = period;
                        period = checkSMA.Period;
        
                        [COLOR=Red]// Step 4: Perform exact same cache lookup as in Step 1, but this
                        // time we're under the protection of the critical section lock, this 2nd
                        // lookup was probably added later for multi-threading reasons[/COLOR]
                        if (cacheSMA != null)
                            for (int idx = 0; idx < cacheSMA.Length; idx++)
                                if (cacheSMA[idx].Period == period && cacheSMA[idx].EqualsInput(input))
                                    return cacheSMA[idx];
        
                        [COLOR=Red]// Step 5: Finally, create a new instance of the SMA class
                        // initialize all properties of that instance[/COLOR]
                        SMA indicator = new SMA();
                        [COLOR=Red]// Step 6: initialize all builtin properties with default values[/COLOR]
                        indicator.BarsRequired = BarsRequired;
                        indicator.CalculateOnBarClose = CalculateOnBarClose;
        #if NT7
                        indicator.ForceMaximumBarsLookBack256 = ForceMaximumBarsLookBack256;
                        indicator.MaximumBarsLookBack = MaximumBarsLookBack;
        #endif
                        [COLOR=Red]// Step 7: set all properties associated with SMA
                        // user-defined parameters from "Parameters" section[/COLOR]
                        indicator.Input = input;
                        indicator.Period = period;
                        [COLOR=Red]// Step 8: add this new object to the global "Indicators" list of objects[/COLOR]
                        Indicators.Add(indicator);
                        [COLOR=Red]// Step 9: call new indicator's internal Setup() method[/COLOR]
                        indicator.SetUp();
        
                        [COLOR=Red]// Step 10: allocate cache first time, or allocate new cache with size + 1
                        // this new temp array will become the permanent cache array later
        [/COLOR]                SMA[] tmp = new SMA[cacheSMA == null ? 1 : cacheSMA.Length + 1];
                        if (cacheSMA != null)
                            [COLOR=Red]// Step 11: copy existing cache array (if any) to temp array[/COLOR]
                            cacheSMA.CopyTo(tmp, 0);
                        [COLOR=Red]// Step 12: append newly created indicator reference to temp array[/COLOR]
                        tmp[tmp.Length - 1] = indicator;
                        [COLOR=Red]// Step 13: keep this temp array as the permanent cache array[/COLOR]
                        cacheSMA = tmp;
                        [COLOR=Red]// Step 14: return newly created indicator reference[/COLOR]
                        return indicator;
                    }
                }
            }
        }
        I've always found Step 3 above perplexing.

        I'm still not sure why that is done. At first glance, it seems rather pointless.

        Any hypothesis on that one?

        [Edit: The only reason I can think of is: force a compiler error if the indicator's property does not define the get() and the set() methods. Perhaps this is the poor man's way of enforcing the rule: indicators must define both methods for each public property exposed as a parameter.]
        Last edited by bltdavid; 08-09-2016, 11:16 AM.

        Comment


          #19
          Wow! Some discussion. I don't really have time to address it all right now, but I'll just make a few observations.

          1) It is NEVER correct, or acceptable, to make calls on an object whose constructor has not yet been run. That's just basic OOP 101. I know NT does it. They had that bad habit in NT7, and I had to code a work-around. Now I know there is at least one case in NT8 where they do so. Realistically we have to do the best we can to deal with NT's bugs. That does not change the fact that it is a bug, and one that I would very much like to see fixed.

          2) I am well aware of the state change calls, and that some, probably most, things should be done there instead of in the constructor. That does not change the fact that the constructor MUST be called before doing anything else, including making calls to OnStateChange(). Furthermore, in the case where I complained no constructor was called, OnStateChange() was also ineffective -- I was doing the requisite initialization in State.SetDefaults, but that did not get called either.

          I set it both places after I noticed I was having problems with NT calling before initialization had occurred. SetDefaults was the correct place, but the constructor should have guaranteed that it would be set no matter what.

          BTW: if you are going to take the position that initialization should be done in one of the states instead of in the constructor, then you have to guarantee no other calls before that state has been run. Specifically, no calls to even ToString() before the object has had a chance to initialize whatever ToString() may use!

          3) Properties exist in the context of the indicator whose properties they are. It is never acceptable to just access or instantiate a property independent of a properly constructed / initialized indicator. There are some fundamentally different types of properties.
          • Access: basic types, such as int and bool. Since these cannot be instantiated outside the indicator the only real issue is that the indicator has been properly constructed before accessing the property. You may generally get away with using an indicator object that has not been constructed, because Get() and Set() are usually just vanilla operations -- just fetch or set the data without transforming it. It does not work in general, though, because Get() or Set() may be taking into account information from the indicator such that trying to use the property before the indicator has been properly constructed will not to work as expected.

          • Instantiation of objects: expandable objects are an example and the case at hand. The indicator may well do some initialization of them, such as passing in information that the property needs but has no way to find out for itself. The only guarantee to an outsider is that if one calls the property's Get() function a properly initialized and functioning object will be returned. It is completely unacceptable to just instantiate such a property and expect it to work correctly. NT MUST get an instance of the property by calling a properly constructed indicator's Get() function. There is no other way to be assured the property will behave correctly. In the case I cited, the bad behavior consisted of the property object trying to use a null reference. To kludge around the NT bug I now check for a null reference. Aside from the minor unnecessary performance hit, the bigger issue is that the property loses a little of its functionality in that case

          • Bottom line: the ONLY legitimate way to access an indicator's property is by using the Get() and Set() methods of a properly constructed instance of that indicator. Anything else is a bug -- it may usually work OK, but cannot work in general.

          4) I noticed remarks about caching and cloning -- none of that is relevant to this discussion. Caching properly constructed objects is fine with me. Cloning, assuming it is done correctly, results in a properly constructed object and so is also fine with me.


          --EV

          Comment


            #20
            Originally posted by ETFVoyageur View Post
            Bottom line: the ONLY legitimate way to access an indicator's property is by using the Get() and Set() methods of a properly constructed instance of that indicator. Anything else is a bug -- it may usually work OK, but cannot work in general.
            Yep, those getters and setters are exactly the means by which NinjaScript gets and sets your indicator's properties.

            But your definition of "properly constructed" assumes too much.

            Why? Because the class's instance constructor is called every time a new instance is created. This is just fundamental part of C# language, and it does get called.

            But, by definition, a constructor is only called once per instance.

            The problem is timing. You cannot depend on the sequence of events -- meaning you cannot control whether NinjaTrader returns to you a freshly constructed reference or a cached reference who's constructor was called long ago.

            Originally posted by ETFVoyageur View Post
            4) I noticed remarks about caching and cloning -- none of that is relevant to this discussion. Caching properly constructed objects is fine with me. Cloning, assuming it is done correctly, results in a properly constructed object and so is also fine with me.
            I think caching is completely and 100% relevant to the discussion.

            Caching is at the core of your entire complaint.

            You're not understanding the caching magic code enough to realize that when a cached instance is re-used, there is simply no mechanism in C# language or .NET framework for NinjaTrader engineers to somehow re-invoke the class instance constructor method that you need executed.

            I mean, when a cached instance is returned to you, the cached instance already refers to an existing object, why are you even expecting the constructor be called again?
            Last edited by bltdavid; 08-08-2016, 08:40 PM.

            Comment


              #21
              Originally posted by ETFVoyageur View Post
              1) It is NEVER correct, or acceptable, to make calls on an object whose constructor has not yet been run. That's just basic OOP 101. I know NT does it.
              No, NinjaTrader does not and cannot do this.

              The C# language and the .NET framework do not allow NinjaTrader to avoid the calling of a class's instance constructor.

              You are at a fundamental disadvantage in your code if you are depending upon the use of an instance constructor in your C# indicator class definition.

              I think the problem is you do not understand the side-effects of the NinjaTrader's indicator reference caching mechanism.

              Again, even when a cached reference is being returned to you, that instance had its constructor run once, but only once, perhaps long ago in the past.

              After a class instance constructor is executed for that instance, the constructor is never run again for that instance. This is just C# programming basics.
              Last edited by bltdavid; 08-08-2016, 07:48 PM.

              Comment


                #22
                Originally posted by NinjaTrader_Brett View Post
                In general we don't recommend user work with constructors at all nor do we document such use with NinjaScript. In lieu of constructors we do support and recommend the OnStateChanged event system with the SetDefaults and Configure events.
                ETFVoyageur, I presume this has been pointed out many times.

                I don't understand why you insist upon using a constructor.

                It is a deeply fundamental design feature of the NinjaScript framework that the compiler generated code for each indicator contain a cache lookup and object creation method embodied in a special C# method named the same as the indicator.

                It appears the side-effects of NinjaScript's caching mechanism is the precise underlying reason why your reliance upon a constructor has been hazardous to your health.

                With respect, of course.
                Last edited by bltdavid; 08-08-2016, 08:45 PM.

                Comment


                  #23
                  This thread has some confusion (e.g. why in the world would anyone think I believe a constructor should be called a second time? Yikes!) I'm going to ignore all that for now and stay focused on the actual problem.

                  I have taken a closer look, and believe I see just what is going on. The basic problem is NT creating an invalid property. The situation is that I have an indicator that has two properties, each of which is an instance of an expandable object. Here's what is happening:

                  * Good: Property #1 is constructed as part of constructing the indicator
                  * Good: Property #2 is constructed as part of constructing the indicator
                  * Good: Indicator's constructor is run
                  * Good: Indicator gets to State.SetDefaults, which passes the dynamic data to the two properties. At this point both properties are complete and internally consistent.

                  ** VERY BAD ** NT constructs another instance of the property, independent of the indicator. It then sets a couple of properties in that object. Unfortunately it does not have enough information to create a fully-consistent property -- it does not have the dynamic information the indicator provides.

                  ** VERY BAD ** NT then calls the indicator's set() method to replace the good property with the bogus one. What NT should be doing is fetching the indicator's good property and setting any values it wants to directly in that property. That would be at least as efficient and would be guaranteed to produce a correct result.

                  * (ditto for the second property)

                  =====

                  The reason the original posting said it looked as if the constructor had not run is that it never occurred to me that NT would try to construct the property out of context. It just does not have enough information to do so successfully. There is a good reason for copy constructors and Clone() methods.

                  I presume the issue is that NT needs a clone of the property. One valid way to do so would be to instantiate a new indicator and then call the get() method for the property. That would return an instance that is guaranteed to be valid.

                  NT could support an optional Clone() method. Only objects that care (a minority) would need to provide this.

                  NT could also require that all such objects provide a Clone() method. Not done today, but it could be done and would also work.
                  Last edited by ETFVoyageur; 08-09-2016, 08:05 AM.

                  Comment


                    #24
                    4) I noticed remarks about caching and cloning -- none of that is relevant to this discussion. Caching properly constructed objects is fine with me. Cloning, assuming it is done correctly, results in a properly constructed object and so is also fine with me.
                    Actually, that may be the heart of the matter, because if NT finds a suitable object in the cache, NT will return that object instead of constructing a new one. In effect, you can never be sure that a constructor will run, because you cannot really be sure that a new object will be created.

                    That is why when I want that kind of control, I write my own constructors. The fact that the NT framework is written to not need constructors in the class, does not mean that you cannot use constructors in the class. It does mean though that you would have to ignore the NT caching, by creating all your objects using the new keyword, and making them all named instances so that you can reference them directly. I have not found calling the indicator SetUp() method on these new instances to be an absolute requirement, but I do it anyway. In other words, you would have to dispense almost completely with NT's quasi-anonymous class methods that allow you to appear to be creating an indicator without the new keyword.

                    Of course, even after that, one can still use the standard NT methods that use the cache anyway. Pretty much the best of both worlds: NT's framework and the purist both.

                    This is an issue with a lot of frameworks. They use what are valid methods, to get around some of the stricter restrictions of a language, usually in order to get better performance, and as a result, some things then do not work quite the way that a purist would expect. If we want to use a framework, we are often forced to live with how the framework does things, or else we may have to find what may look like inelegant workarounds. It is just the nature of the beast.

                    Comment


                      #25
                      This discussion of constructors is a red herring. Now that I understand the problem, as detailed in my previous post today, there is no constructors problem. They are constructing everything. The problem is that they mistakenly think they are capable of creating a brand new instance of the property, independent of the indicator. That is not, in general, possible. Usually, but not in general and not in my case. The only way to get a valid instance today is to use an instance of the indicator and call get() on the property. Perhaps in the future they will add an optional or mandatory Clone() method for such classes. Until they do, though, they MUST use an instance of the indicator if they want to get their hands on an instance of the property object. Then, once they have it, they can use it however they please.

                      As to cache -- to me a cache is where they squirrel away an existing, properly constructed, object instance so they can reuse it later without the overhead of constructing a new instance. I have no problem with that, and the issue I am raising is not in conflict with that. I certainly would not expect another constructor call on an existing object, cached or not. I certainly do expect a constructor to run when an object is first created, but not later again on the same object.

                      Comment


                        #26
                        Originally posted by ETFVoyageur View Post
                        ...
                        As to cache -- to me a cache is where they squirrel away an existing, properly constructed, object instance so they can reuse it later without the overhead of constructing a new instance. I have no problem with that, and the issue I am raising is not in conflict with that. I certainly would not expect another constructor call on an existing object, cached or not. I certainly do expect a constructor to run when an object is first created, but not later again on the same object.
                        Agreed, but that is the crux of the matter. When you try to construct your new object, is NT returning a really new object, or one from the cache? Unless you go to special lengths, you cannot know. That is the point that I am making. And that is why you need the workaround that you are using.

                        But that is only one way to workaround the issue. For me, the simpler way is the one that I use, where I take full control of my object creation when I need to have such control. Indeed, sometimes it is sufficient to just used only named instances of all objects that I create, using NT's method that it sticks in the Indicator partial class: that obviates the cache too.
                        Last edited by koganam; 08-09-2016, 10:36 AM. Reason: Corrected spelling.

                        Comment


                          #27
                          I hear you on the cache issue.

                          I still do not see the relevance to my issue, however -- my issue is that NT is constructing a new property class object, even though NT does not have sufficient information to set the property up correctly in the general case -- usually, but not in general (and not in my case). *I* am not constructing the object, nor am I asking for it. NT is choosing to construct it. The object I am objecting to is not coming from cache -- it is being newly constructed, constructor and all. I then went on to suggest a way they could easily get it correct with the system as it is.

                          That said, I can see a scenario for how the cache could be a problem in slightly different circumstances. I also have a simple brute-force fix I'll go put in right away. Thanks for getting me thinking about that.
                          Last edited by ETFVoyageur; 08-09-2016, 10:17 AM.

                          Comment


                            #28
                            Originally posted by ETFVoyageur View Post
                            * Good: Property #1 is constructed as part of constructing the indicator
                            * Good: Property #2 is constructed as part of constructing the indicator
                            * Good: Indicator's constructor is run
                            * Good: Indicator gets to State.SetDefaults, which passes the dynamic data to the two properties. At this point both properties are complete and internally consistent.

                            ** VERY BAD ** NT constructs another instance of the property, independent of the indicator. It then sets a couple of properties in that object. Unfortunately it does not have enough information to create a fully-consistent property -- it does not have the dynamic information the indicator provides.

                            ** VERY BAD ** NT then calls the indicator's set() method to replace the good property with the bogus one. What NT should be doing is fetching the indicator's good property and setting any values it wants to directly in that property. That would be at least as efficient and would be guaranteed to produce a correct result.
                            In your latest post, sounds like you're finding a path forward. That's excellent news.

                            By any chance, do you have a small script that demonstrates the issues you're seeing? Is there another thread of yours (with code attachments) I should catch up on?

                            An NT8 script is fine -- I'll backport it to NT7 and investigate it there.

                            Like koganam, I, too, have gutted and rebuilt the generated code section, not only to reorganize the argument order but also to avoid using a cache (interesting experiment, that one).

                            I'd love to step through your code, esp the part about the expandable property, since I have several of those I've built and use extensively. I'm very curious myself to understand what is going on.

                            Got any sample code?

                            Thanks

                            Comment


                              #29
                              Originally posted by bltdavid View Post
                              In your latest post, sounds like you're finding a path forward. That's excellent news.

                              By any chance, do you have a small script that demonstrates the issues you're seeing? Is there another thread of yours (with code attachments) I should catch up on?

                              An NT8 script is fine -- I'll backport it to NT7 and investigate it there.
                              Yes, now that I understand what NT is doing to me I have two hacks in to address the problems. In the property class I test for null and just skip that functionality if so -- there is no way to do anything more constructive. In the set() accessor I coerce the dynamic stuff -- that handles both the known bug of passing me a bogus object (which includes a null value) and the conceivable problem of being passed a cached object whose dynamic value is wrong. Brute force, but effective and the only solution I know of short of NT fixing their code.

                              Small script -- perhaps. I have one close enough I may be able to fix it up -- I guess I may need to do so with or without you.

                              Other thread -- no, this is the first I have been aware of this problem.

                              NT8 -- yes, NT8. I have no idea how it will behave in in NT7. As I recall NT7 had problems with expandable objects, but you sound as if you have that all figured out. Expandable objects -- I have several one-off's just because they make for a cleaner more intuitive GUI. I also have my own font picker to replace the SimpleFont GUI. Mine:
                              • GUI is deliberately similar to SimpleFont so it will be familiar to users of other places in the GUI that solicit a font (I hope they all change to points, and have a request in for NT to do so)
                              • Has font size in points, which most users will expect, instead of pixels
                              • Has font color -- that ought to be part of a font picker IMHO
                              • Has underscore and strikethrough commented out, ready to enable when/if SimpleFont supports them
                              • Allows indicators to get a SimpleFont, since that is what NT uses internally. Translating between points and pixels is best done by the computer, not the human using the GUI

                              I'll take a look at how hard/easy it is to adapt my sample to show the problem. If not too hard I'll get on it.

                              BTW, I do have another recent thread in this list asserting that custom type converters for expandable objects never get called. Do you know anything about that?

                              --EV
                              Last edited by ETFVoyageur; 08-09-2016, 12:26 PM.

                              Comment


                                #30
                                Originally posted by ETFVoyageur View Post
                                I also have my own font picker to replace the SimpleFont GUI. Mine:
                                • Has font size in points, which most users will expect, instead of pixels
                                • Has font color
                                • Allows indicators to get a SimpleFont, since that is what NT uses internally. Translating between points and pixels is best done by the computer, not the human using the GUI
                                I've had no problems (in NT7) using Font properties.
                                What problems were you trying to solve?

                                I mean, for NT7, the attached screenshot shows the free and open source PriceActionSwingPro indicator displaying the standard Font property -- it supports many unit choices for font size, including both Point and Pixel.

                                [Edit: I may have mis-understood. Yes, the expandable property for "Font" in NT7 supports font sizes in points, as well as pixels. I've attached a second screenshot showing all "Unit" choices in the dropdown menu.]

                                But, yeah, in NT7, the expandable Font property does not contain a color, so that has to be a separate property.
                                Attached Files
                                Last edited by bltdavid; 08-10-2016, 09:35 AM.

                                Comment

                                Latest Posts

                                Collapse

                                Topics Statistics Last Post
                                Started by Haiasi, 04-25-2024, 06:53 PM
                                2 responses
                                17 views
                                0 likes
                                Last Post Massinisa  
                                Started by Creamers, Today, 05:32 AM
                                0 responses
                                5 views
                                0 likes
                                Last Post Creamers  
                                Started by Segwin, 05-07-2018, 02:15 PM
                                12 responses
                                1,786 views
                                0 likes
                                Last Post Leafcutter  
                                Started by poplagelu, Today, 05:00 AM
                                0 responses
                                3 views
                                0 likes
                                Last Post poplagelu  
                                Started by fx.practic, 10-15-2013, 12:53 AM
                                5 responses
                                5,408 views
                                0 likes
                                Last Post Bidder
                                by Bidder
                                 
                                Working...
                                X