General Design Issues
Here are three designs "templates" I considered for developing my
application, in order of preference:
- Developing an application with everything in a single address
space, without using EML.
- Developing an application that communicates with a Viewer through
a running Session Manager via pipes from a non-EML based application.
- Developing an application that communicates with a running Viewer
through a running Session Manager via pipes from an EML-based
application with calls to my C++ library via a C++ Facade or Adaptor
design pattern interface.
Note that, yes, there are other possible design configurations.
These are just the ones that I decided to iteratively try to use for
developing my
C++ application.
As you might infer from the design order, I wanted to avoid using
EML. EML hails
from the bad old days when cross-platform GUI development was very,
very hard. ERDAS had to make their applications easily portable
between UNIX and Windows based platforms. (And, once upon time,
even VAXen.) As the old computer
science adage goes, "most computer science problems can be solved by
adding a layer of indirection". EML was the layer of indirection
developed by ERDAS to solve the cross-platform GUI problem.
Theoretically, all you'd have to do to port your application from, say,
UNIX to Windows would be to copy the source to a suitable Windows
development environment from your UNIX environment, re-compile and
link, and your application would be ready for work in Windows.
However the bad ole days are over. There exist tools to make easy
writing portable GUI C++ applications, such as wxWindows
and Qt. I have a lot of
experience with Qt, and so preferred to use that to develop my
GUI. Not only did I have a choice of a number of powerful GUI
builders with Qt, but its system of signals and slots made it very easy
to write event-driven applications. Qt has also grown out of its
GUI origins as it now provides network and XML services, among
others.
I hoped to eschew EML in favor of Qt and have lower level Imagine
functions work inside of Qt widgets. During the Developer's
Toolkit course, I noted that EML was very brittle, in that that it was
trivial for EML-based applications to crash during the
edit-compile-test cycle. Moreover the diagnostics were obtuse, if
you got any at all. (It was possible for applications to crash
with no warning and leave no clue as to what happened in the session
log. If things really got wedged, you'd have to restart the
Session Manager.) Also, having to keep two separate files in sync
made it easier to create programming errors; variable and function
names had to be the same in both files, and so you'd have to watch that
when you changed the name in one file that you did so in the
other.
To me, it's much easier to develop and maintain everything in a single
language, and not to have to rely on source written in two languages.
First Whack: Single
Address Space Model
Initially I tried technique #1. For that, I noted that there
seemed to exist a means of creating a Viewer by
giving it a "handle" to a parent widget by calling this function:
extern Evue_DisplayHandle edis_CreateDisplayFromHandle __((
Evue_Connection *apc,
Emsc_Pointer parent,
Eui_BasePart *frame,
Eerr_ErrorReport **err
));
So I:
- initialized Qt
- initialized the Toolkit
- created a link to the Session Manager
- created a simple Qt dialog
- opened a Viewer connection via
evue_Open( "viewer",
*error_report )
- called
edis_CreateDisplayFromHandle()
Unfortunately this didn't work. This seemed to really want the
EML stuff initialized. When I tried to initialize the EML
library, it whined about not being able to find its "built-in DLLs" and
bailed.
So, on to application design technique #2.
Second Whack:
non-EML-based Application Communicating with Session Manager
I knew that Viewers communicated to other processes via pipes through a
Session Manager. All I'd have to do would be to send the right
messages back and forth through a Session Manager pipe to interact with
the Viewer. Simple, right?
Unfortunately all the documentation is oriented towards EML
applications, so the communication protocol isn't documented. You
can presumably infer it from reading header files and reverse
engineering traffic as message packets are marshalled and unmarshalled.
In any case, that didn't stop me from trying. So I:
- initialized Qt
- initialized the Toolkit
- created a link to the Session Manager via
esmg_SessionConnect(
argv[0], ESMG_SESSION_APPLICATION, *error_report );
- created a simple Qt dialog
- opened a Viewer connection via
evue_Open( "viewer",
*error_report )
- called
Evue_DisplayHandle display_handle =
evue_SelectViewer( imp_->viewerConnection.get(), "Select Viewer",
*error_report );
Progess! It actually did allow me to select a Viewer!
Unfortunately my application wedged. I had to manually kill the
application from the Session Manager.
I suspected, though, that there was probably some pipe related
housekeeping that I wasn't doing that was causing my application to
lock up after successfully calling evue_SelectViewer().
Further
research unveiled esmg_ProcessExitHandlerRegister().
I created an
empty process exit handler function and called
esmg_ProcessExitHandlerRegister() with
ESMG_PROCESS_EXIT_HANDLER_EACH_TIME. Success!
The function
call returned! Unfortunately it only seemed to successfully get a
handle on the viewer part of the time. Clearly I needed to do
more research to better understand the message passing protocol and
associated semantics.
Unfortunately I didn't have the time to do this as I need to get
something up and running ASAP. Still, I have high hopes that this
scheme will work. And, when it does, we can all throw off the EML
yoke and more quickly and easily write maintainable GUI apps that talk
to Imagine Viewers.
Third Whack:
Modifying a Traditional EML-based Application to Use C++ Wrapper Classes
So, I'm left with scheme #3. I've not given up on C++, though,
especially since the "FeatureSpace" library I need to use in my project
is already written in C++. Besides, using C++ is so much better
than ANSI C as with C++ memory management is no longer a
nightmare, and I have access to a powerful set of tools in the Standard
C++ library, and other libraries, such as the Boost C++ Library. In
subsequent sessions I share issues related to writing C++ applications
using the Toolkit and Toolkit related wrapper source code.
Note that I've developed a very basic C++ Imagine Wrapper Library
that's suitable for use with this implementation method.