苦瓜的做法怎么做不苦:wxGlade: a GUI builder for wxWidgets/wxPython
wxGlade Tutorial
The aim of this minimal tutorial is to give an overview of wxGlade and its functionalities: despite its very short length, it is (hopefully) complete enough to make you understand how the application works, through a step-by-step description of the creation of a frame containing a notebook with some controls, a menubar and a statusbar.
Before we start, let me apologize for my English: it's not my native language (I'm Italian), so it is very far from perfection.
1. Creation of the frame
To create a new frame, click on the appropriate button of the "palette" (
Now the frame is displayed, and the properties window changes to show the attributes of the widget just created. The first thing we'll do is change the title of the frame, which is by default equal to the object's name, and insert "Frame with notebook": the change takes effect when the Title property loses focus.
2. Creation of the menubar
Let's activate the Widget tab of the properties notebook of our frame, and check the property Has MenuBar to create a menu bar for the frame.
The properties window changes again, and now it shows the properties of the menubar: let's click on the Edit menus button to add some menus with the Menu Editor.
Click on the button Add to add a menu, and then edit the Label field to change its value to "File".
Let's then add some menu items to our File menu: click on the Add button again, change Label to "New", and finally click the > button: after the latter action we can see that the label of our item ("New") is indented compared to the previous one ("File"): this says that "New" is an item of "File".
Repeat the previous operations "ad libitum" to add other menus and items (and sub-menus), then click OK: the window gets closed, and our menubar contains the menus we added.
3. Creation of the statusbar
Under the Has MenuBar property of our frame there's the Has StatusBar one: check it to add the statusbar.
The Fields property of the object just added keeps the fields to display on the statubar: double-click on a field to edit it; to add, remove, insert other fields use the appropriate buttons.
Edit the default field, an set its value to "Created with wxGlade!", then add some other fields with the Add button: the Size column shows the dimension of each field: you can change them as you like, but remember that one of the fields must have a size of -1 (which tells that it will fill the remaining space).
After performing these operations, click on the Apply button to reflect the changes on the widget.
4. Creation of the notebook
Now it's time to add a notebook to our frame. You should have noticed that the frame you've just added contain a BoxSizer (
Let's add such notebook, then: to do so, click on the Notebook button (
A left-click starts the operation, and a dialog asking for the placement of the notebook's tabs appears: choose what you like best.
Now let's select the Common tab of the properties of the object just added: we can see that the Class property's value is wxNotebook, i.e. the name of the class our object is an instance of. If we change such value, we can make our notebook an instance of a custom class derived from wxNotebook: this has no effect during the creation of the app, but it changes the generated code, so that a definition for our custom class will be written. Let's change then the value of Class to MyNotebook, to make wxGlade generate the code for the custom class.
NOTE: The above is true only for "container" widgets, i.e. those that can have children. For controls, instead, the meaning is slightly different; let me illustrate it with an example.
Suppose you have a button inside a panel, its Class is wxButton. If you change such value to MyCustomButton, wxGlade assumes that you have a subclass of wxButton called MyCustomButton defined somewhere, and that such class has a costructor compatible with that of the "regular" wxButton, and so when it generates the code for the object, it writes:button_1 = MyCustomButton(parent, id, "Label")instead ofbutton_1 = wxButton(parent, id, "Label")
NOTE 2: For XRC output, if the value of Class is different from the default one, the object will have a subclass attribute. For the example above, the code generated would be:
This means that you should be a little careful with XRC output, in that you have to remember to reset the Class value to the default one when adding a "top-level" widget (frame, dialog,...), because by default wxGlade assumes they are custom classes.
5. Adding and removing notebook pages
This operation is almost identical to the handling of statusbar fields: the Tabs property of our notebook controls the number of pages (tabs) and their labels (don't forget to "Apply" the changes!).
6. Addition of some controls
We have now reached the last part of this short tutorial: the addition of some controls to the first page of our notebook. In particular, we'll add a text area and two buttons: these operations will allow us to show the layout options of the objects inside sizers, and the cut & paste support of wxGlade.
As said before, the first thing to do in order to add widgets to a container is to set its sizer. Let's start with the addition of a sizer to the first page of the notebook, which is where we are going to put our controls, then: to do so, click on the BoxSizer button (
6.1 Text area
Let's click on the TextCtrl button (
The default dimension isn't right, as we want to display a long and multiline text: to edit the layout, let's select the Layout tab of the TextCtrl's properties, and set to 1 the value of Option and to wxEXPAND that of Alignment. To make the text area multiline, let's check the wxTE_MULTILINE checkbox of the Style property (in the Widget tab): as with almost every Style property in wxGlade, such change has no visible effect, but it changes the generated code.
6.2 Buttons
Now replace the second slot with a horizontal Sizer which will contain our two buttons: set the number of slots to 3, to leave room for a spacer to insert between the two buttons (so as they won't appear too close to each other).
Replace then the first slot with a new button, as usually with the approprate button (
To add the second button, instead, we will use the clipboard: let's click on the Copy item of the popup menu of the first button (or select it and press Ctrl+C), then move the mouse inside the third slot, and finally click on the Paste item of the popup menu of the slot to paste the copied widget (again, you can alternatively left-click on the empty slot and then press Ctrl+V, or if you have a 3-buttons mouse just click on the empty slot with the middle button).
Now add a spacer (
Finally, the last operation we have to do is to edit the layout of the horizontal Sizer that contains the buttons: set to 0 the value of "Option", uncheck wxEXPAND from Aligment and check wxALIGN_CENTER_HORIZONTAL, and set a border (4) on the top (wxTOP) and bottom (wxBOTTOM), to keep the buttons separate from the text area and the notebook lower border.
NOTE on widgets' ids: You certainly know that every wxWindows widget has an id, used among other things for event handling, so in wxGlade each widget has an Id property. This may have the following values:
- a number: this will be the integer which will be passed to the constructor of the widget
- a name: in this case wxGlade assumes it is the name of an id declared somewhere in the program, and uses it. For example, you can set the value of Id to wxID_OK for the default OK button of a dialog
- a pair name=value: in this case before the constructor of the widget the declaration of the variable is generated. For example you could write TEST_BUTTON=wxNewId() to generate the id of a button called "Test" (note for C++ code generation: since ids are stored in an anonymous enum, only constant initializers are allowed, so the assigment above is not legal - but you can use, for instance, TEST_BUTTON=100)
- a pair name=?: this special form means that the code will contain the definition of a varable "name" with an auto-assigned unique id. For python output, this is the same as "name=wxNewId()", while for C++ output, it's the equivalent of "name=1000" (of course, 1000 is just an example here). In this latter case, wxGlade will start generating ids exacly from 1000, so you can safely use ids below that for your own purposes.
7. Last changes
Finally, suppose you are not happy with the current layout, and you decide it's better to put the buttons above the text area and not below as they are: so what can we do? Simple: let's change the value of the Pos property of the sizer which contains the buttons, and set its value to 0, to move the sizer before the text area: easy, isn't it?Now our window is complete: all we have to do is set a reasonable initial size for it. To do this, resize the frame until you find a good size, and then activate the Size property.
8. Code generation
Before we proceed with the code generation, let's save our work, selecting the Save As... item of the File menu of the main wxGlade window: this operation is not strictly necessary, but it's a good practice to not risk to loose the work, in particular until wxGlade will reach a certain maturity :-)Now we can go on with the code generation: select the Application item on the tree of widgets (it is the root) to make the Application tab appear on the properties window. This panel contains the options for the code generation:
- Name: name to give to the wxApp object which represents the application: if this property and the next one (Class) are not active, there will be no code for the application startup, but only that of the various widgets
- Class: name of the class derived from wxApp which represents the application: if this property is not active but the previous one (Name) is, the application object will be an instance of wxPySimpleApp (this applies to Python output only - for C++ output this property must be active if you want the startup code to be generated)
- Encoding: encoding used to store the saved .wxg file (also for XRC);
- Enable gettext support: if checked, all the strings in the generated sources will be wrapped by a "_()", ready for gettext;
- Top window: main window of the application to generate
- Code generation: this controls the kind of output, and lets you choose between a single source file containing all the widgets, or a separate file for each custom class defined (for C++ output, the single-file mode actually generates two files, a ".h" and a ".cpp")
- Language: this lets you choose the language of the generated code: at the moment, Python, C++ and XRC, i.e. wxWindows resources xml format. Note that in this last case some of the properties of the application are ignored (Name, Class, Top window), and some are disallowed (you cannot set Code generation to multi-files)
- Overwrite existing sources: if checked, the code will be completely re-generated instead of updated (see the first note below for details);
- Output path: in single-file mode, name of the output file; in multi-file mode, path to the output directory: in this last case, every custom class will be placed in a file with the name of such class, except for the (eventual) wxApp class, which is placed in a file whose name is given by the Name property described above. For example, for our notebook, we'll have MyFrame.py, MyNotebook.py and app.py (assuming you're generating Python code, of course).
- Generate code: button which starts the code generation
After the selection of the various options as described above, click on the Generate code button: if everything is OK, after a while a message box appears: this says that the operation is over... like this short tutorial ;-)
9. Notes
This section contains a list of things that you should know about wxGlade (known bugs and/or limitations, "hidden" features, tips and tricks, ...) which I wasn't able to fit in the tutorial . The list is loosely sorted by importance.
- When you generate Python or C++ code, if the output file already exists, wxGlade by default doesn't overwrite all its contents, but only the lines inside a
# begin wxGlade: ... # end wxGlade
block. This is a desirable feature in most occasions, since it lets you add your code to the file without worrying of losing it when re-generating the GUI code, but there are situations in which a little attention is required. In particular, you should be aware of the following:- If the output file contains a class whose name is the same as that of one of your custom classes, but its body has no wxGlade block, the code for that class is not generated (a warning appears on the shell);
- If you rename one of your custom classes, you should rename also its code in the output file (and also all the occurrences of such name in the wxGlade tags), because wxGlade has no way of determining the previous name of such class, and will treat it like a brand new one (this means that it will generate a new class declaration instead of updating the old one). Let me explain this with an example:
Suppose you have a class called MyFrame, and the corresponding generated file frame.py:
#!/usr/bin/env python # generated by wxGlade 0.2 on Sat Dec 14 15:15:06 2002 from wxPython.wx import * class MyFrame(wxFrame): def __init__(self, *args, **kwds): # begin wxGlade: MyFrame.__init__ kwds["style"] = wxDEFAULT_FRAME_STYLE wxFrame.__init__(self, *args, **kwds) self.__set_properties() self.__do_layout() # end wxGlade def __set_properties(self): # begin wxGlade: MyFrame.__set_properties self.SetTitle("frame_1") # end wxGlade def __do_layout(self): # begin wxGlade: MyFrame.__do_layout pass # end wxGlade # end of class MyFrame
Now suppose you rename MyFrame to RenamedFrame. If you don't care to fix frame.py accordingly, if you re-generate it you will get something like:
#!/usr/bin/env python # generated by wxGlade 0.2 on Sat Dec 14 15:15:06 2002 from wxPython.wx import * class RenamedFrame(wxFrame): def __init__(self, *args, **kwds): # begin wxGlade: RenamedFrame.__init__ kwds["style"] = wxDEFAULT_FRAME_STYLE wxFrame.__init__(self, *args, **kwds) self.__set_properties() self.__do_layout() # end wxGlade def __set_properties(self): # begin wxGlade: RenamedFrame.__set_properties self.SetTitle("frame_1") # end wxGlade def __do_layout(self): # begin wxGlade: RenamedFrame.__do_layout pass # end wxGlade # end of class RenamedFrame class MyFrame(wxFrame): def __init__(self, *args, **kwds): # content of this block not found: did you rename this class? pass def __set_properties(self): # content of this block not found: did you rename this class? pass def __do_layout(self): # content of this block not found: did you rename this class? pass # end of class MyFrame
which is clearly not what you intended.
- If you remove a custom class from the wxg file, wxGlade won't automatically remove it from the source when it is re-generated (this does not apply if there is no old version of such source), but it will try to update the parts inside wxGlade blocks nonetheless: this means that:
- If you want to remove the class, you have to do it manually,
- If you want to keep the class as is, you have to remove the wxGlade tags.
As of version 0.3, it is possible to turn off this "update contents" feature, by checking the "Overwrite existing sources" property of the Application: if the property value is True, wxGlade will always re-generate the code from scratch (performing the appropriate backups, according to the preferences you can set from View->Preferences->Other).
- Some widgets are not supported at all (e.g. status bar and grid): for them no code will be generated, but instead the XRC output file will contain a comment like this:
- Output files are always overwritten, so if you manually edit the XRC file, all the changes you made will be lost when you re-generate it.
name: value(invalid entries will be silently ignored): for each of these lines, the output will contain a
property of the XRC object.value
python xrc2wxg.py xrc_file.xrc wxg_file.wxg(if wxg_file.wxg is omitted, it defaults to xrc_file.wxg), but there are some limitations you should be aware of:
- First of all, it can handle correctly only "wxGlade-friendly" XRC files. This basically means that all windows but the toplevel ones must be inside a sizer (but there are other cases).
- All the widgets unknown to wxGlade will be replaced by the special CustomWidget component.
- Finally, xrc2wxg is very experimental, and so it probably contains many bugs. If you find one of them, please report it (this is valid for wxGlade in general, BTW).
python wxglade.py -hat your shell's prompt.
I hope the contents are clear (and my English not too ridicule), anyway for questions, comments, critics you can reach me by e-mail at [agriggio users