Standalone application

_ROOT Version: 6.18.4
_Platform: MacOs, Linux
_Compiler: g++

Hi,

I used to be able to create a standalone application that would throw a Canvas. I would be able to draw and redraw histograms in that canvas, which was quite powerful. It would show the histogram growing as it was filling.

The code that I used is essentially what is below. This still works, however, first it complains about multiple applications being created.

Second, I now need to call the Run() method on the application object. This used not to be necessary. I’m not interested in entering ROOTs event loop. I want to display the histogram on the canvas, and once it is modified, I want to redraw the canvas. I’m not interested in user generated events.

I have experimented with this, but it seems that whatever I do to the canvas and the histogram is only shown once Run has been called, and from that moment control has been taken from my main program.

If I remove the application object from my code, the code still works but a canvas window no longer appears.

What’s the best way of dealing with this?

sample code (originally from stackoverflow)

#include <TApplication.h>
#include <TCanvas.h>
#include <TH1D.h>
#include <thread>
#include <chrono>
#include <iostream>

int main ( int argc, char* argv[] )
{
    TApplication app ("app",&argc,argv);

    TCanvas canvas ("fCanvas", "fCanvas", 600, 400);

    TH1D h ("h","h",10,0,10);
    h.Fill(1);
    h.Fill(2);
    h.Fill(2);
    h.Fill(2);
    h.Fill(3);
    h.Fill(3);
    h.Draw();

    canvas.Update();
    canvas.Draw();

    std::cout << "starting the app" << std::endl;
    app.Run();
    std::cout << "run was called" << std::endl;

    std::this_thread::sleep_for( std::chrono::seconds(10) );

    return 0;
}

That’s weird. with the code you posted, this should not happen

Well, when was it not necessary? With which version on which platform? TApplication::Run() is required to process the events (and hence draw graphics and GUI).

In the entire 5.* suite I never called Run(). I was able to conjure up a Canvas and Draw on it. If I wanted to redraw on it I called the Update function on the Canvas.

This worked on Linux, Os and Windows.

I’m quite happy to accept that this may have violated some coding standards, but it did work. Impressively so.

I’m quite happy to do things the proper way, so if you know how I can achieve said functionality, I’d be grateful.

Well, simply create an instance of TApplication and call TApplication::Run(), as in the code you posted. If it doesn’t work, please tell us exactly what you did (how you compiled your code) and what error you got

As I wrote, the main program ends or loses control after calling Run(). In other words, the program and works and shows the Canvas complete with histogram, but then effectively the program is over. To update the histogram whilst retaining the Canvas there needs to be life after Run(). I don’t know how to achieve that.

Sorry, but the code you posted works just fine, and stays up until I select “Quit ROOT” from the Canvas’ “File” menu (at least on Windows)…

I’m not sure I understand what you’re trying to do… You might add gSystem->ProcessEvents(), to process the events outside the main event loop, but that’s not the proper way.
In principle, any fill/update must happen before the Run(). That’s the way it works. Please take a look at the many examples in the test directory

What I’m trying to do is:

  1. Fill some histogram
  2. Launch a canvas
  3. Draw the histogram on a Pad on the Canvas
  4. Update the histogram
  5. Redraw the histogram on the canvas, without restarting the Canvas
  6. Continue with whatever else my program wants to do

This used to be possible.

I used not to have to call Run() in order for my canvas and its contents to be shown. I’m happy to accept this is not how is should have worked and actually is not working this way now. This is what I’m asking; is this what you’re saying?

If you have a concrete running macro, I can try to help you. Just post something which was working before and not working anymore

I already did. Just remove the call to Run.

To add to that. It is not a macro but a standalone C++ program.

More to the point though: I hope I’ve now explained the functionality I’m looking for clearly enough. Can you think of a way to implement this functionality in a standalone C++ program (not a macro)?

If the answer is no, I wouldn’t want to waste more of your time.

Absolutely!

Enlighten me :slight_smile:

Even if I really don’t understand why you don’t want to call TApplication::Run(), which is (and was) the proper way to write a standalone application, here is another (silly) way:

#include <TSystem.h>
#include <TApplication.h>
#include <TCanvas.h>
#include <TH1D.h>
#include <thread>
#include <chrono>
#include <iostream>

int main ( int argc, char* argv[] )
{
    TApplication app ("app",&argc,argv);

    TCanvas *canvas = new TCanvas("fCanvas", "fCanvas", 600, 400);

    TH1D h ("h","h",10,0,10);
    h.Fill(1);
    h.Fill(2);
    h.Fill(2);
    h.Fill(2);
    h.Fill(3);
    h.Fill(3);
    h.Draw();

    canvas->Update();
    canvas->Draw();
    gSystem->ProcessEvents();
    std::cout << "starting the app" << std::endl;
    std::cout << "run was not called" << std::endl;
    gSystem->ProcessEvents();
    std::this_thread::sleep_for( std::chrono::seconds(10) );
    return 0;
}

Ah, but your silly method works! Below an example of the effect I try to achieve (and now observe). Apologies, I realise now I had not given you a fully worked example …

It is not clear to me how I can achieve a similar effect with the Run() method. If I put it at the very end it only gives me the end result. If I use in the first cycle after filling the histogram it gives me the intermediate result and then blocks. I’m happy to be educated on how to use Run properly, but atm I don’t see how I could apply it.

This code still gives me:
Error in TApplication::TApplication: only one instance of TApplication allowed

But that doesn’t seem to affect the overall running of the program. Is there a global Application object that I somehow import through an include?

include <TSystem.h>

#include <TApplication.h>
#include <TCanvas.h>
#include <TH1D.h>
#include <thread>
#include <chrono>
#include <iostream>

int main ( int argc, char* argv[] )

{
    TApplication app ("app",&argc,argv);
    TCanvas *canvas = new TCanvas("fCanvas", "fCanvas", 600, 400);
    TH1D h ("h","h",10,0,10);

    h.Fill(1);
    h.Fill(2);
    h.Fill(2);
    h.Fill(2);
    h.Fill(3);
    h.Fill(3);

    h.Draw();
    canvas->Update();
    canvas->Draw();


    gSystem->ProcessEvents();

    std::this_thread::sleep_for( std::chrono::seconds(3) );

    h.Fill(4);
    h.Fill(5);
    h.Fill(5);
    h.Fill(6);
    h.Fill(6);
    h.Fill(6);

    h.Draw();
    canvas->Update();
    canvas->Draw();


    gSystem->ProcessEvents();

    std::this_thread::sleep_for( std::chrono::seconds(3) );

    return 0;

}
#include <TSystem.h>
#include <TApplication.h>
#include <TCanvas.h>
#include <TH1D.h>
#include <thread>
#include <chrono>
#include <iostream>

// cl -nologo -Od -Z7 -MD -GR -EHsc -I%ROOTSYS%\include test_run2.cxx /link -LIBPATH:%ROOTSYS%\lib libCore.lib libGui.lib libHist.lib libGpad.lib /out:test_run2.exe

int main ( int argc, char* argv[] )
{
    TApplication app ("app",&argc,argv);
    TCanvas *canvas = new TCanvas("fCanvas", "fCanvas", 600, 400);
    TH1D h ("h","h",10,0,10);

    h.Fill(1);
    h.Fill(2);
    h.Fill(2);
    h.Fill(2);
    h.Fill(3);
    h.Fill(3);

    h.Draw();

    canvas->Modified();
    canvas->Update();

    canvas->WaitPrimitive();

    h.Fill(4);
    h.Fill(5);
    h.Fill(5);
    h.Fill(6);
    h.Fill(6);
    h.Fill(6);

    canvas->Modified();
    canvas->Update();

    app.Run();
    return 0;
}

Here, canvas->WaitPrimitive(); will wait for double-click inside the canvas without blocking the event loop. This is the easiest solution (there are more). I also modified your code and removed unnecessary Draw() calls. Please try and let me know.
And I don’t know why you have the Error in TApplication::TApplication: only one instance of TApplication allowed error message…

1 Like

WaitPrimitive is also not what I’m looking for because it requires user interaction. My use case is a running simulation of a density function that is being updated. At set moments in time I want to show what the function looks like, so that a user can observe the evolution of the density function. Having to click would be nuisance. I realise that visualisation can be done offline in a macro, but this way of doing things have proved invaluable in debugging the simulation code as we developed the method.

You ProcessEvent() approach seems to be doing what I need, many thanks. Unless you see a fundamental problem with it (are there drawbacks?), I will probably stick with it.

I really appreciate the effort you put into this.

What about something like this:

int main ( int argc, char* argv[] )
{
    TApplication app ("app",&argc,argv);
    TCanvas *canvas = new TCanvas("fCanvas", "fCanvas", 600, 400);
    TH1D h ("h","h",10,0,10);
    h.Fill(1);
    h.Draw();
    canvas->Modified();
    canvas->Update();
    while (!gSystem->ProcessEvents()) {
       h.Fill(10 * gRandom->Rndm());
       canvas->Modified();
       canvas->Update();
       gSystem->Sleep(100);
    } 
    app.Run();
    return 0;
}

Obviously, in the while loop you can replace the random value by the density function update…
As I said, there are always several solutions to a given problem :wink:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.