Objektorientierte Programmierung mit C++ (WS 2010) Dr. Andreas F. Borchert, Tobias Brosch Institut für Angewandte Informationsverarbeitung Universität Ulm Blatt 11: Abgabetermin 19. Januar 2011 11 Uhr Mandelbrotmenge Interaktiv (Teil 1) Ziel dieses und der nächsten Übungsblätter ist es, die Mandelbrotmenge interaktiv darzustellen. Dabei werden wir unsere bereits kennengelernten Techniken vertiefen und nochmals üben. Model-View-Controller-Pattern View 1..* Controller 1 1 Model Ein häufig verwendetes Design-Pattern, insbesondere im Zusammenhang mit GUIs (Graphical User Interfaces), ist das MVC-Pattern. Es strukturiert den Code in drei Komponenten: 1. Model: Repräsentiert die Daten. 2. View: Visualisiert das Modell. 3. Controller: Stellt Methoden zur Manipulation des Modells bereit. Multiarray Im Übungsblatt zur Mandelbrotmenge, hatten wir ein Verfahren gesehen, welches uns zu jedem Bildpunkt einen Wert zwischen 0 und 1 bestimmt. Das Ergebnis dieser Berechnung, wollen wir zunächst in einem zweidimensionalen Array abspeichern, welches Teil unseres Modells sein wird. Hierzu verwenden wir die multi_array-klasse aus der boost-bibliothek. Für die Zwecke dieses Beispiels, werden folgende Methoden ausreichend sein: #include <boost / multi_array. hpp> int main ( ) { Institut für Angewandte Informationsverarbeitung 1
// i n i t i a l i z e 2 dimensional matrix o f type double // and s i z e 300 times 200 boost : : multi_array<double, 2> mat( boost : : e x t e n t s [ 3 0 0 ] [ 2 0 0 ] ) ; // can be r e s i z e d mat. r e s i z e ( boost : : e x t e n t s [ 1 0 0 ] [ 5 0 ] ) ; const std : : s i z e _ t dims = mat. shape ( ) ; int width = dims [ 0 ] ; int h e i g h t = dims [ 1 ] ; // a c c e s s and a s s i g n elements for ( int i = 0 ; i < width ; ++i ) for ( int j = 0 ; j < height ; ++j ) mat [ i ] [ j ] = 0 ; Model Unser Model wird durch die Klasse MBSet dargestellt. Dabei kapseln wir die Parameter dieser Klasse in ihrer inneren Klasse MBSetParams. QObject MBSet - _params: MBSetParams - _mat: boost::multi_array<double, 2> + MBSet(width = 400: int, height = 200: int, parent: QObject*) + updatemodel() signals: viewchanged(mat: const boost::multi_array<double, 2>*) MBSet::MBSetParams - _width: int - _height: int - _center_x = 0: double - _center_y = 0: double - _scale = 1./100: double - _maxiter: int + MBSetParams(width: int, height: int) + scr2world(x: double&, y: double&) + get_num_iter(x_pos: double, y_pos: double) + getwidth(): int + getheight(): int + getcenterx(): double + getcentery(): double + getscale(): double + int getmaxiter() Dabei implementiert MBSet::MBSetParams::get_num_iter die bekannte Mandelbrotiteration double MBSet : : get_num_iter ( double x_pos, double y_pos ) const { double x = 0 ; double y = 0 ; int maxiter = params. getmaxiter ( ) ; for ( int i = 0 ; i < maxiter ; ++i ) { Institut für Angewandte Informationsverarbeitung 2
double x_tmp = x x y y + x_pos ; y = 2 x y + y_pos ; x = x_tmp ; i f ( x x + y y > 32) { return std : : pow ( ( double ) i / maxiter, 0. 2 ) ; return 0 ; und MBSet::MBSetParams::scr2World rechnet Bildschirm- bzw. Array-Koordinaten in Zahlen der komplexen Ebene um. Dabei dienen die Parameter x und y sowohl als Ein- als auch als Ausgabeparameter. Beim Aufruf beinhalten die Variablen die Arraykoordinaten, nach Beendigung, die Koordinaten in der Welt der komplexen Ebene: void MBSet : : MBSetParams : : scr2world ( double &x, double &y ) const { x = ( x _width / 2) _scale + _center_x ; y = ( _height / 2 y ) _scale + _center_y ; Die Methode updatemodel verwendet diese beiden Methoden, um unser Array _mat mit den entsprechenden Werten unserer Mandelbrotiteration zu füllen. Die Methode viewchanged(...), definiert ein Qt-Signal. Diese muss nicht implementiert werden und wird analog zu private oder public-methoden mit dem Schlüsselwort signals gekennzeichnet. Damit der Meta-Object-Kompiler von Qt entsprechenden Code für den Signal- Slot-Mechanismus generiert, muss zusätzlich das Makro Q_OBJECT in die Klassendefinition mit aufgenommen werden: class MBSet : public QObject { Q_OBJECT class MBSetParams {... ; MBSet( int width = 400, int height = 200, QObject parent = 0 ) ; void updatemodel ( ) ; s i g n a l s : void viewchanged ( const boost : : multi_array<double, 2> mat ) ; private :... ; Am Ende der Methode updatemodel schickt Ihr das Signal mit emit viewchanged(&_mat); an alle verbundenen Slots. Institut für Angewandte Informationsverarbeitung 3
Aufgabe: Implementiert die Klasse MBSet und ihre innere Klasse MBSetParams wie im UML-Diagramm angegeben in den Dateien MBSet.h und MBSet.cpp. View QWidget MBViewer + MBViewer(parent = 0: QWidget*) public slots: + updateview(mat: const boost::multi_array<double, 2>*) Um den Signal-Slot-Mechanismus zu ermöglichen, muss hier ebenfalls das Makro Q_OBJECT mit in die Klassendefinition mit aufgenommen werden: #include <boost / multi_array. hpp> c l a s s MBViewer : public QLabel { Q_OBJECT MBViewer(QWidget parent = 0 ) ; public s l o t s : void updateview ( const boost : : multi_array<double, 2> mat ) ; ; Die Methode updateview, wird hierbei als public slot gekennzeichnet (kann aber wie jede public Methode auch normal aufgerufen werden). Der Hintergrund der Klasse MBViewer ist, dass ein Label in der Lage ist, eine QPixmap darzustellen. Da für den direkten Pixelzugriff die Klasse QImage jedoch besser geeignet ist, erstellen wir zunächst ein QImage, weisen die Pixelwerte zu, und setzen die Pixmap unserer von QLabel abgeleiteten Klasse auf eine Pixmap von unserem QImage: // main part o f updateview ( ) : QImage image ( width, height, QImage : : Format_RGB32 ) ; // t r a n s l a t e double v a l u e s to c o l o r s for ( int i = 0 ; i < width ; ++i ) { for ( int j = 0 ; j < h e i g h t ; ++j ) { image. s e t P i x e l ( i, j, qrgb( 0, ( mat ) [ i ] [ j ] 255, 0 ) ) ; setpixmap (QPixmap : : fromimage ( image ) ) ; a d j u s t S i z e ( ) ; // makes sure, t h a t out QLabel f i t s to i t s c o n t e n t s Institut für Angewandte Informationsverarbeitung 4
Aufgabe: Implementiert die Klasse MBViewer wie im UML-Diagramm angegeben in den Dateien MBViewer.h und MBViewer.cpp. Controller Die Interaktion mit Unserem Modell, werden wir im nächsten Blatt betrachten. Jetzt wollen wir ersteinmal etwas auf unserem Bildschirm sehen. Main Nach so viel Text, bekommt Ihr zur Belohnung unser Hauptprogramm mit dazu. Wie Ihr seht, ist das Verdrahten unserer Komponenten nun sehr einfach: // main. cpp : #include " MBViewer. h " // View #include "MBSet. h " // Model int main ( int argc, char argv [ ] ) { QApplication a ( argc, argv ) ; MBViewer mbviewer ; MBSet mbset (400, 2 0 0 ) ; // make view update on model change QObject : : connect ( &mbset, SIGNAL( viewchanged ( const boost : : multi_array<double, 2 > )), &mbviewer, SLOT( updateview ( const boost : : multi_array<double, 2 > ))); // g e n e r a t e i n i t i a l content, which w i l l emit the s i g n a l // viewchanged, so t h a t our S l o t updateview i s c a l l e d mbset. updatemodel ( ) ; mbviewer. show ( ) ; return a. exec ( ) ; Submission Einreichen der Lösung mit: submit cpp 11 main. cpp MBSet. h MBSet. cpp MBViewer. h MBViewer. cpp Viel Spaß! Institut für Angewandte Informationsverarbeitung 5