Warning: this page refers to an old version of SFML. Click here to switch to the latest version.
Integrating to a wxWidgets interface
Introduction
The best way to integrate some SFML graphics to a wxWidgets interface is to create a specialized type
of control. In this tutorial, we'll define a wxSFMLCanvas
class that will
be a base class for creating your custom rendering controls.
Creating a custom control
Let's have a look at what we need to define a wx/SFML control. To create a custom wxWidgets control, we must
inherit from the base class wxControl
. And, to allow our control to be a SFML view, we also inherit
from sf::RenderWindow
.
#include <SFML/Graphics.hpp>
#include <wx/wx.h>
class wxSFMLCanvas : public wxControl, public sf::RenderWindow
{
// ...
};
What are we going to put into this class ? Well, nothing much in the public interface, as all the useful
functions will be brought by both wxControl
and sf::RenderWindow
. In fact, from wxWidgets'
point of view wxSFMLCanvas
will be like any other control, and from SFML's point of view it will be
like any other rendering window. That's the power of inheritance.
So, in the public interface we only need a standard constructor, which will take the usual parameters that
define a wxWidgets control (parent, identifier, position, size, style), and a destructor which will be empty
but needs to be virtual, as wxSFMLCanvas
will be used as a base class.
Then, in the private part of our class, we'll need to define a few functions that will be bound to some useful events. We'll need to catch the idle event, which is the event that is triggered when there is no event to process, the paint event, and the erase-background event. The idle event will be useful for updating our control as often as possible ; the paint event will be called each time the control needs to be repainted, so we'll put our SFML rendering code in it. Finally, we won't put anything into the erase-background event, we override it just to prevent wxWidgets from drawing it, which would cause some flickering.
We also need to define one virtual function, to notify the derived class window updates.
Let's put this all together, and see what our class would look like :
#include <SFML/Graphics.hpp>
#include <wx/wx.h>
class wxSFMLCanvas : public wxControl, public sf::RenderWindow
{
public :
wxSFMLCanvas(wxWindow* Parent = NULL, wxWindowID Id = -1, const wxPoint& Position = wxDefaultPosition,
const wxSize& Size = wxDefaultSize, long Style = 0);
virtual ~wxSFMLCanvas();
private :
DECLARE_EVENT_TABLE()
virtual void OnUpdate();
void OnIdle(wxIdleEvent&);
void OnPaint(wxPaintEvent&);
void OnEraseBackground(wxEraseEvent&);
};
Almost every function will be empty, we only need to explain three of them : OnIdle
, OnPaint
and
the constructor. The latter will contain all the hardly-understandable-platform-specific part of the
code, so we'll leave it for the end.
First, let's have a look at the OnIdle
function :
void wxSFMLCanvas::OnIdle(wxIdleEvent&)
{
// Send a paint message when the control is idle, to ensure maximum framerate
Refresh();
}
That was really easy, wasn't it ? The Refresh
function is defined in wxControl
, and will
trigger a repaint event to update the control. By calling it in the idle event, we ensure maximum framerate
for our SFML view.
The paint event is not much more complicated :
void wxSFMLCanvas::OnPaint(wxPaintEvent&)
{
// Prepare the control to be repainted
wxPaintDC Dc(this);
// Let the derived class do its specific stuff
OnUpdate();
// Display on screen
Display();
}
The first thing we do in the paint function is to create a wxPaintDC
object, and pass it a pointer to
our control. Doing this will "lock" the graphic area of the control, and ensure we'll be able to draw into it.
Forgetting to do it would result in weaird things.
Now, let's see how we link our wxWidgets control and a SFML render window. This part heavily depends on the underlying wxWidgets implementation, so don't focus too much on it.
#ifdef __WXGTK__
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <wx/gtk/win_gtk.h>
#endif
wxSFMLCanvas::wxSFMLCanvas(wxWindow* Parent, wxWindowID Id, const wxPoint& Position, const wxSize& Size, long Style) :
wxControl(Parent, Id, Position, Size, Style)
{
#ifdef __WXGTK__
// GTK implementation requires to go deeper to find the
// low-level X11 identifier of the widget
gtk_widget_realize(m_wxwindow);
gtk_widget_set_double_buffered(m_wxwindow, false);
GdkWindow* Win = GTK_PIZZA(m_wxwindow)->bin_window;
XFlush(GDK_WINDOW_XDISPLAY(Win));
sf::RenderWindow::Create(GDK_WINDOW_XWINDOW(Win));
#else
// Tested under Windows XP only (should work with X11
// and other Windows versions - no idea about MacOS)
sf::RenderWindow::Create(GetHandle());
#endif
}
As you can see, the implementation which requires more attention is the GTK one. As GTK is not the lowest level,
we need to go deeper to get the internal window handle. We also need to realize the widget (to make sure it is actually
created), and disable the double-buffering provided by GTK, as we already have our own one.
And yes, GTK_PIZZA is quite a weird name, I still don't understand it.
For other implementations (WXMSW, WXX11) directly giving the identifier returned by GetHandle
will be enough.
Creating the wxWidgets interface and using our custom control
Now that we have a generic SFML / wxWidgets component, let's specialize it to do something useful. Here we'll define a simple custom control that displays a sprite.
class MyCanvas : public wxSFMLCanvas
{
public :
MyCanvas(wxWindow* Parent,
wxWindowID Id,
wxPoint& Position,
wxSize& Size,
long Style = 0) :
wxSFMLCanvas(Parent, Id, Position, Size, Style)
{
// Load an image and assign it to our sprite
myImage.LoadFromFile("sprite.png");
mySprite.SetImage(myImage);
}
private :
virtual void OnUpdate()
{
// Clear the view
Clear(sf::Color(0, 128, 128));
// Display the sprite in the view
Draw(mySprite);
}
sf::Image myImage;
sf::Sprite mySprite;
};
Then we create the main window of the application, into which we'll create a SFML view with our custom component :
class MyFrame : public wxFrame
{
public :
MyFrame() :
wxFrame(NULL, wxID_ANY, "SFML wxWidgets", wxDefaultPosition, wxSize(800, 600))
{
new MyCanvas(this, wxID_ANY, wxPoint(50, 50), wxSize(700, 500));
}
};
And finally, the application class :
class MyApplication : public wxApp
{
private :
virtual bool OnInit()
{
// Create the main window
MyFrame* MainFrame = new MyFrame;
MainFrame->Show();
return true;
}
};
IMPLEMENT_APP(MyApplication);
Conclusion
With this component, you can integrate SFML into your wxWidgets interfaces very easily.
Feel free to use it and improve it !
That was the last tutorial about SFML graphics package. You can now go and create your own
graphics software, or you can jump to
another section
to learn a new package.