follow my heart...
分类: C/C++
2006-08-25 12:01:31
Since wxSmith is able to do something already it's high time to write a small tutorial on how to use it. At the very beginning, I must point out that 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 :)
wxSmith can be used inside any wxWidgets project. However, we currently have only one option to easily create a wxWidgets app. Simply select File -> New Project -> wxWidgets Application from the menu. If You have compiled wxWidgets using the wiki tutorial, select Using wxWidgets DLL in Project Options. Hit Create and save the project. If you're running on Windows, you probably will have to change the WX_DIR Custom variable. It should point to the root wxWidgets directory and it can be changed in Project -> Build Options menu in the Custom variables tab.
After this step you should be able to produce an app like this:
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:
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
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...
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:
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:
Always make sure that a valid item is selected and that a valid insertion method is chosen.
There are five types of sizers in wxWidgets, four 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. Perform the following:
You probably noticed that our panel decreased it's sizer. This is because the sizer automatically adjusts to the minimal size of it's parent. And there's an additional red border - it shows where the sizer is located.
Because this is a "Hello World" application, we'll put the text into a newly created sizer. To do this:
And because this text looks so lonely, we'll add another button here:
Ok, now we have this:
But I would prefer to put this button below the text. How to do this ? As I mentioned before, sizers are automatic positioning items and, in our case, the sizer decided to put the widgets in a horizontal row, one next to another. If we want them to be in a vertical colume, we need to change some properties for the sizer:
What have we done ? We instructed the sizer to create only one column of widgets. Alternatively, we could have set the number of rows (Y) to 2. The effect would have been the same. Setting the values to 0 means that the sizer has to find its values automatically. Ok, let's see what we've done.
In this tutorial we will put our panel over the main frame. To do this:
You should see something like this:
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 a bigger font, and let's say, a blue font colour. The first can be done using the previously created wxFlexGridSizer. It needs just a few modifications:
The changed properties keep information about columns and rows which should expand when the window changes size. Values are integers (zero-based indexes) separated by commas.
The second is also easy:
Ok, let's compile & run. Now when we resize the window, our panel changes dynamically, the font is bigger and the colour has changed :)
Now we'll add an action to our button. Let's say it will close the program. First, let's change the 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 like many other GUI systems - through events. An event is a small peace of information saying that something has happened - for example, the user clicked on a button. But we want to do something when such and event happens. To connect events with actions we have to create an event handler. wxSmith can do this automatically:
As You can see, a new empty function has been created
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event)
{
}
Here we can write some code that will be executed when you press the button. So, let's add a Close() command
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event)
{
Close();
}
and see what happens. I click on Close button and... nothing happens. Why ? Because we closed the panel, not the whole window. How do I know that ? Our event handler is a member of the HelloWorldPnl class. Everything inside the event handler pertains to the panel, not the outer window. When we called Close() we called this function in the wxPanel class. But how can we close the main frame? Change the code to:
void HelloWorldPnl::OnButton1Click(wxCommandEvent& event)
{
GetParent()->Close();
}
The GetParent() function will return a pointer to the parent window - frame. Now Close() is being called on the panels parent, the window. But be careful. Our example was easy and we just assumed that main frame will be the panel's parent. Usually we can not be so sure.
Our Hello World application is ready to go :) I hope It wasn't boring. Not bored ? Then read next chapter ;)
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.
wxSmith is not as intelligent as it may seem ;) 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. If you want to write your event handler manually, you can put its declaration here.
Code outside the //(* - //*) comments won't be touched.
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(".xrc");