Understand how to embed a Canvas in a wxWidgets application

Hi everyone, I’d need some help in embedding a TCanvas in a wxWidgets 3.0.0 program since I’m having an hard time with the instructions about this topic (How to Embed a TCanvas in External Applications).

I never tried to use wxWidgets before this project, so I’m quite unprepared about the library itself!

Here’s my code:

#include <wx\wxprec.h>

#ifndef WX_PRECOMP
#include <wx\wx.h>
#endif

// ROOT Includes
#include <TApplication.h>
#include <TVirtualX.h>
#include <TSystem.h>
#include <TCanvas.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();

    TApplication *RApp;
    TSystem      *RSystem;
    TCanvas      *RCanvas;
    TVirtualX    *RVirtualX;
    wxTimer      *Timer;
};

DECLARE_APP(MyApp);
wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);

private:
    void OnExit(wxCommandEvent& event);
    void OnTimer(wxTimerEvent& event);
    wxDECLARE_EVENT_TABLE();
};

enum
{
    ID_Timer = 1
};

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)

EVT_MENU(wxID_EXIT, MyFrame::OnExit)
EVT_TIMER(ID_Timer, MyFrame::OnTimer)

wxEND_EVENT_TABLE()

bool MyApp::OnInit()
{

    Timer = new wxTimer(this, ID_Timer);
    Timer->Start();

    RSystem = new TSystem;

    RApp = new TApplication("app", 0, NULL);
    RApp->SetReturnFromRun(true);

    MyFrame* frame = new MyFrame("ROOT Test", wxPoint(50, 50), wxSize(450, 340));
    frame->Show(true);

    RVirtualX = new TVirtualX;
    int windowID = RVirtualX->AddWindow((ULong_t) frame->GetId(), 450, 340);
    RCanvas = new TCanvas("RCanvas", 450, 340, windowID);

    return true;
}

MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size)
{
    wxMenu* menuFile = new wxMenu;
    menuFile->Append(wxID_EXIT);

    wxMenuBar* menuBar = new wxMenuBar;
    menuBar->Append(menuFile, "&File");

    SetMenuBar(menuBar);
    CreateStatusBar();
    SetStatusText("Welcome to wxWidgets!");

}

void MyFrame::OnExit(wxCommandEvent& event) { Close(true); }

void MyFrame::OnTimer(wxTimerEvent& event)
{

    wxGetApp().RApp->StartIdleing();
    wxGetApp().RSystem->InnerLoop();
    wxGetApp().RApp->StopIdleing();

}

I tried to modify the “Hello World!” example on the wxWidgets documentation (here’s the link) and show a TCanvas on a wxFrame.
Following the instructions on how to embed the TCanvas i created a TApplication in my wxApp initializer and then I started having some problem, the instructions say that I should create a TCanvas in a class called “MyWindow” and that should be the wxFrame class, but by doing so nothing shows up running the program!
I then tried to create the TCanvas in the wxApp initializer, as you can see in my code, and by doing so the program starts with a menu bar as it should and immediately crashes!

The program breaks trying to initialize TCanvas, so I think this is the code poorly written:

RVirtualX = new TVirtualX;
int windowID = RVirtualX->AddWindow((ULong_t) frame->GetId(), 450, 340);
RCanvas = new TCanvas("RCanvas", 450, 340, windowID);

I believe I’m not handling correctly the Window ID that I have to pass to the TCanvas constructor, so the problem could be about the TVirtualX class, but, since the instrunction don’t tell anything about that, I just created it a bit randomly! :blush: (I don’t usually have to do with that class!)

I also tried to implement a timer, but nothing changed!

I’m using ROOT 5.34.14, wxWidgets 3.0.0, Visual Studio Express 2013 on Windows 7 64bit.
Since I’m not an expert programmer, my code is probably really flawed!

Thanks in advance, Nicolò!

Hi,

First, remove these lines, they are illegal:

RSystem = new TSystem; RVirtualX = new TVirtualX; Since TSystem and TVirualX instances are created internally, and accessible via globals gSystem and gVirtualX. For example:

int windowID = gVirtualX->AddWindow((ULong_t) frame->GetId(), 450, 340); RCanvas = new TCanvas("RCanvas", 450, 340, windowID); The first parameter of gVirtualX->AddWindow (i.e. frame->GetId()) should be the native (system) id of the parent window. So you should make sure that your frame->GetId() returns the native Window id…

Cheers, Bertrand.

Thank you Bertrand, the program stopped crashing!
I didn’t know about gVirtualX and gSystem, I should read something about them… #-o
And also I was not using the right window ID for gVirtualX->AddWindow, to get the native ID I must get the HWND (an handle to a window, link) and then get the ID calling GetWindowLong (link).
Now the code is:

HWND canvasWindow = (HWND) this->GetTopWindow()->GetHWND();
int windowID = gVirtualX->AddWindow((ULong_t)GetWindowLong(canvasWindow, GWL_ID), 600, 600);

ThoughI still have problems trying to draw a simple TGraph with two point on it!
I found that the Menubar and the Statusbar don’t allow the TCanvas to appear on screen, everything looks just like a plain wxWidgets program. Also I believe i have to work with Device Contexts (link) since, without the Menu/Statusbar, the TCanvas gets printed slightly outside the main window (that’s just a positioning problem :laughing: ) but only the section outside the window is visible! Reading the wxWidgets documentation seems like the wx Device Contexts would allow me to draw anything one the window’s screen region, but I don’t yet get how! :blush:

I’ll keep working on it! This was just an update to thank you Bertrand and for everyone that’s interested in wxWidges!