The Problem
I want to start dynamically passing a TCanvas from essentially the "Model" part of my program to the "View" part. The way I thought about doing this, was to simply create my TCanvas on the View at startup, then update this TCanvas with the View TCanvas once the graphs had been filled. I created a test bench to see if it would work.
I've displayed a working method and the broken method.
I'm using QT-ROOT, the TQtWidget is a custom widget which is essentially a back to the TCanvas.
Setting Up My Canvas
void DataTestTab::setupCanvas(int cNCbc) //I pass "2" to this for now to generate the below loop twice
{
for (int i=0; i<cNCbc; i++)
{
m_vectorCanvas.push_back(new TQtWidget(this));
//m_vectorCanvas[i]->GetCanvas()->SetFillColor(i);
QHBoxLayout *loH = new QHBoxLayout(this);
loH->addWidget(m_vectorCanvas[i]);
m_vectorLayout.push_back(loH);
QGroupBox *gbCanvas = new QGroupBox(this);
QString title = QString("CBC %1").arg(i);
gbCanvas->setTitle(title);
gbCanvas->setLayout(m_vectorLayout[i]);
m_vectorGroupBox.push_back(gbCanvas);
ui->loCbcBox->addWidget(m_vectorGroupBox[i]); //adding the panels to the main layout
}
}
This Works
void DataTestTab::drawTest()
{
static Int_t HistoID = 1;
qDebug() << "in Testing env ";
std::vector<TH1D*> graphs;
std::vector<TCanvas*> vCanvas;
TString name("h1_");
Bool_t build = false;
for (int i = 0; i <m_vectorCanvas.size() ; i++)
{
TCanvas *cCanvas = new TCanvas(build);
name += HistoID++;
vCanvas.push_back(cCanvas);
vCanvas.at(i)->cd();
TH1D *h1 = new TH1D(name.Data(),name.Data(),10,0, 10);
graphs.push_back(h1);
graphs.at(i)->Fill(i);
graphs.at(i)->Draw();
//graphs.at(i)->DrawCopy();
m_vectorCanvas.at(i)->GetCanvas()->SetFillColor(i+5);
m_vectorCanvas.at(i)->cd();
qDebug() << i;
m_vectorCanvas.at(i)->GetCanvas()->SetCanvas(vCanvas.at(i));
m_vectorCanvas.at(i)->Refresh();
}
}
Corresponding output:
Albeit the graphs are in the wrong order.
This Does Not Work
I transfer this method to another class and pass the TCanvas back over signal/slots.
void DataTestWorker::doWork()
{
static Int_t HistoID = 1;
qDebug() << "in Testing env ";
std::vector<TH1D*> graphs;
std::vector<TCanvas*> vCanvas;
TString name("h1_");
Bool_t build = false;
for (int i = 0; i <2 ; i++)
{
TCanvas *cCanvas = new TCanvas(build);
name += HistoID++;
vCanvas.push_back(cCanvas);
vCanvas.at(i)->cd();
TH1D *h1 = new TH1D(name.Data(),name.Data(),10,0, 10);
graphs.push_back(h1);
graphs.at(i)->Fill(i);
graphs.at(i)->Draw();
}
emit sendGraphData(vCanvas); //void sendGraphData(const std::vector<TCanvas*> &canvas);
The graph data is then sent to here:
void DataTestTab::drawGraph(const std::vector<TCanvas*> &canvas)
{
for (int i=0; i<m_vectorCanvas.size(); i++)
{
canvas.at(i)->cd();
m_vectorCanvas.at(i)->cd();
m_vectorCanvas.at(i)->GetCanvas()->SetCanvas(canvas.at(i));
m_vectorCanvas.at(i)->Refresh();
//m_vectorCanvas.at(i)->GetCanvas()->Update();
}
}
This is the output of this method:
The only error I can see at the moment is I get this for the SetupTab:
QLayout: Attempting to add QLayout "" to GUI::DataTestTab "DataTestTab", which already has a layout
QLayout: Attempting to add QLayout "" to GUI::DataTestTab "DataTestTab", which already has a layout
I am trying to tackle thing one at a time. Your error message
QLayout: Attempting to add QLayout "" to GUI::DataTestTab "DataTestTab", which already has a layout
stems from the fact that you are calling this constructor for your QHBoxLayout:If you look at the documentation for
QLayout
(e.g. http://qt-project.org/doc/qt-4.8/qlayout.html#QLayout) then you are calling the constructor with a parent (in your casethis
) and the documentation says:So even if you don't explicitly set these layouts as the main layout of your
DataTestTab
, Qt tries to do this because this is how the constructor is written and this results in the message you are seeing.The obvious cure for this problem is to change
to
which then calls this constructor:
i.e. a
QLayout
that can be added to another one (and has to in order to work).EDIT: I think you can simplify your way of handing over objects. There is no reason to construct a
TCanvas
for each histogram and pass it around, especially since theTCanvas
will not take ownership of your histogram and I believe that when you issueTH1::Draw()
even havingcd()
'ed into the canvas you want, this does not achieve what you are trying to do.I played with your code and I could achieve what you want by simply having:
and
I obviously do not perform any checks to make sure that the
std::vector<TH1D*>
andstd::vector<TQtWidget*>
have the same length etc. but with this code I was able to display two histograms on the twoTCanvas
as you showed in yourThis Works
example.Does this help?