WxSmith Tutorial & Pointers
"Hello world" Tutorial
Since wxSmith is able to do something already it's high time to write some tutorial on how to use it. At the very beginning I must point - to use wxSmith You MUST have wxWidgets compiled (version 2.6 or later), build instructions can be found here
WARNING: wxSmith is still unstable so use it at Your own risk. And there's no undo yet. I warned You ;)
Ok, let's start :)
wxWidgets starts to breathe
wxSmith can be used inside any wxWidgets project. Currently we have only one option to easily create wxWidgets app. Simply select File -> New Project -> wxWidgets Application from menu. If You have compiled wxWidgets using wiki tutorial, select Using wxWidgets DLL in Project Options. Hit Create and save project. If You're running on Windows, You will probably have to change WX_DIR Custom variable. It should point to root wxWidgets directory and it can be changed in Project -> Build Options menu on Custom variables tab.
After this step You should be able to produce an app like this:
Let's add some fireworks
Now it's time to fill the empty frame with our "Hello World" message. We will do this by placing a panel over the frame. Select wxSmith -> Add Panel from menu. If you're doing this for the first time in a project, it should display this message box:
Just click Yes, it will bring up a configuration dialog. You can set-up following options here:
- Class Name - name of class which will contain our panel
- Header File / Source File - files which will contain the panel's class
- Xrc File - selecting this option allow you to use a xrc file containing the panel's data
In our tutorial we will use a class named "HelloWorldPnl" (notice that usually when typing a class name, header and source file names are generated automatically). The config dialog should look like this:
Click Create and we have our panel right in the editor
Building window
wxWidgets comes with something called Sizers. But what is this for? If You have been working with java you will remember something called Layout managers. Implementation in wxWidgets differs a little bit but it does almost the same. Ok, but let's put some explanation here: Usually when adding items into windows You must specify the item's position and size. wxWidgets tries to automate this process and it uses sizers for that. They are automatic positioning and sizing window items. Sizers have one big advantage. When You write cross-platform applications, you cannot assume that fonts, buttons, etc. are sized the same from one platform to another. This can even occur on the same platform. When You use sizers, You don't have to worry about that. All sizing is done automatically. And one more thing - sizers can even reposition and resize window items when the window itself changes size. So, let's add some sizers here. But first...
How can I add something ?
In a newly opened editor you will see eight black boxes around something which looks like a button without a label
These black boxes are surrounding a currently selected item. In our case it's a whole panel. Adding new item is simply done by clicking on one of the buttons in the palette at the bottom of the C::B window.
These buttons have small pictures showing what they add. If you're unsure, hold the mouse over the button and, after a moment, you will see the name of the wxWidgets class that the button represents - that's our item.
New items are added relatively to the current selection. You can add new item in one of three ways:
- Before the currently selected widget
- After the currently selected widget
- Into the currently selected widget
You change the insert method via a palette (it should be easy to find ;)). (big buttons, far right) Note that all three insertions settings are not always accessible. For example You cannot add anything into a button. There are also some special situations when You cannot add new item:
- When an item has sizer inside, it cannot contain anything else. Items can be added into the sizer only
- When an item has anything but a sizer inside, you cannot add a sizer into it.
- Spacers (empty fields added into sizers instead of real items) can be added into a sizers only
Always make sure that a valid item is selected and that a valid insertion method is chosen.
Adding items
There are 5 types of sizers in wxWidgets, 4 are currently supported inside wxSmith (wxGridBagSizer is not supported yet). If You wish to learn about sizers i propose wxWidgets doccumentation In our sample we will add wxFlexGridSizer. To do this follow me:
- Select panel by clicking on it
- Click on wxFlexGridSizer button (third in Layout row)
You proably noticed that our panel decreased it's sizer. This is made because sizer tries to automatically adjust minimal size of it's parent. And there's additional red border - it shows where sizer is located.
Because this is "Hello World" application, we need this text here. We will put it into created sizer. To do this:
- Select sizer (click somewhere insided red border)
- Make sure that Insetion Type is Into
- Click on wxStaticText button
- Click on newly created text item (should be "Label") and go to properties (The Properties panel is inside Resources tab at the left of C::B Use the little arrows in the Management panel to find the Resources Panel )
- Change Label property to "Hello World !!"
And because this text looks so lonely, we will add button here:
- Select Newly created text
- Chose "After" Insertion type
- Click on Button's icon
Ok, now we have this:
But I would preffer to put this button down. How to do this ? As I mentioned before, sizers are automatically positioning items and in our case, sizer decided to put widgets in horizontal row, one next to another. If we want them to be in vertical row, we need to change some properties for sizer:
- Select wxFlexGridSizer in tree representing structure of our window (over properties)
- Expand Cols x Rows property and change X value to 1
What have we done ? We said that this sizer should create only one column of widgets. We could also set number of rows (Y) to 2, effect would be same. Setting value to 0 means that sizer has to find this value automatically. Ok, let's see what have we done.
Using created panel
In this tutorial we will put out panel over main frame. To do this, just follow me:
- Open main.cpp file
- Add #include "helloworldpnl.h" at the begginning of file
- At the end of MyFrame::MyFrame add new HelloWorldPnl(this);
- Compile and Run
You should see something like this:
But I want it to look better
Let's see. I would like my panel to be more interesting, I want it to change when we resize our window. And I want bigger font, and let's say, blue font colour. First can be done using previously created wxFlexGridSizer, need just few modifications:
- Select wxFlexGridSizer from resource browser
- Change growable cols property to 0 (zero)
- Change growable rows property to 0 (zero)
Changed properties keep information about columns and rows which should expand when window changes size. Values are integers (zero-based indexes) divided by coma.
Second is also easy:
- Select wxStaticText from resource browser (or click on it in editor)
- Find font property and expand it
- Change Use Font to True
- Click on font property below - button with "..." will appear, click on it
- Select new font, I used "Times New Roman", Bold, size: 20
- Find foreground property and expand it
- Change Use Colour to true
- In Colour below select Custom, colour dialog will appear
- Select colour You like :)
Ok, let's compile & run. Now when we resize window, our panel changes dynamically, font is bigger and colour has changed :)
How to make button respond
Now we'll add action to our button, let's say it will close program. First thing is to change button label to "Close". This should be pretty easy and I hope You won't have any problems with it. Ok, now let's add some action.
wxWidgets works as many other gui systems - through events. Event is a small peace of informathion saying that something has happened - for example user clicked on button. But we waht to do something when such event happens. To connect event with action we have to create event handler and wxSmith can do it automatically:
- Select button
- Switch to Events tab (if You see properties, it's right there)
- In the line named EVT_BUTTON choose "-- Add new handler --"
- Change event handler name to something You want (I will leave it as it is ;)) and click OK
As You can see, new empty function has been created
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event) { }
Here we can write some code and it will be executed when You press button. So, let's add Close() command
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event) { Close(); }
and see what happens. I click on Close button and... nothing happens. Why ? Because we want to close panel, not whole window. How I know that ? Our event handler is member of HelloWorldPnl class and everything inside event handler will be relative to it and when we call Close() we call this function in wxPanel class. But how to call it inside main frame ? Change the code to
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event) { GetParent()->Close(); }
GetParent() function will return pointer to parent window - frame. Now it's easy to call Close inside it. But be careful. Our example was easy and we can assume that main frame will be panel's parent. Usually we can not be sure.
Our Hello World application is ready to go :) I hope It wasn't boring. Not bored ? Read next chapter ;)
Pointers
Some technical info
Ok, I'll try to explain how wxSmith affects our code, how to work with it, why you shouldn't be worried about losing your code.
Where wxSmith generates its code ?
wxSmith is not not as intelligent as it looks like ;) When we say it generates code, it simply replaces whole code pieces without wondering if code is placed in the right position. But the code works. How is it done ?!
When You look into files generated inside wxSmith, you may find some special comments like :
//(*Headers(HelloWorldPnl) //*)
These comments are used by wxSmith to find the place where new code should be applied. Each //(* comment starts automatically generated block of code and //*) closes it. Everything between these comments is regenerated, even if you add something there. The only exception is a block started with the //(*Handlers comment. wxSmith can only add to this block of code and if you want to write your event handler manually, you can put its declaration here.
Code outside the //(* - //*) comments won't be touched.
Loading XRC resources
When using an XRC file, do not forget to initialize the wxXMLResouce Handlers & XRC File. For example in your App::OnInit:
// Loading XRC resource file (not in a zip file). wxXmlResource::Get()->InitAllHandlers(); wxXmlResource::Get()->Load("<your XRC File name>.xrc");