Title | COS3711 Summary - An Introduction to Design Patterns in C++ with Qt |
---|---|
Author | Wilco Breedt |
Course | Advanced Programming |
Institution | University of South Africa |
Pages | 22 |
File Size | 436.1 KB |
File Type | |
Total Downloads | 418 |
Total Views | 942 |
COS3711 Summary For: An Introduction to Design Patterns in with Qt : Wilco Breedt 1. Reflection 2 2. Validation 5 3. XML 7 4. Model View Architecture 8 5. Momento 13 6. Concurrency 15 7. Singleton 17 8. UML 18 9. Factory Method 19 10. Notes 20 COS3711 Wilco Breedt 1 G const QMetaProperty metaProp CO...
COS3711 Summary For: An Introduction to Design Patterns in C++ with Qt By: Wilco Breedt
1. Reflection
2
2. Validation
5
3. XML
7
4. MV/MVC - Model View Architecture
8
5. Momento
13
6. Concurrency
15
7. Singleton
17
8. UML
18
9. Factory Method
19
10. Notes
20
COS3711
Wilco Breedt 1
1. Reflection 1. 2. 3. 4. 5. 6.
Inherit QObject Q_OBJECT Macro Q_PROPERTY(int size READ getSize WRITE setSize) Q_PROPERTY(type name READ getter WRITE setter) MOC (Meta Object Compiler) Looks for QOBJECT Macro and compiles a meta object file. Before RUNTIME. Dynamic Properties (When you use setProperty and the property you are trying to set was NOT in the class Q_PROPERTY declared by yourself. This means you cannot use the QMetaProperty to get these values because Dynamic properties are created at RUNTIME where static properties are created BEFORE RUNTIME when the MOC generates the MetaObject for the class 7. QMetaObject - Object that carries meta data about another object (QObject) Used for reflection 8. Static Properties - Defined in the class - Information about static properties are created at compile time and exist within QMetaObject 9. Dynamic Properties- Defined outside the class - created at runtime and you cannot obtain information about them using the QMetaObject
===================================image.h=================================== #ifndef IMAGE_H #define IMAGE_H #include class Image: public Q Object / / Inherit from QObject { Q_OBJECT // QOBJECTMacro (MOC) Q_PROPERTY(i nt size READ getSize WRITE setSize) // Q_PROPERTY (MOC) Q_PROPERTY(QString name READ getName WRITE setName) // Q_PROPERTY (MOC) public: Image(); int getSize(); void setSize(int s); QString getName(); void setName(QString s); private: int length; // Q_PROPERTY(int size ...) This does not have to be the same as the Q_PROPERTY that gets its value QString name; }; #endif // IMAGE_H ===================================image.cpp=================================== #include "image.h" Image::Image() {} void Image::setSize(int s ) { length = s; } int Image::getSize() { r eturn length; } void Image::setName(QString s) { ame = s; n } QString Image ::getName() { r eturn name; }
COS3711
Wilco Breedt 2
====================================main.cpp=================================== #include #include #include #include "image.h" void printObject(Q Object *obj) { / / Method that uses reflective techniques c onst QMetaObject *meta = obj->metaObject( ); QString className = meta->className(); QString result = ""; // Static Properties ( Was declared by yourself in the Q_PROPERTY of the class ) (Declared at compile time) // for (int i = meta->propertyOffset(); i propertyCount(); i++) Get rid of properties in base class for (int i = 0; i propertyCount(); i++) { // First method to get value of meta property const QMetaProperty metaProp = meta->property(i); const char *name = metaProp.name(); // Get the name of the meta property QVariant value = obj->property(name); / / Get the value of the meta property // Convert meta property name to QString if you have to // QString name = QString(metaProp.name()); // Second method to get value of meta property // const QMetaProperty metaProp = meta->property(i); // QVariant value = metaProp.read(obj); result += QString(" %1 %2 ").arg(name).arg(value.toString()); } // Dynamic Properties (Wasn’t declared in the Q_PROPERTY/ Declared at RUNTIME) foreach(QByteArray dynamicPropName, obj->dynamicPropertyNames()) { QVariant value = obj->property(dynamicPropName); result += QString(" %1 %2 ") .arg(Q String(dynamicPropName)) .arg(value.toString()); } qDebug() dynamicPropertyNames()) { QVariant value = i->property(dynamicPropName); merge.append(QString("DName %1, Value %2").arg(QString(dynamicPropName)).arg(value.toString())); } } // If the Object is not a pointer (Image *i) -> Image i // f oreach(Image i, list) { // const QMetaObject metaObj = i.metaObject(); // for (int in = 0; in setProperty("name2", "Wilco2.png"); // Dynamic Property // Getting Values int imageSize = img->property(" size").toInt(); // ->property returns QVariant // OR QVariant name = img->property("name"); // ->property returns QVariant QString imageName = name.toString(); // Convert to QString qDebug() getName()); name.appendChild(nameText); } QString xml = doc.toString(); }
COS3711
Wilco Breedt 7
4. MV/MVC - Model View Architecture 1.
Views - ONLY displays the data, declare one instance and set the Model (that is basically the only thing you do with it) The rest of the things comes from the Model (use setModel to set the view) a.
QTableView (Model Based)
b. QListView (Model Based) c.
QTreeView (Model Based)
d. QTableWidget (Item Based/ Convenience class) e.
QListWidget (Item Based/ Convenience class)
f.
QTreeWidget (Item Based/ Convenience class)
2. Models - Serve the DATA to the Views (Remember Abstract means you cannot use it, you can not make an instance of it, you cannot instantiate it ! you need to inherit it and then you can use it !!! QAbstractItemModel, QAbstractTableModel, QAbstractListModel) Concrete models are models of which you can make a instance of
Objectives when using a QAbstractTableModel (Nothing is done on the View, everything is done on the MODEL!) : 1.
Headers
2. Edit table data (Double click on row and edit it)
COS3711
Wilco Breedt 8
3.
Add table data (Add rows)
4.
Remove / Delete data (Delete rows)
How to implement a QAbstractTableModel (subclassing it): ●
Functions YOU MUST IMPLEMENT !!!!!!! ○
rowCount() (pure virtual)→ How many rows are in your data
○
columnCount() (pure virtual) → How many columns are there in your data
○
data() (pure virtual) → To return the actual value / data (provides the data for each and every CELL in
○
headerData() (This is just to make it a WELL behaved model)
your table) ●
Functions to implement if you want your model to be EDITABLE ! ○
setData() (If you want your table to be editable)
○
flags() that returns a value containing Qt::ItemIsEditable / Qt::ItemIsSelectable / Qt::ItemIsEnabled
int MyModel::rowCount(const Q QModelIndex &parent) const { // Q_UNUSED(parent); // Sodat jy nie warnings kry vir goed wat nie gebruik word nie. return list.count(); // list.length - 1 ?? } int MyModel::columnCount(const Q QModelIndex &parent) const { // Q_UNUSED(parent); // Sodat jy nie warnings kry vir goed wat nie gebruik word nie. return headers.count(); // headers.length - 1 ?? } // Get the header data QVariant MyModel::headerData(int section, Qt::Orientation orientation, i nt role) const // MUST IMPLEMENT { if (role != Qt::DisplayRole) // If it is not for display purposes don't return QVariant {
// Readonly return QVariant();
} if (orientation == Qt::Vertical) // orientation != Qt::Horizontal { return QVariant(); } return headers.at(section); // Headers are stored in QList || QStringList; // return headers[section]; } // Get the data for each cell QVariant MyModel::data(const QModelIndex & index, int role) const / / MUST IMPLEMENT {
COS3711
Wilco Breedt 9
if (!index.isValid()) // Check if the index is VALID ! { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) // Important { // int row = index.row(); // int col = index.column(); // QObject *object = list[row]; // QString columnName = headers[col]; / / QVariant value = object->property(columnName.toStdString().c_str()); // Return const char // return value; r eturn list[index.row()]->property(headers[index.column()].toStdString().c_str()); // Return the data requested. } return QVariant(); } void MyModel::insert() / / Do not have to implement, just for fun. { QObject *object = new QObject(); foreach(QString header, headers) { object->setProperty(header.toStdString().c_str(), "MyTest"); } list.append(object); } a cell // Must be here to edit the MODEL !! // Set the data for bool MyModel::setData(const Q ModelIndex & index, const Q Variant & value, int r ole) / / MUST I MPLEMENT EDITABLE {
COS3711
Wilco Breedt 10
if (role == Qt::EditRole && index.isValid()) { int row = index.row(); int col = index.col(); // list[row]->setProperty(headers[col].toStdString().c_str(), value); l ist.at(row)->setProperty(headers.at(col).toStdString().c_str(), value); emit dataChanged(index, index); // Signal the view that the data changed. return true; } return false; } // Must be here to edit the MODEL !! Qt::ItemFlags M yModel::flags(const QModelIndex & index) // MUST IMPLEMENT EDITABLE { // Do some check here to see if you are allowed to edit it ? // If everything is editable then return everything ! return (Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); } MVC M: Model → Application object / data V: View → Presentation C: Controller → Controls data flow from the model to the view or from the view to the model. Notifies everyone of what is happening. MV Delegate: Renders items for editing (QSpinBox, QLineEdit etc.) Sits between the Model and View Transition from MVC to MV (Why MV is a good example of MVC): ●
Do not see the C (Controller)
●
View and Controller are merged together
●
If something changes in the model, the view is notified, if something changes on the view the model is notified
The controller is like an observer (it observes the changes on the model & view). What is the relationship between MVC and observer pattern ●
In the observer pattern the observer automatically respond to events occurring in the subject/ its subjects
●
In the same way the model, view and controller automatically respond to data changes within each other.
●
This can hence be seen as they are observing each other
MV vs MVC
COS3711
Wilco Breedt 11
In the MV pattern there is no controller, there is however a delegate, which is used to render items for editing on the view and update the model with values from the view when editing is done from the view. A delegate can therefore be seen as playing the role of a controller in the MV vs MVC architecture. Difference between QTableView/QAbstractItemModel and QTableWidget (What is the difference between item based and model based views) ●
QTableWidget (Item based) handles the data for you/data is stored in the widget (This is a convenience class)
●
QTableView (Model based) requires a model to handle the data for you
●
Item based Less flexible ○
In Model based the view and the model are separated, this makes it more flexible because you can have multiple views for the same model, or multiple models for the same view.
○
Model and View are tightly coupled together in the item based.
○
Item based is not reusable because the data is stored within the widget (the view moves with the data)
COS3711
Wilco Breedt 12
5. Momento ●
When you want to serve the state of an object (state = data within the class) in a previous time frame (basically it is used to restore data)
●
3 classes involved ○
Originator → Class that has the state that you want to serve (The class you want to backup) The originator must handle the back-up logic (So all the logic basically comes here)
●
○
Memento → Copy the state of the Originator (This is where the backup is stored)
○
Caretaker → Stores the memonto’s
Momento class (Backup class) → Only has the state + a getter and a setter to set the data
// Originator class ImageList { public: ImageList(); Backup getBackup(); void restore(Backup b); private: QList list; }; // Implementation of the getters and setters within the ImageList Backup ImageList::getBackup() { Backup b; b.setList(l ist); return b; } void ImageList::restore(Backup b) { list = b.getList(); } // Memento class Backup { public: Backup(); void setList(Q List l); QList getList(); private:
COS3711
Wilco Breedt 13
QList list; }; // Caretaker class BackupList: public Q List { public: BackupList(); }; Basic steps of the client: 1.
Backup a.
Get an instance from the originator using the getter you declared
b. Store that backup instance in the caretaker. 2. Restore a.
Get the instance of the backup in the caretaker
b. Use the setter you declared in the originator to restore the backup // CLIENT I mageList i; BackupList bl; // Backup Backup b = i.getBackup(); // Get a instance from originator. bl.append(b); // Store the backup in the caretaker // Restore Backup r = bl.at(0 ); // bl[0] // Get the backup from the caretaker. i.restore(r); // Restore the backup in the originator. Encapsulation REQUIREMENT Should always satisfy this ! ●
Caretaker must NOT be able to modify the memento
●
Make everything private and make the originator a friend class
●
See below how to do this !
// Memento class Backup { private: friend class ImageList; Backup(); List l); void s etList(Q QList getList(); QList list; };
COS3711
Wilco Breedt 14
6. Concurrency ●
The ability to run multiple processes at the same time (Threads)
●
Example: You want to loop a 900 times over 0 - 900. Then check if the numbers are prime numbers. Imagine if it took 1 second for a check, this means your program will be busy for 900 seconds. But if you split it into threads [0 - 300, 301 - 600, 601 - 900] and execute those loops at the same time, then it will take a ⅓ of the time to complete the 900 loop.
Have two things involved in threads (Two classes involved) 1.
Worker → Does the actual work (Does the loop) a.
Must be a QObject
b. Use signals to communicate with the Client 2. Client → The one telling the worker to do its stuffies // Worker, USING THE QOBJECT is the RECOMMENDED APPROACH !! class SearchImages: public Q Object { Q_OBJECT public: SearchImages(); // QList search(QList list, int size); void search(QList list, int size); // Changed the above to this to make it use signals and slots signals: void imageFound(Image); // Signal for when a image is found to emit outside the thread. void finished(); }; // Client QList imgList; QThread *thread = new QThread(); SearchImages *s = new SearchImages(); // Move the Q Object t o t hread, moveToThread e xists o n Q Object c lass s->moveToThread(thread); // connect(senderObject, signalOnSenderObject, receiverObject, slotOnReceiverObject) Connect Basic Params // When the thread starts, start the search function in SearchImage Class connect(thread, SIGNAL(started()), s, SLOT(search(imgList, 300))); // Connect when the SearchImage finds an image and emits it and then sends the image to the handleImage function. connect(s, SIGNAL(imageFound(I mage)), this, SLOT(handleImage(I mage))); // The following 3 steps ARE FOR CLEANUP !! connect(s, SIGNAL(finished()), thread, SLOT(quit()); // When done, quit the thread, CLEANUP connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()); // Delete the thread nicely, CLEANUP connect(thread, SIGNAL(finished()), si, SLOT(deleteLater())); // Delete the worker nicely, CLEANUP thread->start(); // Emits the signal started ... and then the above search(size) SIGNAL SLOT gets handled,
COS3711
Wilco Breedt 15
QProcess ●
Run an exe
●
readyReadStandardOutput - Signal that gets emitted when something is printed to the CONSOLE !!!
QProcess *process = n ew QProcess (); connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(manage()); process->start("image.exe"); void manage() { QByteArray ba = process->readAllStandardOutput(); // Readline QString output = QString(ba); QStringList lines = output.split("/n"); }
COS3711
Wilco Breedt 16
7. Singleton ●
ONE instance
●
Can be asked with Memento
●
Steps ○
1. Hide the constructor → Move the constructor to the private members
○
2. Create a method on the class to get an instance. (getInstance)
○
3. Make that method that you created (getInstace) a static method, this makes it easier so you can call it like so. BookList bl = BookList::getInstance();
○
This makes the getInstance function INSTANCE INDEPEN...