Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Multithreading errors with Draw.Text

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

    Multithreading errors with Draw.Text

    Hi there,

    I'm having a problem trying to render text on a blueprint chart. Here's what I'm using:

    Code:
    public class Ver14 : Strategy
        {​
    
    //  Define custom fonts for use in this section
                NinjaTrader.Gui.Tools.SimpleFont Arial_7pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 7);
                NinjaTrader.Gui.Tools.SimpleFont Arial_8pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 8);
                NinjaTrader.Gui.Tools.SimpleFont Arial_9pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 9);
                NinjaTrader.Gui.Tools.SimpleFont Arial_10pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 10);
                NinjaTrader.Gui.Tools.SimpleFont Arial_12pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 12);
                NinjaTrader.Gui.Tools.SimpleFont Wingdings2_12pt = new NinjaTrader.Gui.Tools.SimpleFont("Wingdings 2", 12);
                NinjaTrader.Gui.Tools.SimpleFont Wingdings2_16pt = new NinjaTrader.Gui.Tools.SimpleFont("Wingdings 2", 16);
    
                // Freeze the colors we want to use
                Brush expensive_green = new SolidColorBrush(Color.FromArgb(0, 0, 100, 0));
                Brush bargain_green = new SolidColorBrush(Color.FromArgb(0, 0, 128, 0));
                Brush delta_green = new SolidColorBrush(Color.FromArgb(0, 203, 236, 181));
                Brush expensive_red = new SolidColorBrush(Color.FromArgb(0, 177, 14, 20));
                Brush bargain_red = new SolidColorBrush(Color.FromArgb(0, 221, 17, 25));
                Brush delta_red = new SolidColorBrush(Color.FromArgb(0, 255, 204, 203));​
    
    protected override void OnBarUpdate()
            {​
    
    //  Freeze the brushes we will be using in the script
                    expensive_green.Freeze();
                    bargain_green.Freeze();
                    delta_green.Freeze();
                    expensive_red.Freeze();
                    bargain_red.Freeze();
                    delta_red.Freeze();​
    
    
    }  //  end of on bar update
    
    private void chart_print_indicatorIcons()
            {      
            //  Green/Expensive
                if (delta_momentum_meter == 5 && price_momentum_meter == -5)
                {
                    chart_counter++;        //  Increase the chart counter
    
                    Draw.Text(this, chart_counter.ToString(), true, "q 444", 1,            //  Show a dark green arrow with three dollar signs to signal expensive buys    
                        Close[0], 1, expensive_green,                
                        Wingdings2_12pt, TextAlignment.Center,
                        Brushes.Transparent, Brushes.Transparent, 0);
    
                }
    
            //  Red/Expensive
                else if (delta_momentum_meter == -5 && price_momentum_meter == -5)
                {
                    chart_counter++;        //  Increase the chart counter
    
                    Draw.Text(this, chart_counter.ToString(), true, "444 w", 1,            //  Show a dark red arrow with three dollar signs to signal expensive sells    
                        Close[0], 1, expensive_red,                
                        Wingdings2_12pt, TextAlignment.Center,
                        Brushes.Transparent, Brushes.Transparent, 0);
    
                }​
    
    (etc.)
    ----

    I generate the following error on running the script:​
    Error on calling 'OnBarUpdate' method on bar 10: The calling thread cannot access this object because a different thread owns it.

    For reference, "chart_print_indicatorIcons()" is called whenever there's a particular event in the code that gets triggered.

    Also, I'm not sure where to place a Dispatcher.ASync reference in there, but please help me out if you could.

    It's weird because I never encountered this in NT7.

    Thanks in advance!


    #2
    My guess is that you need call chart_print_indicatorIcons() using TriggerCustomEvent.

    Comment


      #3
      You didn't encounter it in NT7 because it wasn't multithreaded in this way in NT7. You don't need to freeze fonts, but you need to freeze brushes. You should freeze them immediately after you created them, not later in OnBarUpdate. It has to be done before you get to OnRender or OnBarUpdate - if you're creating them in OnStateChange you should freeze them right there before it exits that method. If you're creating them at the top, you have a problem, because it could run OnRender before they get frozen and that's in a different thread.

      I would suggest rather than instantiating the brushes there at the top, you just declare the variables and don't set a value or set them to null, and then in OnStateChange under State.Configure or State.DataLoaded, set them equal to the new SolidColorBrush(whatever) and then freeze them. Don't do it at the top, and don't fail to freeze them before you leave OnStateChange. If you do these things, it should work without this failure.
      Last edited by QuantKey_Bruce; 05-16-2023, 05:32 AM.
      Bruce DeVault
      QuantKey Trading Vendor Services
      NinjaTrader Ecosystem Vendor - QuantKey

      Comment


        #4
        Originally posted by QuantKey_Bruce View Post
        You didn't encounter it in NT7 because it wasn't multithreaded in this way in NT7. You don't need to freeze fonts, but you need to freeze brushes. You should freeze them immediately after you created them, not later in OnBarUpdate. It has to be done before you get to OnRender or OnBarUpdate - if you're creating them in OnStateChange you should freeze them right there before it exits that method. If you're creating them at the top, you have a problem, because it could run OnRender before they get frozen and that's in a different thread.

        I would suggest rather than instantiating the brushes there at the top, you just declare the variables and don't set a value or set them to null, and then in OnStateChange under State.Configure or State.DataLoaded, set them equal to the new SolidColorBrush(whatever) and then freeze them. Don't do it at the top, and don't fail to freeze them before you leave OnStateChange. If you do these things, it should work without this failure.
        Hi Bruce!

        That worked! But now I have a different problem. All of the text is showing up is white. It's not colored when using Color.FromArgb (or Color.FromRgb)
        It DOES work when I just use regular brush colors (Brushes.DarkRed), but not RGB)

        Here's what I have:

        (before onBarUpdate and onStateChange:
        Code:
        //  Define custom fonts for use in this section
                    NinjaTrader.Gui.Tools.SimpleFont Arial_7pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 7);
                    NinjaTrader.Gui.Tools.SimpleFont Arial_8pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 8);
                    NinjaTrader.Gui.Tools.SimpleFont Arial_9pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 9);
                    NinjaTrader.Gui.Tools.SimpleFont Arial_10pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 10);
                    NinjaTrader.Gui.Tools.SimpleFont Arial_12pt = new NinjaTrader.Gui.Tools.SimpleFont("Arial", 12);
                    NinjaTrader.Gui.Tools.SimpleFont Wingdings2_12pt = new NinjaTrader.Gui.Tools.SimpleFont("Wingdings 2", 12);
                    NinjaTrader.Gui.Tools.SimpleFont Wingdings3_16pt = new NinjaTrader.Gui.Tools.SimpleFont("Wingdings 3", 16);
        
                    Brush expensive_green = null;
                    Brush bargain_green = null;
                    Brush delta_green = null;
                    Brush expensive_red = null;
                    Brush bargain_red = null;
                    Brush delta_red = null;​
        On state change in DataLoaded (also tried State.Configure)

        Code:
         if (State == State.DataLoaded)  
                        {
                            // Set the colors we want to use for our brushes
                            Brush expensive_green = new SolidColorBrush(Color.FromRgb(0, 100, 0));
                            Brush bargain_green = new SolidColorBrush(Color.FromRgb(0, 128, 0));
                            Brush delta_green = new SolidColorBrush(Color.FromRgb(203, 236, 181));
                            Brush expensive_red = new SolidColorBrush(Color.FromRgb(177, 14, 20));
                            Brush bargain_red = new SolidColorBrush(Color.FromRgb(221, 17, 25));
                            Brush delta_red = new SolidColorBrush(Color.FromRgb(255, 204, 203));
        
                            //  Freeze these brushes
                            expensive_green.Freeze();
                            bargain_green.Freeze();
                            delta_green.Freeze();
                            expensive_red.Freeze();
                            bargain_red.Freeze();
                            delta_red.Freeze();
                        }​
        in OnBarUpdate, where a method is called to display text:

        Code:
        private void chart_print_indicatorIcons()
                {      
                //  Green/Expensive
                    if (delta_momentum_meter == 5 && price_momentum_meter == -5)
                    {
                        chart_counter++;        //  Increase the chart counter
        
                        Draw.Text(this, chart_counter.ToString(), true, "Green/Expensive", 1,            //  Show a dark green arrow with three dollar signs to signal expensive buys    
                            Close[0], 1, expensive_green,                
                            Arial_12pt, TextAlignment.Center,
                            Brushes.Transparent, Brushes.Transparent, 0);
        
                    }
        
                //  Red/Expensive
                    else if (delta_momentum_meter == -5 && price_momentum_meter == -5)
                    {
                        chart_counter++;        //  Increase the chart counter
        
                        Draw.Text(this, chart_counter.ToString(), true, "Red/Expensive", 1,            //  Show a dark red arrow with three dollar signs to signal expensive sells    
                            Close[0], 1, expensive_red,                
                            Arial_12pt, TextAlignment.Center,
                            Brushes.Transparent, Brushes.Transparent, 0);​
        
        (etc.)
        Attachment shows the white text to the left of the blueprint chart prints.
        Attached Files
        Last edited by Spiderbird; 05-16-2023, 02:10 PM. Reason: Edited: Added more context

        Comment


          #5
          Hi Spiderbird, you are defining your brushes within OnStateChanged within State.DataLoaded, so the scope is limited to only OnStateChanged. You will need to move the brush variable decalration to the class level and initialize it in State.DataLoaded, then freeze it.

          Code:
          //Class level
          Brush expensive_green;
          
          //in OnStateChanged, DataLoaded:
          expensive_green = new SolidColorBrush(Color.FromRgb(0, 100, 0));
          expensive_green.Freeze();
          Chris L.NinjaTrader Customer Service

          Comment


            #6
            Yes, just remove the word Brush from those lines so it's not overloading the class field expensive_green with a local one which is then disposed at the end of the method scope.
            Bruce DeVault
            QuantKey Trading Vendor Services
            NinjaTrader Ecosystem Vendor - QuantKey

            Comment


              #7
              Originally posted by NinjaTrader_ChrisL View Post
              Hi Spiderbird, you are defining your brushes within OnStateChanged within State.DataLoaded, so the scope is limited to only OnStateChanged. You will need to move the brush variable decalration to the class level and initialize it in State.DataLoaded, then freeze it.

              Code:
              //Class level
              Brush expensive_green;
              
              //in OnStateChanged, DataLoaded:
              expensive_green = new SolidColorBrush(Color.FromRgb(0, 100, 0));
              expensive_green.Freeze();

              That did the trick! I feel a bit dumb for putting Brush in there twice without realizing it.
              Thanks for your timely help, and I hope you both are doing well.

              Comment

              Latest Posts

              Collapse

              Topics Statistics Last Post
              Started by Balage0922, Today, 07:38 AM
              0 responses
              1 view
              0 likes
              Last Post Balage0922  
              Started by JoMoon2024, Today, 06:56 AM
              0 responses
              6 views
              0 likes
              Last Post JoMoon2024  
              Started by Haiasi, 04-25-2024, 06:53 PM
              2 responses
              19 views
              0 likes
              Last Post Massinisa  
              Started by Creamers, Today, 05:32 AM
              0 responses
              6 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  
              Working...
              X