Integrating ROOT libraries into Cocoa program on Mac OS X

Ok, so I’m hoping to be able to do this, but I’m not sure if it can be done. I want to use the ROOT libraries to create and display histograms and other graphs inside of a Cocoa based program. I’ve got ROOT installed, and have successfully compiled and run several simple programs that use the libraries, but whenever I call the Draw() command on a histogram, the console displays a message along the lines of “TCanvas::MakeDefCanvas: created default TCanvas with name c1”, and no graphics are displayed. For reference, here is the code that generates that message:

[code]#include

using namespace std;

int hsimple();

#ifndef CINT
#include “TFile.h”
#include “TH1.h”
#include “TH2.h”
#include “TProfile.h”
#include “TNtuple.h”
#include “TRandom.h”

//______________________________________________________________________________
int main()
{
return hsimple();
}
#endif

int hsimple()
{
// Create a new ROOT binary machine independent file.
// Note that this file may contain any kind of ROOT objects, histograms,
// pictures, graphics objects, detector geometries, tracks, events, etc…
// This file is now becoming the current directory.
TFile hfile(“hsimple.root”,“RECREATE”,“Demo ROOT file with histograms”);

// Create some histograms, a profile histogram and an ntuple
TH1F *hpx   = new TH1F("hpx","This is the px distribution",100,-4,4);
TH2F *hpxpy = new TH2F("hpxpy","py vs px",40,-4,4,40,-4,4);
TProfile *hprof = new TProfile("hprof","Profile of pz versus px",100,-4,4,0,20);
TNtuple *ntuple = new TNtuple("ntuple","Demo ntuple","px:py:pz:random:i");

// Fill histograms randomly
Float_t px, py, pz;
for ( Int_t i=0; i<10000; i++) {
	gRandom->Rannor(px,py); //px and py will be two gaussian random numbers
	pz = px*px + py*py;
	Float_t random = gRandom->Rndm(1);
	hpx->Fill(px);
	hpxpy->Fill(px,py);
	hprof->Fill(px,pz);
	ntuple->Fill(px,py,pz,random,i);
}
int bob;
hpx->Draw();
cin >> bob;
// Save all objects in this file
hfile.Write();

// Close the file. Note that this is automatically done when you leave
// the application.
hfile.Close();

return 0;

}
[/code]

Now, I presume that I’m doing something wrong, or missing a step somewhere, so what would be really great is if someone could provide a small sample program that will actually generate graphics. If that’s even possible.

My ultimate goal is to be able to display the graph inside a regular Cocoa view of some sort, rather than in an X11 window. Again, not sure if this is even possible.

Thanks,

Paul

Because you open TFile as an object in the stack, it is destroyed when your function returns as well as all objects associated to this file. If you start with ROOT, I suggest to create all your objects in the heap
TFile *file = new TFile(…

and not close the file at the end of your script. Or, even simpler do not create a TFile and remove teh statements like file.Write and file.Close

Rene

Well, the TFile portion of the code is really rather irrelevant, isn’t it? Perhaps a clearer example might be:

[code]#include
#include “TH1D.h”

using namespace std;

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

TH1D* myhisto=new TH1D("myhisto","myhisto",10,0,10);
myhisto->Fill(3);
myhisto->Draw();
cout<<"Program Finished"<< endl;
return 0;

}

[/code]

This does the same thing: Namely, nothing. :laughing: The example I gave in the first post was just one of the test programs. The point is that when I call Draw(), I get the “TCanvas::MakeDefCanvas: created default TCanvas with name c1” message, and do NOT get any actual graphics.

Thanks,

Paul

Please do not use your main program if you are starting with ROOT. We are spending an incredible amount of time explaining this. Simply execute your script under ROOT.
If you have a file hsimple.C, do
root >.x hsimple.C (your script is interpreted by CINT)
root > .x hsimple.C+ (your script is compiled by the standard compiler (eg gcc) and executed with the
maximum performance)
(see first pages of the Users Guide)

If you insist having a main program (nothing justifies it!) have a look at the ROOT main program in $ROOTSYS/main/rmain.cxx

Rene

I’m sorry, I think I haven’t made my intentions clear. I do NOT want to run scripts inside of the the ROOT environment, I want to incorporate root libraries into my own external program, compiled in Xcode. The examples I show above are intended to be compiled either from the terminal on OS X, or in Xcode, and are meant to illustrate that I don’t know how to make graphics appear. In this context, a main() is required for compilation by the gcc compiler. I’m not sure that we’re on the same page here, but I will take a look at the rmain.cxx.

The program I am writing is a simulator for the HELIOS project at Argonne, and I need to present several graphs inside it. I need graphing libraries to do this, and I hope that I can use the ROOT libraries for it.

Paul

If you want to see the graphics and interact with the canvas, you need a class controlling the event loop like TApplication or TRint. I have modified your example below to use a TApplication

Rene

[code]#include
#include “TH1D.h”
#include “TApplication.h”

using namespace std;

int main( int argc, char** argv) {
TApplication app(“fctest”, &argc, argv);

TH1D* myhisto=new TH1D(“myhisto”,“myhisto”,10,0,10);
myhisto->Fill(3);
myhisto->Draw();
cout<<“Program Finished”<< endl;
app.Run();
return 0;
}[/code]

Ah, excellent! That solves half of the problem. The second half is trickier though: Is it possible to get the graph to show up in an NSView or similar object in the Cocoa framework?

Paul

And another question: The .so library files seem to be giving Xcode trouble when I use them in an Objective-C project. Are they anywhere available as .cxx, .cpp, and .c files (in one nice chunk), or is it possible to compile root so that they are consolidated as such? Or is .so the only way to access them?

Thanks,

Paul

Hi Paul,

currently the ROOT graphics use X11 windows to draw into. I’ve been looking into it but it does not seem to be possible to redirect the rendering of an X11 window into an NSView window. For that we would need native rendering in Cocoa or via GL into Cocoa, which is almost there (being worked on).

Considering the issue of using ROOT in Xcode, it will be simplest to make an ROOT.framework which contains all ROOT .so’s and includes and can be imported in Xcode as a single unit. I’ll look into how to do that (but if you can sort this out I am happy to take the recipe).

Cheers, Fons.

Ok, I’ve got this thing mostly working. main.mm creates the NSApplicationMain and starts the event loop:

//
//  main.m

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}

Next, my nib has a window with a button. the button is linked to the method callBob:

//
//  Caller.h

#import <Cocoa/Cocoa.h>

@interface Caller : NSObject {}
- (IBAction)callBob:(id)sender;
@end
//
//  Caller.m

#import "Caller.h"
#import "RootGunk.h"

@implementation Caller

- (IBAction)callBob:(id)sender
{
	bob();
}
@end

callBob calls bob, which is in RootGunk.h and RootGunk.cpp. I had to use an extern “C++” thing to keep Xcode from freaking out about ROOT’s Class class:

/*
 *  RootGunk.h
 */

extern "C++"{
	
int bob();

}
/*
 *  RootGunk.cpp
 */

extern "C++" {
	
#import "RootGunk.h"
#import <iostream>
#import "TH1D.h"
#import "TApplication.h"
	
	using namespace std;
	
	int bob() {
		int argc=0;
		char** argv;
		TApplication app("fctest", &argc, argv);
		TH1D* myhisto=new TH1D("myhisto","myhisto",10,0,10);
		myhisto->Fill(3);
		myhisto->Fill(3);
		myhisto->Fill(3);
		myhisto->Fill(5);
		
		myhisto->Draw();
		cout<<"Program Finished"<< endl;
		app.Run();
		return 0;
	}
}

So now, when I click the button, it starts up X11 and I get my histogram. The remaining issue is that control is stuck in the app.Run() function call, and so the cocoa side of the app gets stuck with the spinning beach ball. Further, when I select Quit Root from the X11 file menu, it kills the whole application without ever coming back to the return 0;.

This is where my knowledge of cocoa hits it’s limit, and my lack of knowledge of root shines becomes obvious. Is there any way to fix these two problems?

Thanks,

Paul

Hi,

You should not call app.Run(); at all, but create a timer to process events via “gSystem->ProcessEvents();” every xx milliseconds. You can also take a look at “Embedding ROOT in External Applications” in these release notes

And to tell the application to return from run, just call “app.SetReturnFromRun(true);

Cheers, Bertrand.

I’ll give the loop a try once I figure out how to multithread the ‘right’ way under os x (pthreads is to be shunned in favor of the new Grand Central Dispatch, apparently). In the mean time, app.SetReturnFromRun(true); doesn’t seem to be working. When I select ‘Quit ROOT’ it still kills the whole application, and never returns (I have a breakpoint set right after the Run() call, so I can tell that it’s not getting there). I tried selecting ‘close canvas’, and even just closing out the X11 window that the histogram shows up in, but doing either of those just leaves the whole program doing nothing (so I guess the TApplication object is still in run mode, even though there are no windows for it to display to).

Any advice?

Thanks,

Paul

Well, I guess it is due to the app.Run() call. You will have the problem as long as you call this method.
I got all this working on Windows with several external applications/frameworks, but never tried on Mac OS.
And anyway, integrating different main applications (e.g. NSApplicationMain and TApplication) and different event loops (e.g. app.Run()) is very tricky, and maybe it is simply not working on Mac OS…

Cheers, Bertrand.

[quote=“rdm”]Hi Paul,

currently the ROOT graphics use X11 windows to draw into. I’ve been looking into it but it does not seem to be possible to redirect the rendering of an X11 window into an NSView window. For that we would need native rendering in Cocoa or via GL into Cocoa, which is almost there (being worked on).

Considering the issue of using ROOT in Xcode, it will be simplest to make an ROOT.framework which contains all ROOT .so’s and includes and can be imported in Xcode as a single unit. I’ll look into how to do that (but if you can sort this out I am happy to take the recipe).

Cheers, Fons.[/quote]

I’m looking through Apple’s framework documentation, and if I can figure out how to do it I’ll definitely let you know. It would definitely be nice to have a small sample project to go with it too, that people like myself can learn from, so if I get it all worked out, I’ll try and put one together and upload it.

thanks,

Paul

[quote=“bellenot”]Well, I guess it is due to the app.Run() call. You will have the problem as long as you call this method.
I got all this working on Windows with several external applications/frameworks, but never tried on Mac OS.
And anyway, integrating different main applications (e.g. NSApplicationMain and TApplication) and different event loops (e.g. app.Run()) is very tricky, and maybe it is simply not working on Mac OS…

Cheers, Bertrand.[/quote]

Ok, well, I’ll see what happens when I put it in its own thread and do the loop instead.

Thanks,

Paul

You may try ROOT with Qt plugin. If you compile Qt with Cocoa support then ROOT will use it.
See root.bnl.gov/QtRoot/How2Install4Unix.html

See twiki.cern.ch/twiki/bin/view/At … i#Mac_OS_X as well.

[quote=“fine”]You may try ROOT with Qt plugin. If you compile Qt with Cocoa support then ROOT will use it.
See root.bnl.gov/QtRoot/How2Install4Unix.html

See twiki.cern.ch/twiki/bin/view/At … i#Mac_OS_X as well.[/quote]

I’m not sure yet, but this smells like it may be exactly what I’m looking for. :slight_smile:

Thanks,

Paul

[quote=“fine”]You may try ROOT with Qt plugin. If you compile Qt with Cocoa support then ROOT will use it.
See root.bnl.gov/QtRoot/How2Install4Unix.html

See twiki.cern.ch/twiki/bin/view/At … i#Mac_OS_X as well.[/quote]

Ok, after about 4 days of trying, I’ve ALMOST got QtRoot installed and working. When I run the install script as it’s provided from bnl, everything installs, and I can run demos.C in the tutorials folder of the root directory. However, when I modify the script to add the -cocoa flag to the QtRoot installation, I run into problems. Trying to run demos.C results in:

root [0] .x demos.C
Qt internal error: qt_menu.nib could not be loaded. The .nib file should be placed in QtGui.framework/Versions/Current/Resources/  or in the resources directory of your application bundle.

Now this seems to be a known problem, and is discussed here: http://bugreports.qt.nokia.com/browse/QTBUG-5952. They suggest adding the qt_menu.nib manually to the application bundle. However, root.exe is NOT a bundle, so I’m not sure where to put the file.

So anyone have any ideas? Is there another solution that will fix the whole problem?

Thanks,

Paul

Thank you very much for your time.[quote=“TraxusIV”]
When I run the install script as it’s provided from bnl, everything installs, and I can run demos.C in the tutorials folder of the root directory.
[/quote]Thank you very much for the good news.[quote=“TraxusIV”]
However, when I modify the script to add the -cocoa flag to the QtRoot installation, I run into problems. Trying to run demos.C results in:

root [0] .x demos.C Qt internal error: qt_menu.nib could not be loaded. The .nib file should be placed in QtGui.framework/Versions/Current/Resources/ or in the resources directory of your application bundle. [/quote]
I have to beg a pardon I have no access to Mac machine to provide you any direct advice. I’ve forwarded your question to QtRoot mail list (see lists.bnl.gov/pipermail/qt-root … 00522.html ) and hope QtRoot/Mac experts will share their experience soon.[quote=“TraxusIV”]So anyone have any ideas? Is there another solution that will fix the whole problem?[/quote]On the other hands, I do not understand what you expected to see with "-cocoa " flag. The ROOT main is not GUI it is the console application “by design”. Did you try to build “qtExamples” ? Some of them are GUI applications. It should behave properly.

Hi Paul.

I’d be interested in seeing your success with this project. It would be pretty cool to do this. I haven’t even attempted to do this yet as I’m just now learning my way around Cocoa.
However, I had a thought yesterday that might work - although it is clearly not idea.

What if you used TCanvas::Print to generate an image file (png, eps, pdf, jpg, … ??) and then just tell the Cocoa part to load that image onto you GUI. As I said, its clearly not ideal since you have to write/read to disk. But… if you can’t get QtRoot working. Clearly, the performance of your program will depend on how often you need to redraw your image.

Adam