the elj project

Open Source Eiffel Libraries and Applications

(SmartEiffel and ISE Eiffel)


wxEiffel

wxEiffel

Introduction

This 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 Info

The core developers are:

You can find the ELJ project page here.

Prerequisites

You 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:

  • The wxEiffel dll / shared object reachable for your app and compiler.
  • Your Eiffel environment fully functional.
  • The GOBO build tools geant and gexace properly installed.

Tutorial

Let's dive into it: here comes a little step by step tutorial which shows various aspects of the programming with wxEiffel.

A minimal application

We 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 widgets

Now 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 wxEiffel

Unlike 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 sizers

The 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 Frames

One 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 dialogs

Informing 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 dialogs

Displaying 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.
Have a lot of fun!

Class status

The following table shows the status of every wrapper for the corresponding wxWindows class.
C = complete
P = partial
N = not done

wxWindows class Status of wxEiffel wrapper
wxAcceleratorEntry C
wxAcceleratorTable C
wxActivateEvent C
wxApp P
wxArray N
wxArrayString N
wxArtProvider C
wxAutomationObject N
wxBitmap C
wxBitmapButton C
wxBitmapDataObject C
wxBitmapHandler N
wxBoxSizer C
wxBrush C
wxBrushList N
wxBufferedInputStream N
wxBufferedOutputStream N
wxBusyCursor C
wxBusyInfo C
wxButton C
wxCalculateLayoutEvent C
wxCalendarCtrl C
wxCalendarDateAttr C
wxCalendarEvent C
wxCaret C
wxCheckBox C
wxCheckListBox C
wxChoice C
wxClassInfo P
wxClient N
wxClientDC C
wxClientData N
wxClientDataContainer N
wxClipboard C
wxCloseEvent C
wxCmdLineParser N
wxColour C
wxColourData C
wxColourDatabase P
wxColourDialog C
wxComboBox C
wxCommand N
wxCommandEvent C
wxCommandProcessor N
wxCondition C
wxConfigBase C
wxConnection N
wxContextHelp C
wxContextHelpButton C
wxControl C
wxCountingOutputStream N
wxCriticalSection C
wxCriticalSectionLocker N
wxCSConv N
wxCursor C
wxCustomDataObject P
wxDatabase N
wxDataFormat C
wxDataInputStream N
wxDataObject P
wxDataObjectComposite P
wxDataObjectSimple P
wxDataOutputStream N
wxDate N
wxDateSpan N
wxDateTime C
wxDateTimeHolidayAuthority N
wxDateTimeWorkDays N
wxDb N
wxDbColDataPtr N
wxDbColDef N
wxDbColFor N
wxDbColInf N
wxDbConnectInf N
wxDbIdxDef N
wxDbInf N
wxDbTable N
wxDbTableInf N
wxDC C
wxDCClipper N
wxDebugContext N
wxDebugStreamBuf N
wxDialog C
wxDialUpEvent C
wxDialUpManager C
wxDir N
wxDirDialog C
wxDirTraverser N
wxDllLoader P
wxDocChildFrame N
wxDocManager N
wxDocMDIChildFrame N
wxDocMDIParentFrame N
wxDocParentFrame N
wxDocTemplate N
wxDocument N
wxDragImage C
wxDropFilesEvent C
wxDropSource C
wxDropTarget C
wxDynamicLibrary P
wxEncodingConverter N
wxEraseEvent C
wxEvent C
wxEvtHandler C
wxExpr N
wxExprDatabase N
wxFFile N
wxFFileInputStream N
wxFFileOutputStream N
wxFFileStream N
wxFile N
wxFileDataObject C
wxFileDialog C
wxFileDropTarget C
wxFileHistory C
wxFileInputStream N
wxFileName N
wxFileOutputStream N
wxFileStream N
wxFileSystem N
wxFileSystemHandler N
wxFileType N
wxFilterInputStream N
wxFilterOutputStream N
wxFindDialogEvent C
wxFindReplaceData C
wxFindReplaceDialog C
wxFlexGridSizer C
wxFocusEvent C
wxFont C
wxFontData C
wxFontDialog C
wxFontEnumerator C
wxFontList N
wxFontMapper C
wxFrame C
wxFSFile N
wxFTP N
wxGauge C
wxGDIObject N
wxGenericDirCtrl N
wxGenericValidator N
wxGLCanvas N
wxGrid C
wxGridCellAttr N
wxGridCellBoolEditor N
wxGridCellChoiceEditor N
wxGridCellEditor P
wxGridCellFloatEditor N
wxGridCellNumberEditor N
wxGridCellTextEditor N
wxGridEditorCreatedEvent N
wxGridEvent C
wxGridRangeSelectEvent N
wxGridSizeEvent N
wxGridCellBoolRenderer N
wxGridCellFloatRenderer N
wxGridCellNumberRenderer N
wxGridCellRenderer N
wxGridCellStringRenderer N
wxGridTableBase P
wxGridSizer C
wxHashMap N
wxHashTable N
wxHelpController P
wxHelpControllerHelpProvider C
wxHelpEvent C
wxHelpProvider C
wxHtmlCell N
wxHtmlColourCell N
wxHtmlContainerCell N
wxHtmlDCRenderer N
wxHtmlEasyPrinting N
wxHtmlFilter N
wxHtmlHelpController C
wxHtmlHelpData N
wxHtmlHelpFrame N
wxHtmlLinkInfo N
wxHtmlParser N
wxHtmlPrintout N
wxHtmlTag N
wxHtmlTagHandler N
wxHtmlTagsModule N
wxHtmlWidgetCell N
wxHtmlWindow N
wxHtmlWinParser N
wxHtmlWinTagHandler N
wxHTTP N
wxIcon C
wxIconBundle C
wxIconizeEvent C
wxIdleEvent C
wxImage C
wxImageHandler N
wxImageList C
wxIndividualLayoutConstraint C
wxInitDialogEvent C
wxInputStream P
wxIPV4address N
wxJoystick C
wxJoystickEvent C
wxKeyEvent C
wxLayoutAlgorithm C
wxLayoutConstraints C
wxList N
wxListBox C
wxListCtrl C
wxListEvent C
wxListItem C
wxListView N
wxLocale C
wxLog C
wxLogChain C
wxLogGui C
wxLogNull N
wxLogPassThrough N
wxLogStderr N
wxLogStream N
wxLogTextCtrl N
wxLogWindow N
wxLongLong N
wxMask C
wxMaximizeEvent C
wxMBConv N
wxMBConvFile N
wxMBConvUTF7 N
wxMBConvUTF8 N
wxMDIChildFrame C
wxMDIClientWindow C
wxMDIParentFrame C
wxMemoryDC C
wxMemoryFSHandler N
wxMemoryInputStream N
wxMemoryOutputStream N
wxMenu C
wxMenuBar C
wxMenuEvent C
wxMenuItem C
wxMessageDialog C
wxMetafile C
wxMetafileDC C
wxMimeTypesManager C
wxMiniFrame C
wxModule N
wxMouseCaptureChangedEvent C
wxMouseEvent C
wxMoveEvent C
wxMultipleChoiceDialog N
wxMutex C
wxMutexLocker N
wxNode N
wxNotebook C
wxNotebookEvent C
wxNotebookSizer C
wxNotifyEvent C
wxObject P
wxObjectRefData N
wxOutputStream P
wxPageSetupDialog C
wxPageSetupDialogData C
wxPaintDC C
wxPaintEvent C
wxPalette C
wxPanel C
wxPathList N
wxPen C
wxPenList N
wxPlotCurve C
wxPlotWindow C
wxPoint P
wxPostScriptDC C
wxPreviewCanvas C
wxPreviewControlBar C
wxPreviewFrame C
wxPrintData C
wxPrintDialog C
wxPrintDialogData C
wxPrinter C
wxPrinterDC C
wxPrintout C
wxPrintPreview C
wxPrivateDropTarget N
wxProcess C
wxProcessEvent C
wxProgressDialog C
wxProtocol N
wxQuantize N
wxQueryCol N
wxQueryField N
wxQueryLayoutInfoEvent C
wxRadioBox C
wxRadioButton C
wxRealPoint N
wxRecordSet N
wxRect P
wxRegEx N
wxRegion C
wxRegionIterator C
wxSashEvent C
wxSashLayoutWindow C
wxSashWindow C
wxScreenDC C
wxScopedArray N
wxScopedPtr N
wxScrollBar C
wxScrolledWindow C
wxScrollEvent C
wxScrollWinEvent C
wxSemaphore N
wxServer P
wxSimpleHelpProvider P
wxSingleChoiceDialog N
wxSingleInstanceChecker C
wxSize P
wxSizeEvent C
wxSizer C
wxSlider C
wxSockAddress N
wxSocketBase N
wxSocketClient N
wxSocketEvent N
wxSocketInputStream N
wxSocketOutputStream N
wxSocketServer N
wxSpinButton C
wxSpinCtrl C
wxSpinEvent C
wxSplashScreen C
wxSplitterEvent C
wxSplitterWindow C
wxStaticBitmap C
wxStaticBox C
wxStaticBoxSizer C
wxStaticLine C
wxStaticText C
wxStatusBar C
wxStopWatch C
wxStreamBase N
wxStreamBuffer N
wxStreamToTextRedirector N
wxString N
wxStringBuffer N
wxStringClientData N
wxStringList N
wxStringTokenizer N
wxSysColourChangedEvent C
wxSystemOptions C
wxSystemSettings P
wxTabCtrl N
wxTabEvent N
wxTaskBarIcon N
wxTempFile N
wxTextAttr N
wxTextCtrl C
wxTextDataObject C
wxTextDropTarget C
wxTextEntryDialog N
wxTextFile N
wxTextInputStream N
wxTextOutputStream N
wxTextValidator N
wxThread C
wxTime N
wxTimer C
wxTimerEvent C
wxTimeSpan N
wxTipProvider C
wxTipWindow C
wxToggleButton C
wxToolBar C
wxToolTip C
wxTreeCtrl C
wxTreeEvent C
wxTreeItemData C
wxTreeLayout N
wxTreeLayoutStored N
wxUpdateUIEvent C
wxURL N
wxValidator N
wxVariant N
wxVariantData N
wxView N
wxWave N
wxWindow C
wxWindowDC C
wxWindowDisabler N
wxWizard C
wxWizardEvent C
wxWizardPage C
wxWizardPageSimple P
wxXmlResource C
wxXmlResourceHandler C
wxZipInputStream N
wxZlibInputStream N
wxZlibOutputStream N

elj Project Quick Links
Project Status/Warning (25 July 2003)
Official Downloads:
0.7 Release (20 Jun 03) (announce getting started docs)
Docs: Tutorial (pdf), Style (pdf) Syntax etl3
wxEiffel: wxWindows (ver:2.4.1) (cvs, wxWindows, lists: users dev, news *, wx-c)
Common Classes (cvs)
elj-db: elj database interface
elj FireBird interface (cvs, lists)
elj sqlite (cvs, home, list, news, SQL)
elj SleepyCat interface (cvs, news)
elj mysql interface) (cvs)
elj Postgres interface (cvs, news)
elj btrieve (cvs news)
compression: zlib bzip2/bzlib
elj Lua interface (cvs home list news)
elj libxml2 interface (cvs eg list news)
os: elj win32api interface
Related/Interesting
Eiffel Struggle 2003 (NICE cetus-links ceug)
ewg (Eiffel<->C) (home cvs news)
ePDF (PDF) (home cvs news)
yaesockets (home cvs news)
nenie Eiffel (xml,networking,snmp,DES,..) - (homepage cvs)
Goanna (web services) - (cvs)
mico/E (open source Eiffel CORBA services )
ePosix: posix interface (news)
EiffelOpenGL (sf cvs)
elj-girls: our first application
diuturnal: multiplayer turn-based strategy game (home cvs)
elj Perlish -(cvs examples Perl)
elj Reg Exp -(cvs PCRE)

elj Dependency Links
SmartEiffel (GNU Eiffel Compiler FAQ) - (beta bugs list news *)
ISE Eiffel (downloads) - (list cle faqs)
GOBO Project (lists: dev user commits cvs geant * gexace)
elj Mailing List: (elj-devel elj-users elj-commits)

elj hosted and supported by SourceForge

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