the elj projectOpen Source Eiffel Libraries and Applications(SmartEiffel and ISE Eiffel) |
wxEiffelIntroductionThis is the main page of wxEiffel, the Eiffel wrapper for the wxWindows library. The work on this project has been started after we decided that it is too much work to develop and maintain a pure Eiffel platform independent GUI library. We started to look for a ready to use and easy to wrap C or C++ library for Windows and Linux and finally decided that wxWindows was the way to go. We are still happy with this decision. wxEiffel is only a part of the ELJ project. We provide a couple of libraries and wrappers to develop, among other types of apps, databased GUI applications with the SmartEiffel and ISE Eiffel. Contact InfoThe core developers are:
PrerequisitesYou can download the latest version of the ELJ library package here. Download and install the package of your choice and you should be ready to go. You must have:
TutorialLet's dive into it: here comes a little step by step tutorial which shows various aspects of the programming with wxEiffel. A minimal applicationWe create an application here which does nothing except for creating and diplaying an empty frame.
class TUTORIAL inherit WX_DEFS creation make feature {NONE} make is local app: WX_APP do create app.make(agent initialize) end -- make feature {NONE} initialize (a_app: WX_APP): BOOLEAN is do -- create a frame, -1 is a synonym for 'default' create frame.make (Void, -1, "Hello Eiffel world", -1, -1, -1, -1, wxDEFAULT_FRAME_STYLE) -- make the frame visible ... frame.show -- ... and declare it as the 'main' frame a_app.set_top_window (frame) -- return False if you don't want the application to start Result := True end -- initialize frame: WX_FRAME end -- class TUTORIALSo, what's remarkable here? Every wxEiffel application needs an instance of the WX_APP class created and initialized. The initialization routine must be passed as an agent to the WX_APP object and must return TRUE to let the message dispatcher loop start its task. If you let the routine return FALSE, the application control flow reaches the point after 'create app' immediately, otherwise the application runs until the top level frame is closed and then reaches the point after creating the application object. A standard WX_FRAME is not visible unless you show it with the 'show' routine of the WX_WINDOW class and no window is the top window unless you tell the application object which one is the chosen one. Adding widgetsNow let's add a few widgets to our frame.
... create frame.make (Void, -1, "Hello Eiffel world", -1, -1, -1, -1, wxDEFAULT_FRAME_STYLE) -- add the following two lines create text.make (frame, -1, "edit field", 1, 1, -1, -1, 0B) create button.make (frame, -1, "Button", 1, 30, -1, -1, 0B) ... ... frame: WX_FRAME -- add the following two lines text: WX_TEXT_CONTROL button: WX_BUTTON ...Nothing really exciting here: we have declared and added two child widgets, one of type WX_TEXT_CONTROL, one of type WX_BUTTON. The only special thing here is that child widgets must have a parent, our 'frame' here. Once your application grows and your frames get more and more complex, it would be a good idea to create descendents of WX_FRAME, redefine their creation routine and create the children within that new routine. Events in wxEiffelUnlike preogramming with wxWindows in C++ we cannot use macros, so here is an overview of how event driven coding is done in wxEiffel.
... create button.make (frame, -1, "Button", 1, 30, -1, -1, 0B) button.connect (-1, -1, wxEVT_COMMAND_BUTTON_CLICKED, agent on_button_click) ... a_app.set_top_window (frame) frame.connect (-1, -1, wxEVT_CLOSE_WINDOW, agent on_close_window) ... on_button_click (a_event: POINTER) is do text.set_label ("Clicked!") end -- on_button_click on_close_window (a_event: POINTER) is local evt: expanded WX_EVENT do print ("Bye!") evt.skip (a_event) end -- on_close_window ...From the snippets above you can see that we are connecting events dynamically instead of binding handler routines at compile time. Here is an explanation of the params given to the 'connect' call: The first and second params describe the range of IDs this connection will be esatblished with. By giving -1, we declare that we want all event IDs and no subrange. This is important for menu events for example. These menu events are handled by the frame the menu belongs to and the frame has the possibility to define several event routines depending on specific ranges of menu IDs. Next is the event constant describing what kind of event we want to connect to and last the agent which will be executed in the case of a event. The agent routines look always the same, independent of what specific event they handle: they take at least one argument, a pointer, which is a handle to the C++ event object. To access the routines of this object, you declare a local Eiffel event object and use the C++ handle as parameter for every routine call of the Eiffel object. What does 'skip' mean? In short words: skip advises the wxEiffel runtime system to proceed with the default event handling for this event. Removing the call to skip in the 'on_close_window' rouine would result in a non closable frame, because not calling skip supresses the normal 'on_close' behaviour of a frame which is the destroying of the frame. You can also disconnect a event from a WX_WINDOW, take a look at the short for of WX_EVENT_HANDLER to learn more about this topic. There is one event connected by every descendent of WX_WINDOW: 'on_destroy' This event handler does a lot of important clean up work in the Eiffel code, so if you need to connect this event for your own purposes, take a look into the code of the WX_WINDOW class and call the appropriate clean up routines on your own. You may also want to redefine the event handler itself and simply call 'precursor'. Working with sizersThe following introduction to sizers has been stolen from the original wxWindows docs:The layout algorithm used by sizers in wxWindows is closely related to layout in other GUI toolkits, such as Java's AWT, the GTK toolkit or the Qt toolkit. It is based upon the idea of the individual subwindows reporting their minimal required size and their ability to get stretched if the size of the parent window has changed. This will most often mean, that the programmer does not set the original size of a dialog in the beginning, rather the dialog will assigned a sizer and this sizer will be queried about the recommended size. The sizer in turn will query its children, which can be normal windows, empty space or other sizers, so that a hierarchy of sizers can be constructed. Note that wxSizer does not derive from wxWindow and thus do not interfere with tab ordering and requires very little resources compared to a real window on screen. What makes sizers so well fitted for use in wxWindows is the fact that every control reports its own minimal size and the algorithm can handle differences in font sizes or different window (dialog item) sizes on different platforms without problems. If e.g. the standard font as well as the overall design of Motif widgets requires more space than on Windows, the initial dialog size will automatically be bigger on Motif than on Windows. And here comes Eiffel:
.. local box: WX_BOX_SIZER .. create button.make (frame, -1, "Button", -1, -1, -1, -1, 0B) create box.make (wxVERTICAL) box.append_window (text, False, wxEXPAND, 0) box.append_window (button, True, wxEXPAND | wxTOP, 5) frame.set_auto_layout (True) frame.set_sizer (box) box.set_size_hints (frame) box.fit (frame) ...First of all we have to create a sizer, in this case we choose a simple box style sizer with a vertical layout, next is to add the widgets to the sizer. The arguments to 'append_window' have the following meaning: the first one is a reference to a WX_WINDOW; the second one controls wether the WX_WINDOW will be stretched in both directions (True) or only in opposite layout direction if the third argument contains the wxEXPAND flag. The last argument describes the distance in points to the neighbours at the edges mentioned in the third argument, the top edge in our example for the button. In a last step we have to tell the frame to use the auto layout mechanism and which sizer to use. The sizer needs the advise to calculate the appropriate sizes and to make use of the calculation results. That's all. Sizers can be nested, so you can create real complicated layouts with them and I strongly recommend that you take the time and play a little around with this example. Remember that -1 denotes default values and note that we do not need to st the position of widgets explicitly once we start to use sizers. More FramesOne frame is not enough for most applications, so we will add another one in this section.In class TUTORIAL we change the button click implementation: ... local my_frame: MY_FRAME do create my_frame.make (frame) end -- on_button_click ... And we add a new class to our sample: class MY_FRAME inherit WX_DEFS WX_FRAME rename make as make_frame end creation make feature {NONE} make (a_parent: WX_FRAME) is local box: WX_BOX_SIZER do make_frame (a_parent, -1, "Child frame", -1, -1, -1, -1, wxDEFAULT_FRAME_STYLE) create text.make (Current, -1, "edit field", -1, -1, -1, -1, 0B) create button.make (Current, -1, "Button", -1, -1, -1, -1, 0B) create box.make (wxVERTICAL) box.append_window (text, False, wxEXPAND, 0) box.append_window (button, True, wxEXPAND | wxTOP, 5) set_auto_layout (True) set_sizer (box) box.set_size_hints (Current) box.fit (Current) show end -- make text: WX_TEXT_CONTROL button: WX_BUTTON end -- class MY_FRAMEYou can see here how easy it is to derive your own frame class from the stock one. Please note that you can close every single child frame, but once you are closing the 'main' frame, all children disappear and the application terminates. The reasón for this behaviour is, that we have given the main frame as the 'parent' argument to the childs. If we ommit this argument and pass 'Void' as parent, the application will not terminate until the last frame has been closed manually by the user. Message dialogsInforming the user - how to use message dialogs.
class MY_FRAME ... on_button_click (a_event: POINTER) is local dlg: WX_MESSAGE_DIALOG do create dlg.make (Current, "Message text", "Message title", wxICON_INFORMATION | wxOK | wxCENTRE) dlg.show_modal end -- on_button_clickThis task is quite simple: we create an appropriate message dialog and display it with the call 'show_modal'. Our runtime system will care for cleaning up the allocated memory. Please note that the flag wxCENTRE has no meaning under Windows. Other dialogsDisplaying and destroying of self made dialog boxes.We change MY_FRAME as follows: class MY_FRAME ... on_button_click (a_event: POINTER) is local dlg: MY_DIALOG do create dlg.make (Current) dlg.show_modal dlg.destroy end -- on_button_click ... and add the following class: class MY_DIALOG inherit WX_DEFS WX_DIALOG rename make as make_dialog end creation make feature {NONE} make (a_parent: WX_FRAME) is local box: WX_BOX_SIZER do make_dialog (a_parent, -1, "Dialog", -1, -1, -1, -1, wxSYSTEM_MENU | wxCAPTION | wxCENTRE) create text.make (Current, -1, "Do you agree?", -1, -1, -1, -1, 0B) create button.make (Current, wxID_OK, "Yes", -1, -1, -1, -1, 0B) create box.make (wxVERTICAL) box.append_window (text, False, wxEXPAND, 0) box.append_window (button, True, wxEXPAND | wxTOP, 5) set_auto_layout (True) set_sizer (box) box.set_size_hints (Current) box.fit (Current) show end -- make text: WX_STATIC_TEXT button: WX_BUTTON end -- class MY_DIALOGVery similar to MY_FRAME, isn't it? If you let this example run, you will notice that a click on the button closes the dialog. This happens whenever you define a button with the IDs wxID_OK or wxID_CANCEL. You can check which button has closed the dialog with the query 'modal_result' of class WX_DIALOG. Please note also that we had to add the call 'destroy' to our class MY_FRAME, because the wxEiffel runtime system does not delete dialogs automatically. If all frames of your app have been closed and the app still denies to terminate, you should check for dialogs you have forgotten to destroy. More Info
This is the end of our introduction into the programming with wxEiffel. For further informations, you should take a
deep look into the original wxWindows documentation (you will find that wxEiffel is a very thin wrapper) and also
carefully check out the examples which come with wxEiffel. Class status
The following table shows the status of every wrapper for the corresponding wxWindows class.
|
|
``.. in open source, software lives on if there are enough believers to keep it alive ..'' (WSJ - 20 Jul 2003) |
http://elj.sourceforge.net/docs/wxEiffel
Dec 04, 2003, 00:26 UTC