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 TUTORIAL
So, 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_FRAME
You 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_click
This 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_DIALOG
Very 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