Programmation dans Anatomist: fabriquer un nouveau type de fenêtre


Regardez toujours la doc automatique en meme temps... (et les sources des classes que je donne en exemple, ça peut aider
aussi)

Les fenêtres dérivent de la classe de base AWindow, qui elle-même est un observateur (une fenêtre observe les objets qui sont affichés dedans).
On va prendre l'exemple d'une nouvelle fenêtre: ABubu.
Le ménage n'ayant pas encore été fait dans les classes de fenêtres, il y a encore pas mal de fonctions virtuelles pures et de choses à redéfinir. C'est un peu plus complexe que pour les objets qui ont été un tout petit peu nettoyés.
Il n'y a pas non plus d'exemple très simple de fenêtre dans la librairie anatomist, regardez quand-même QObjectBrowser, c'est la plus récente et donc la plus propre (et aussi, elle ne fait pas de visu donc ça simplifie la présenration de la partie générique sans visu).

class ABubu : public AWindow
{
public:
  ABubu( params quelconques... );
  virtual ~ABubu();
  static AWindow* createBubu();
  virtual Type type() const;
  virtual Point3df GetPosition() const;
  virtual void SetPosition( const Point3df& position , const Referential *refdep );
  virtual void SetTime( float time );
  virtual void DisplayClickPoint();
  virtual void setGeometry( int x, int y, unsigned w, unsigned h );
  virtual void geometry( int *x, int *y, unsigned *w, unsigned *h );
  virtual void CreatePixMap();     // celle-là devrait disparaître, je ne vois pas ce qu'elle fout ici...
  virtual void Refresh();
protected:
  virtual void CreateTitle();
  virtual void Draw( bool flush = true );
private:
  static int registerClass();
  static set<unsigned>    _bubuCount;
  static string           _baseTitle;
  static int              _classType;
};
Je n'ai mis ici que les fonctions virtuelles pures à redéfinir obligatoirement, mais il y en a plein d'autres dont la redéfinition est plus que fortement conseillée. En fait un grand nombre de ces fonctions à redéfinir vient du fait que l'interface est indépendante de la librairie graphique utilisée (Motif/MPP ou Qt) et qu'il faut surcharger en fonction (resizes, géométrie, iconification, titre de la fenêtre etc). Pour bien faire, il faudrait interposer une seconde couche Motif ou Qt (AQtWindow et AMotifWindow par ex.). C'est un peu ce qui est fait du côté Motif/Mpp (AMainWindow / AMenuWindow), et que j'ai pas fait du côté Qt (mea culpa).
 

Résumé

Fonctions spécifiques à la librairie graphique (géométrie et look de la fenêtre)
Enregistrement dynamique du type et création
Comptage des fenêtres de ce type
Construction et destruction
Affichage dans la fenêtre
 

Fonctions spécifiques à la librairie graphique (géométrie et look de la fenêtre)

Pour ces fonctions, regarder comment c'est fait dans QObjectBrowser (browser/qwObjectBrowser.cc) en Qt.

setGeometry() positionne et dimentionne la fenêtre sur l'écran (QWidget::setGeometry() en Qt)
geometry()au contraire, retourne la position et les dimentions de la fenêtre
show()affiche et désiconifie la fenêtre. Il faut toujours rappeler la fonction AWindow::show(). En Qt:

void ABubu::show()
{
  QWidget::show();
  AWindow::show();
}
Unmanage() iconifie la fenêtre. Pareil que pour Manage(). En Qt:
void ABubu::hide()
{
  AWindow::hide();
  QWidget::hide();
}
SetTitle() met la ligne de titre de la fenêtre (window manager). Il faut aussi appeler AWindow::SetTitle() avant. En Qt:
void ABubu::SetTitle( const string title )
{
  AWindow::SetTitle( title );

  setCaption( title.c_str() );
}

Enregistrement dynamique du type et création

Comme pour les objets, le type de fenêtre doit être demandé et déclaré dans une usine à fenêtre (AWindowFactory). On utilise aussi l'initialisation d'une variable statique pour effectuer cet enregistrement (à l'initialisation de la librairie, donc):
 
string  ABubu::_baseTitle = "Bubu";    // titre de base pour les fenêtres de ce type
int     ABubu::_classType = registerClass();
int ABubu::registerClass()
{
  int type = AWindowFactory::registerType( _baseTitle, createBubu );

  return( type );
}
createBubu() est la fonction qui est appelée par l'usine à fenêtres et qui crée une fenêtre à partir de l'interface graphique (bouton):
AWindow* QABubu::createBubu()
{
  ABubu* b = new ABubu( params... );

  b->Manage();
  return( b );
}
Nouveau (01/10/00): maintenant on peut aussi stocker les pixmaps et des descriptifs dans une extension de l'usine à fenêtres: QAWindowFactory. Les pixmaps en 3 versions (small, large, active) sont stockés ici. Ils sont chargés automatiquement à la création de la fenêtre de contrôle -rien besoin de faire !- s'ils sont stockés à l'emplacement standard avec des noms standards: dependencies/icons/window-type-small.xpm, dependencies/icons/window-type-large.xpm, dependencies/icons/window-type-active.xpm.
On n'a donc plus besoin de charger les icônes "à la main", et surtout plus besoin de les déclarer dans la class QWindowTree: cette classe ne devrait même pas être connue à l'extérieur de ControlWindow (c'est un peu une classe privéé) et c'est maintenant possible.
Pour ajouter un descriptif à un type de fenêtre Bubu:
QAWindowFactory::setDescription( "Bubu", QAWindowFactoty::Descrip( "description brève", 
                                 "c'est une nouvelle fenêtre. Elle s'appelle Bubu. "
                                 "C'est un joli nom, non ?" ) );
La description brève est mise par défaut ("Fenêtre de type Bubu") si on ne met rien.
Même mécanisme pour les icônes avec setPixmaps() et QAWindowFactory::PixList (voir la doc automatique).
Cette cuisine doit être faite à l'initialisation, dans registerClass() par exemple.

Comptage des fenêtres de ce type

_bubuCount est une liste statique des numéros affectés à chaque fenêtre de ce type particulier. Cette variable doit donc être redéfinie pour chaque nouveau type de fenêtre (-> pas automatisable). Elle sert à donner un numéro à chacune ("Axial", "Axial(2)", etc). Le compteur est géré par la fonction CreateTitle().
void ABubu::CreateTitle()
{
  if( _instNumber >= 0 ) return;        // already done

  set<unsigned>::iterator       si;
  unsigned                      n;
  char                          nstr[20];

  for( si=_bubuCount.begin(), n=0; si!=_bubuCount.end() && *si==n; 
       ++si, ++n );
  _instNumber = n;
  _bubuCount.insert( n );
  sprintf( nstr, "%d", n+1 );
  if( n == 0 ) SetTitle( _baseTitle );
  else SetTitle( _baseTitle + " (" + nstr + ")" );
}

Construction et destruction

Dans le constructeur, il faut:
  • Ben plus grand chose. En particulier, plus besoin d'enregistrer l'icône du type de fenêtre dans l'afficheur de la fenêtre de contrôle.
  • ABubu::ABubu() : AWindow()
    {
    }
    Le destructeur doit retirer le numéro de la fenêtre dans la liste statique des numéros utilisés:
    ABubu::~ABubu
    {
      if( _instNumber != -1 ) 
        _bubuCount.erase( _bubuCount.find( _instNumber ) );
    }
    N.B: Heu, n'oublions pas qu'en Qt, quand on ferme une fenêtre, par défaut elle n'est pas détruite. Pour que ce soit le cas, ne pas oublier de passer le flag WDestructiveClose au constructeur de QWidget.

    Affichage dans la fenêtre

    L'affichage des objets dans la fenêtre est effectué par la fonction Refresh(). On y met ce qu'on veut, par ex. les fenêtres 2D appellent les fonctions Update2D() des objets qu'elles contiennent, les fenêtres 3D appellent les fonctions Update3D() des objets, et les fenêtres Browser décrivent les objets sous forme d'arbres attribués.