[Wcurve-devel] Modifications to Document-View Architecture
jruscio
jruscio@vt.edu
Wed, 11 Sep 2002 16:52:31 -0400
All,
Here is a brief synopsis of the changes I logged into the tree last Sunday
night.
It is highly desirable that we specify a generic interface to be implemented
by all different view types. This will allow the application (WcurveApp class)
and document (WcurveDoc class) to treat all views as the generic type and be
unconcerned with the particular details of each one. This will also allow us
to add new view types into the system with very little churn to exsisting
code...and given the exploratory nature of this project it is likely that such
additions will be required.
In Java it would be extremely easy, we could just declare an abstract
interface, and declare that all view types implement it. As it stands however
we are using C++ and need to use some kind of multiple inheritance in order to
allow us to subclass existing QT widgets and also specify our own personal
interface.
Currently there are two concrete view types, PlotView and SequenceView.
PlotView derives from QGLWidget and SequenceView derives from QTextView.
QGLWidget and QTextView each in turn derive from QWidget. You have an
inheritance hierarchy similar to this:
QWidget
/\ /\
/ \
/ \
QGLWidget QTextView
/|\ /|\
| |
PlotView SequenceView
It would appear that at first glance we can use QWidget as our generic
interface, and indeed there are several QWidget operations that the
application needs access to (show(), setFocus(), isActiveWindow()..etc). The
problem however is that in order to implement the Observer pattern and other
functionality such as printing there are other operations that the generic
interface needs to implement. Ideally we would setup the following
architecture:
QWidget
/\ /|\ /\
-------/ | \-----------
| | |
| WcurveView |
QGLWidget /\ /\ QTextView
/|\ -----/ \------ /|\
| | | |
PlotView SequenceView
Each concrete type derives from its widget and the WcurveView interface. We
could access each type through its WcurveView interface and still have access
to the QWidget operations we need.
The problem with this however is that if you do not specify the inheritance as
virtual, C++ constructs 2 Qwidget objects everytime a concrete view is
created, one for WcurveView, and one for the QT widget we derive from
(QGLWidget or QTextView). The QWidget that is constructed for the WcurveView
has no useful feature (Its a grey box) but that is the one we would have
access to. TrollTech hasn't declared the inheritance in any of its specialized
QT Widgets as virtual so this isn't an option....what we are left with is the
following heirarchy:
QWidget
/\ /\
-------/ \-----------
| |
| WcurveView |
QGLWidget /\ /\ QTextView
/|\ -----/ \------ /|\
| | | |
PlotView SequenceView
There are now two issues left to solve:
1. How do we access the QWidget operations we need through the WcurveView
interface?
2.How do we take the Qwidget objects that the windowActivated() and other
similar signals produce and cast them to WcurveViews.
This is the final solution I arrived at...and if you can think of an
improvement, let me know...cause this still has kind of a funky smell to it.
Issue 1 solution
Declare all of the QWidget operations we need as pure virtual operations in
WcurveView.h and then implement them in the Concrete types as calls to the
corresponding operations in the QT Widget superclasses:
class WcurveView{
.
.
.
virtual void setCaption(QString) = 0;
virtual bool isActiveWindow() = 0;
virtual bool close() = 0;
virtual void showMaximized() = 0;
virtual void setFocus() = 0;
virtual void show() = 0;
virtual void installEventFilter ( const QObject * obj ) = 0;
.
.
.
}
class PlotView :public QGLWidget, public WcurveView {
.
.
.
void setCaption(QString str){QGLWidget::setCaption(str);}
bool isActiveWindow(){return QGLWidget::isActiveWindow();}
bool close(){return QGLWidget::close();}
void showMaximized(){QGLWidget::showMaximized();}
void setFocus(){QGLWidget::setFocus();}
void show(){QGLWidget::show();}
void installEventFilter ( const QObject * obj)
{QGLWidget::installEventFilter(obj);}
.
.
.
}
Issue 2 Solution
Implement a method resolveViewType() in WcurveApp that can take a Qwidget,
downcast it to the proper concrete type and then cast it back to a WcurveView:
/** changes all control settings to newly selected view */
void WcurveApp::slotUpdateControls(QWidget *w){
//do nothing if the window that gained focus isn't a good view
WcurveView *view = resolveViewType(w);
if(!view)
return;
//update the controls using *view
.
.
.
.
}
/** takes a Qwidget and performs the necessary hocus-pocus to turn it into a
WcurveView */
WcurveView* WcurveApp::resolveViewType(QWidget *view){
if(PlotView *plot = dynamic_cast<PlotView*>(view))
return (WcurveView *) plot;
else if(SequenceView *seq = dynamic_cast<SequenceView*>(view))
return (WcurveView *) seq;
else
return NULL;
}
Whew.....!
Well that is it in a nutshell. I still need to abstract out the fileview code
into its own class for us to truly minimize churn when adding a new view. I'm
doing that next. Once that is done I am going to add the ChaosView type...and
will record the LOC affected. The metric will hopefully be miniscule and I
will annouce it here.
L8r,
Joe Ruscio