Accessing canvas from file

Hello,

my problem is following. I have root file with histogram and canvas where histogram is draw. I open root file with “UPDATE” option to fit histogram and redraw it on canvas. I have no problem with getting histogram, fiting it and saving back to file. Problem is with canvas. After getting it from file, object exists, I can even print list of primitives from it, but when I want to get access to pad to change color then my app crash: segmentation violation.

Here is what i see (I can reproduce this phenomena in CINT). After using Get on gDirectory my canvas exists but pointer to subpad is NULL. List of Primitives shows only histogram. But after using Draw on canvas pointer to subpad is not NULL and list of primitives is bigger.

root [0] Attaching file Export/output.root as _file0... root [1] TCanvas * can = (TCanvas *)gDirectory->Get("c_InvMass") root [2] can->cd() (class TVirtualPad*)0x0 root [3] can->GetListOfPrimitives()->Print() Collection name='TList', class='TList', size=1 TH1.Print Name = h_InvMass, Entries= 1991107, Total sum= 1.00072e+06 root [4] can->Draw() root [5] can->cd() (class TVirtualPad*)0x12acbc0 root [6] can->GetListOfPrimitives()->Print() Collection name='TList', class='TList', size=3 TFrame X1=1070.000000 Y1=0.000000 X2=1200.000000 Y2=40482.750000 FillColor=600 FillStyle=1001 TH1.Print Name = h_InvMass, Entries= 1991107, Total sum= 1.00072e+06 TPaveText X1=1115.216856 Y1=42506.888032 X2=1154.783144 Y2=45290.077177 Collection name='TList', class='TList', size=1 Text X=0.000000 Y=0.000000 Text=InvMass A&B Font=0 Size=0.000000 Color=0 Align=0

As far as I understand after using Get the object is not fully fetched from disk, right? Is there any way to avoid calling dummy Draw (my app doesn’t draw any histograms) and get full object to memory?

Greetings,
Rafal

Usually when you save a canvas in a histogram it is to Draw or to print it later, not to store the data within the canvas.

Of course, I don’t use canvas to store data which are used for further analysis, but I use it to prepare view of the final plot. Sometimes I would like script to modify content of the canvas, for example to add extra labels or numbers after fitting.

If I do the following I do not need and dummy Draw():

root [0] TFile *f = new TFile("c1.root")
(class TFile *) 0x7fc71a36d630
root [1] TCanvas *c = (TCanvas *)f->Get("c1");
root [2] c->GetListOfPrimitives()->Print()
Collection name='TList', class='TList', size=3
 TFrame  X1=-4.000000 Y1=0.000000 X2=4.000000 Y2=855.750000 FillStyle=1001
 TH1.Print Name  = hpx, Entries= 25000, Total sum= 25000
 TPaveText  X1=-2.370115 Y1=884.097861 X2=2.370115 Y2=957.370325
 Collection name='TList', class='TList', size=1
  Text  X=0.000000 Y=0.000000 Text=This is the px distribution Font=0 Size=0.000000 Color=0 Align=0
root [3] 

For me doesn’t. Can you also please show me what is the result of c->cd(0) executed after c->GetListOfPrimitives()->Print()?

root [3] c->cd(0)
(class TVirtualPad *) 0x0

So it also doesn’t work for you, you have NULL pointer as a result.

I guess you can use gPad:

root [3] c->cd()
(class TVirtualPad *) 0x0
root [6] gPad
(class TVirtualPad *) 0x7fabd4e01700
root [7] c->Draw()
root [8] c->cd()
(class TVirtualPad *) 0x7fabd4e01700

This is some kind of solution, but from other side I though that gPad always shows the latest selected pad/canvas (so it is a result of canvas selection), so why gPad works whereas can->cd(0) doesn’t?

cd() works only when on drawn canvases.
You can also draw the canvas in batch mode so it does not display and you can use cd afterwards.

Well, I am loosing any hope that there is a way to extract canvas from root file in a normal way, and I don’t understand why there is no problem with histograms but there must be with canvas. Here is simple program which I am writing. It works easy - open any root file and export every canvas to PNG file. Well, It should but it doesn’t work. Can someone help?

[code]#include <getopt.h>
#include
#include

#include <TCanvas.h>
#include <TFile.h>
#include <TKey.h>
#include <TROOT.h>

#include <TImage.h>
#include <TString.h>

#include
#define PR(x) std::cout << “++DEBUG: " << #x << " = |” << x << “| (” << FILE << ", " << LINE << “)\n”;

int flag_png;
int flag_eps;
int flag_pdf;

std::string outpath = “./”;

bool extractor(const std::string & file) {
TFile * f = TFile::Open(file.c_str(), “READ”);
if (!f->IsOpen()) {
std::cerr << “File " << file << " not open!” << std::endl;
return false;
}

TKey * key;
TIter nextkey(f->GetListOfKeys());

while ((key = (TKey*)nextkey())) {
	const char * classname = key->GetClassName();
	TClass *cl = gROOT->GetClass(classname);
	if (!cl) continue;

	TObject *obj = key->ReadObj();

	if (obj->InheritsFrom("TCanvas")) {
		TCanvas * can = (TCanvas*)obj;
		PR(can->GetName());
		PR(can);

		can->Draw();

		TImage * img = TImage::Create();
		img->FromPad(can);
		TString filename = outpath + TString::Format("%s.png", can->GetName());
		img->WriteImage(filename);
		delete img;
	}
}

return true;

}

int main(int argc, char ** argv) {
struct option lopt[] =
{
{“png”, no_argument, &flag_png, 1},
{“eps”, no_argument, &flag_eps, 1},
{“pdf”, no_argument, &flag_pdf, 1},
{“dir”, required_argument, 0, ‘d’},
{ 0, 0, 0, 0 }
};

Int_t c = 0;
while (1) {
	int option_index = 0;

	c = getopt_long(argc, argv, "d:", lopt, &option_index);
	if (c == -1)
		break;

	switch (c) {
		case 0:
			if (lopt[option_index].flag != 0)
				break;
			printf ("option %s", lopt[option_index].name);
			if (optarg)
				printf (" with arg %s", optarg);
			printf ("\n");
			break;
		case 'd':
			outpath = optarg;
			break;
		case 'h':
			exit(EXIT_SUCCESS);
			break;
		case '?':
			break;
		default:
			break;
	}
}

while (optind < argc) {
	extractor(argv[optind++]);
}

return 0;

}[/code]

Well, after “can->Draw();” try to add “can->Modified(); can->Update();” (just to make sure that it is really “physically” drawn).
Also, it seems to me that the “TImage” is just an abstract interface and you should actually use the “TASImage” class instead.

Thanks for hint with TASImage but retrieving TCanvas still doesn’t work.

This is the code which works for me. Of someone has better solution please share.

[code]#include <getopt.h>
#include
#include

#include <TCanvas.h>
#include <TFile.h>
#include <TKey.h>
#include <TROOT.h>

#include <TString.h>

#include <RootTools.h>

int flag_png;
int flag_eps;
int flag_pdf;

std::string outpath = “./”;

TDirectory * target;

bool extractor(const std::string & file)
{
TFile * f = TFile::Open(file.c_str(), “READ”);
if (!f->IsOpen()) {
std::cerr << “File " << file << " not open!” << std::endl;
return false;
}

    target->cd();

    TKey * key;
    TIter nextkey(f->GetListOfKeys());
    while ((key = (TKey*)nextkey())) {

            TObject *obj = key->ReadObj();
            if (obj->InheritsFrom("TCanvas")) {
                    TCanvas * can = (TCanvas*)obj;

                    can->Clone(can->GetName());

                    if (flag_png) RootTools::ExportPNG(can, outpath);
                    if (flag_eps) RootTools::ExportEPS(can, outpath);
                    if (flag_pdf) RootTools::ExportPDF(can, outpath);
            }
    }
    return true;

}

int main(int argc, char ** argv)
{
struct option lopt[] =
{
{“png”, no_argument, &flag_png, 1},
{“eps”, no_argument, &flag_eps, 1},
{“pdf”, no_argument, &flag_pdf, 1},
{“dir”, required_argument, 0, ‘d’},
{ 0, 0, 0, 0 }
};

    Int_t c = 0;
    while (1) {
        int option_index = 0;

        c = getopt_long(argc, argv, "d:", lopt, &option_index);
        if (c == -1)
                break;

        switch (c) {
            case 0:
                if (lopt[option_index].flag != 0)
                        break;
                printf ("option %s", lopt[option_index].name);
                if (optarg)
                        printf (" with arg %s", optarg);
                printf ("\n");
                break;
            case 'd':
                outpath = optarg;
                break;
            default:
                break;
        }
 }

target = gDirectory;

while (optind < argc) {
    extractor(argv[optind++]);
}

return 0;

}[/code]