苦瓜的做法怎么做不苦:wxGlade: a GUI builder for wxWidgets/wxPython

来源:百度文库 编辑:九乡新闻网 时间:2024/04/26 03:37:27

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" (): a dialog asking for the name of the class of the new window is displayed: choose the name you prefer, but remember that it must be a valid name for a Python (or C++) class.

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 (). This is because with wxGlade every container widget must have a Sizer before you can add sub-widgets to it, and since it is very common for frames to have just one child window, wxGlade adds an 1-slot BoxSizer automatically for you. This doesn't happen for other containers, for which you'll have to set their sizer manually, as will be explained below, after the addition of the notebook to the frame.

Let's add such notebook, then: to do so, click on the Notebook button () on the palette: now when you move the mouse inside the sizer's area (), the pointer changes its shape and becomes a cross: this tells us that we can drop the notebook on the sizer's only slot.
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 of
button_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 () on the palette, and then move the mouse inside the page. Again, the pointer is now a cross, and so we can drop the sizer on our page: if everything is OK, you should see a dialog asking for some features of the sizer to add. We have to change the default values, since we want a 2-slots vertical box, so set to Vertical the orientation and to 2 the number of slots. None of the two actions is mandatory, since both the orientation (or better, the type) of the sizer and the number of slots can be changed in any moment: the former by changing the value of the Class property, the latter from the popup menu you can activate with a right-click on the "handle" button of the sizer or on the corresponding node of the tree of widgets. (As a side note, this same procedure can be used to show the menu of almost every object handled by wxGlade).

6.1 Text area

Let's click on the TextCtrl button () and insert the text area in the first slot of our sizer.

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 () of the appropriate dimension between the buttons, to keep them at a reasonable distance.

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).

  • When you add new custom classes to an app, if there is a previous version of the generated code, the definitions of the new classes will be inserted *before* the old ones: this usually is not a problem, but for C++ output there could be occasions in which the generated code won't compile "out of the box", but you'll have to perform some manual adjustments (e.g. add some forward declarations before the new classes).
     
  • When you add slots to sizers, it may be possible that you don't see such new slots: this happens because the size of the window that contains the sizer is too small, and so new slots are hidden. The solution is to resize the window, so that its layout is recalculated and the new slots are shown.
     
  • XRC code generation has some differences wrt Python or C++; apart from those already mentioned in section 8, the most important ones are:
    • 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.

     
  • Starting from version 0.2, there's a special component called CustomWidget (). This is different from the others because it can be used to include in a wxg file any widget, even if it is not directly supported by wxGlade. The key feature for this is the Arguments property, which has two different meanings: for Python or C++ output, it lets you define the constructor parameters for the object. In this case it has two special parameters, $parent and $id, which as you can guess by their names are placeholders for the actual parent and id of the object. For XRC output, instead, it lets you specify additional properties of the object. In this case each entry should be in the form
    name: value
    (invalid entries will be silently ignored): for each of these lines, the output will contain a
    value
    property of the XRC object.
     
  • Starting from version 0.2, there's a script called xrc2wxg.py shipped with wxGlade. You can use this script to convert an XRC file to a wxGlade resource, so that you can edit it with wxGlade itself. Its usage is straightforward:
    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).
       
  • You can invoke code generation also from the command line, without starting the GUI. For the details, type
    python wxglade.py -h
    at 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 sf net].