| wxWidgets GUI Toolkit |
29 October 2004, 2:31AM |
|
I started working on some tools for my little test engine and quickly remembered how annoying it
is to keep on rewriting the same GUI components over and over. I love writing good tools,
but GUIs can get annoying after a while. I really dislike MFC, and I'm not overly fond of the WTL.
So my first instinct was to dig up some wrapper classes I wrote a
few years ago for various controls and window operations using the Windows API. After thinking about it a
bit more, I remembered using the wxWidgets (formerly wxWindows)
GUI library on Linux a while back and decided
to give it another look. The Linux version used gtk+ for its GUIs, so I assumed it would be using the
Windows port of gtk+ to keep things consistent. How wrong I was! It uses native Windows code, which means
the resulting GUIs have the Windows look and feel we're all familiar with.
So what exactly is wxWidgets? Its a library that offers a simple
programming interface for writing GUIs on a number of platforms (such as Windows, Linux, and MacOS.)
Its fast, simple to use, and looks good. Even if you're only writing applications for a single OS, its
worth a look just for the simplicity it offers.
The library is available under the wxLicense, which is basically the LGPL with an added exception: you can
distribute derived works in binary form however you want (ie, you don't need to release your code),
making it a perfectly good choice for writing proprietary/commercial applications as well as open
source ones. On top of that the project has grown popular over time, meaning there are a lot of resources,
tutorials, and communities out there focused on it.
So how does it work? Well, I've only been fooling with it for a couple hours or so, but I've been very impressed with
what I've seen so far. I downloaded the latest stable build for Windows, and unlike nearly any other library I'veever worked with, the binaries built on the first try without need for any manual configuration or tweaking.
The manual doesn't explain a whole lot about how to get started, but the many examples included with the library
effectively explain how to do everything from simple controls to more advanced things, such as splitter windows and MDI interfaces.
Below is some sloppy source code (and a screenshot further down) to a quick tool interface I threw together for an
object mesh converter I needed to write. I cut out the actual functionality since we're just talking about GUIs here:
ObjectTool.h:
#ifndef __OBJECTTOOL__H__ #define __OBJECTTOOL__H__
/** The main implementation of the Object Tool as a dialog window. */
class ObjectTool : public wxDialog { public:
enum { ID_MESHLIST = 10000, ID_STATICBOX = 10001, ID_BUTTON_ADD = 10002, ID_BUTTON_REMOVE = 10003, ID_BUTTON_NAME = 10004, ID_BUTTON_TEXTURE = 10005 };
ObjectTool(wxWindow *parent, const wxString &title, wxPoint position);
void OnClick_AddMesh(wxCommandEvent &event); void OnClick_RemoveMesh(wxCommandEvent &event); void OnClick_SetName(wxCommandEvent &event); void OnClick_SetTexture(wxCommandEvent &event);
void shutdown();
private:
wxListCtrl *m_meshList;
DECLARE_EVENT_TABLE() };
#endif |
ObjectTool.cpp:
#include <wx/wx.h> #include "wx/listctrl.h"
#include "ObjectTool.h"
/** Initializes the dialog window and creates the child controls */
ObjectTool::ObjectTool(wxWindow *parent, const wxString &title, wxPoint position) : wxDialog(parent, -1, title, position) { SetSize(600, 250);
// Create a static grouping box. wxStaticBox *staticBox = new wxStaticBox(this, ID_STATICBOX, "Object Meshes:", wxPoint(5, 5), wxSize(583, 215));
// Create the main list control for listing meshes. m_meshList = new wxListCtrl(this, ID_MESHLIST, wxPoint(12, 20), wxSize(568, 165), wxLC_REPORT);
// Insert the list columns. m_meshList->InsertColumn(0, "Mesh Name", wxLIST_FORMAT_LEFT, 200); m_meshList->InsertColumn(1, "Vertices", wxLIST_FORMAT_LEFT, 82); m_meshList->InsertColumn(2, "Faces", wxLIST_FORMAT_LEFT, 82); m_meshList->InsertColumn(3, "Texture", wxLIST_FORMAT_LEFT, 200);
// Create the dialog buttons. wxButton* buttonAddMesh = new wxButton(this, ID_BUTTON_ADD, _("&Add Mesh"), wxPoint(12, 190), wxSize(140, 25), 0); wxButton* buttonRemoveMesh = new wxButton(this, ID_BUTTON_REMOVE, _("&Remove Mesh"), wxPoint(155, 190), wxSize(140, 25), 0); wxButton* buttonSetName = new wxButton(this, ID_BUTTON_NAME, _("Set &Name"), wxPoint(298, 190), wxSize(140, 25), 0); wxButton* buttonSetTexture = new wxButton(this, ID_BUTTON_TEXTURE, _("Set &Texture"), wxPoint(441, 190), wxSize(140, 25), 0); }
/** This function is called when the 'add mesh' button is clicked. */
void ObjectTool::OnClick_AddMesh(wxCommandEvent &event) { static const wxChar *FILE_TYPES = _T("3DS Mesh Files (*.3ds)|*.3ds|" "All files|*.*");
// Show the file open dialog to select a mesh.
wxFileDialog *fileDlg = new wxFileDialog(this, "Load Mesh File", "", "", FILE_TYPES, wxOPEN, wxDefaultPosition);
if(fileDlg->ShowModal() == wxID_OK) { char fname[4096] = {0}; sprintf(fname, "%s\\%s", fileDlg->GetDirectory().c_str(), fileDlg->GetFilename().c_str());
// ... do something with the file name; } }
/** This function is called when the 'remove mesh' button is clicked. */
void ObjectTool::OnClick_RemoveMesh(wxCommandEvent &event) { // ... do something; }
/** This function is called when the 'set name' button is clicked. */
void ObjectTool::OnClick_SetName(wxCommandEvent &event) { // ... do something; }
/** This function is called when the 'set texture' button is clicked. */
void ObjectTool::OnClick_SetTexture(wxCommandEvent &event) { // ... do something; }
/** This function shuts down the dialog window. */
void ObjectTool::shutdown() { Close(TRUE); }
// The event table which maps the events to member functions;
BEGIN_EVENT_TABLE(ObjectTool,wxDialog) EVT_BUTTON(ID_BUTTON_ADD, ObjectTool::OnClick_AddMesh) EVT_BUTTON(ID_BUTTON_REMOVE, ObjectTool::OnClick_RemoveMesh) EVT_BUTTON(ID_BUTTON_NAME, ObjectTool::OnClick_SetName) EVT_BUTTON(ID_BUTTON_TEXTURE, ObjectTool::OnClick_SetTexture) EVT_CLOSE(ObjectTool::shutdown) END_EVENT_TABLE() |
Main.cpp:
#include <wx/wx.h> #include "wx/listctrl.h"
#include "ObjectTool.h"
/** Main application */
class ObjectToolApp : public wxApp { public:
bool OnInit(); };
/** Creates the parent frame and main object tool dialog window. */
bool ObjectToolApp::OnInit() { // Create an invisible frame as a dialog parent; wxFrame *frame = new wxFrame(NULL, -1, "Game Object Converter"); frame->Show(FALSE);
// Create the dialog window and show it; ObjectTool *objtool = new ObjectTool(frame, "Game Object Converter", wxDefaultPosition); objtool->ShowModal();
// Close the frame and exit after the dialog is closed; frame->Close(TRUE);
return true; }
IMPLEMENT_APP(ObjectToolApp) |
The source code should be
pretty self-explanatory. Here's what it looks like running:
Simple, good looking, and in theory would work on various platforms. I haven't actually tried building it on my Linux machine yet,
but I'm mostly focused on Windows development at the moment anyhow.
While the above code example might not look significantly simpler than doing a dialog window tool with the Windows API directly, the real gains
involved with using a library like wxWidgets are seen when you're working on more complex interfaces -- like the
MDI windows and splitters I mentioned. And of course the whole multi-platform thing.
Note that the above code is my first quick go at writing a wxWidgets app, and I posted it just to show you the basic concept.
For one thing, the sample code doesn't check
the pointers to make sure the window objects were created correctly. As far as deleting them, my understanding is that
the wxDialog's destructor takes care of that for the controls, and similarly for the other base classes.
All I'm really saying is: no guarantees that its implemented correctly or in the best way. I've still got
a lot of reading to do!
All in all, from what I've seen I'm pretty sure I'll be writing all of my engine's tools with wxWidgets.
In addition you can call GetHandle() from some of the base classes to get a native window handle (HWND, obviously Windows-specific), which
means I see no reason why I couldn't set up a Direct3D renderer on a wxWidget canvas. That sounds like fun to me.
The one slightly frustrating thing about the whole lot is that by default you have to specify the position and sizes
of every widget by hand. There are visual tools to help you design GUIs (ie, save time), but the two best ones
(wxDesigner and DialogBlocks)
are both commer$ial applications. But I suppose if the library proves ultra-handy for my work
after using it for a while, such tools would probably be worth the investment.
Anyway, have a look at the library yourself if you're interested.
Complete List Of Journal Updates:
Auto-Terrain Texturing (15 November 2004, 12:27PM)
On Orientation Interfaces (14 November 2004, 7:30PM)
Basic Terrain Generation (10 November 2004, 4:39AM)
Engine Tool Interface (07 November 2004, 2:37AM)
Render Buffer Ranges (02 November 2004, 5:15AM)
wxWidgets GUI Toolkit (29 October 2004, 2:31AM)
Generic Rendering Interfaces (27 October 2004, 6:55PM)
Source Documentation with Doxygen (25 October 2004, 6:34PM)
Signs of Life? (25 October 2004, 5:14PM)
Key Value Scripts (08 November 2003, 4:32PM)
Octrees for Potential Colliders (01 November 2003, 4:09PM)
Flexporter and Game Levels (31 October 2003, 12:14PM)
New Project and More Updates (31 October 2003, 6:14AM)
HSL Color Space (07 May 2003, 6:14AM)
A Couple Graphics Books (02 December 2002, 5:12PM)
Texture Detail Using Colored Triangles (02 November 2002, 3:26AM)
Voxel Mesh Creation and Rendering (27 October 2002, 11:30AM)
Development Journal (25 October 2002, 10:57AM)
|
|