Tag Archives: qt

Rolisteam Evolution

The function evolution…

I would like to show the evolution of one function in rolisteam code.
This function is called when the user (Game Master) closes a map or image.

The function must close the map or image and send a network order to each player to close the image or plan.

At the beginning, the function looked like that.
Some code line and comments were written in French. No coding rules were respected.
Low-level code for networking. The function was very long.

[pastacode lang=”cpp” message=”” highlight=”” provider=”manual” manual=”%20%2F%2F%20On%20recupere%20la%20fenetre%20active%20(qui%20est%20forcement%20de%20type%20CarteFenetre%20ou%20Image%2C%20sans%20quoi%20l’action%0A%20%20%20%20%20%20%20%20%2F%2F%20ne%20serait%20pas%20dispo%20dans%20le%20menu%20Fichier)%0A%20%20%20%20%20%20%20%20QWidget%20*active%20%3D%20workspace-%3EactiveWindow()%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Ne%20devrait%20jamais%20arriver%0A%20%20%20%20%20%20%20%20if%20(!active)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20qWarning(%22Close%20map%20action%20called%20when%20no%20widget%20is%20active%20in%20the%20workspace%20(fermerPlanOuImage%20-%20MainWindow.h)%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20On%20verifie%20pour%20le%20principe%20qu’il%20s’agit%20bien%20d’une%20CarteFenetre%20ou%20d’une%20Image%0A%20%20%20%20%20%20%20%20if%20(active-%3EobjectName()%20!%3D%20%22CarteFenetre%22%20%26%26%20active-%3EobjectName()%20!%3D%20%22Image%22)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20qWarning(%22not%20expected%20type%20of%20windows%20(fermerPlanOuImage%20-%20MainWindow.h)%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Creation%20de%20la%20boite%20d’alerte%0A%20%20%20%20%20%20%20%20QMessageBox%20msgBox(this)%3B%0A%20%20%20%20%20%20%20%20msgBox.addButton(QMessageBox%3A%3AYes)%3B%0A%20%20%20%20%20%20%20%20msgBox.addButton(QMessageBox%3A%3ACancel)%3B%0A%20%20%20%20%20%20%20%20msgBox.setIcon(QMessageBox%3A%3AInformation)%3B%0A%20%20%20%20%20%20%20%20msgBox.move(QPoint(width()%2F2%2C%20height()%2F2)%20%2B%20QPoint(-100%2C%20-50))%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20On%20supprime%20l’icone%20de%20la%20barre%20de%20titre%0A%20%20%20%20%20%20%20%20Qt%3A%3AWindowFlags%20flags%20%3D%20msgBox.windowFlags()%3B%0A%20%20%20%20%20%20%20%20msgBox.setWindowFlags(flags%20%5E%20Qt%3A%3AWindowSystemMenuHint)%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20M.a.j%20du%20titre%20et%20du%20message%0A%20%20%20%20%20%20%20%20if%20(active-%3EobjectName()%20%3D%3D%20%22CarteFenetre%22)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20msgBox.setWindowTitle(tr(%22Close%20Map%22))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20msgBox.setText(tr(%22Do%20you%20want%20to%20close%20this%20map%3F%5CnIt%20will%20be%20closed%20for%20everybody%22))%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20msgBox.setWindowTitle(tr(%22Close%20Picture%22))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20msgBox.setText(tr(%22Do%20you%20want%20to%20close%20this%20picture%3F%5CnIt%20will%20be%20closed%20for%20everybody%22))%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20msgBox.exec()%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Si%20l’utilisateur%20n’a%20pas%20clique%20sur%20%22Fermer%22%2C%20on%20quitte%0A%20%20%20%20%20%20%20%20if%20(msgBox.result()%20!%3D%20QMessageBox%3A%3AYesRole)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Emission%20de%20la%20demande%20de%20fermeture%20de%20la%20carte%0A%20%20%20%20%20%20%20%20if%20(active-%3EobjectName()%20%3D%3D%20%22CarteFenetre%22)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Recuperation%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20QString%20idCarte%20%3D%20((CarteFenetre%20*)active)-%3Ecarte()-%3EidentifiantCarte()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Taille%20des%20donnees%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quint32%20tailleCorps%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Taille%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sizeof(quint8)%20%2B%20idCarte.size()*sizeof(QChar)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Buffer%20d’emission%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20char%20*donnees%20%3D%20new%20char%5BtailleCorps%20%2B%20sizeof(enteteMessage)%5D%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Creation%20de%20l’entete%20du%20message%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20enteteMessage%20*uneEntete%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete%20%3D%20(enteteMessage%20*)%20donnees%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3Ecategorie%20%3D%20plan%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3Eaction%20%3D%20fermerPlan%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3EtailleDonnees%20%3D%20tailleCorps%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Creation%20du%20corps%20du%20message%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20p%20%3D%20sizeof(enteteMessage)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Ajout%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quint8%20tailleIdCarte%20%3D%20idCarte.size()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20memcpy(%26(donnees%5Bp%5D)%2C%20%26tailleIdCarte%2C%20sizeof(quint8))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%2B%3Dsizeof(quint8)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20memcpy(%26(donnees%5Bp%5D)%2C%20idCarte.data()%2C%20tailleIdCarte*sizeof(QChar))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%2B%3DtailleIdCarte*sizeof(QChar)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Emission%20de%20la%20demande%20de%20fermeture%20de%20la%20carte%20au%20serveur%20ou%20a%20l’ensemble%20des%20clients%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20emettre(donnees%2C%20tailleCorps%20%2B%20sizeof(enteteMessage))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Liberation%20du%20buffer%20d’emission%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20delete%5B%5D%20donnees%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Suppression%20de%20la%20CarteFenetre%20et%20de%20l’action%20associee%20sur%20l’ordinateur%20local%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20((CarteFenetre%20*)active)-%3E~CarteFenetre()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Emission%20de%20la%20demande%20de%20fermeture%20de%20l’image%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Recuperation%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20QString%20idImage%20%3D%20((Image%20*)active)-%3EidentifiantImage()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Taille%20des%20donnees%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quint32%20tailleCorps%20%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Taille%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sizeof(quint8)%20%2B%20idImage.size()*sizeof(QChar)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Buffer%20d’emission%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20char%20*donnees%20%3D%20new%20char%5BtailleCorps%20%2B%20sizeof(enteteMessage)%5D%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Creation%20de%20l’entete%20du%20message%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20enteteMessage%20*uneEntete%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete%20%3D%20(enteteMessage%20*)%20donnees%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3Ecategorie%20%3D%20image%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3Eaction%20%3D%20fermerImage%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20uneEntete-%3EtailleDonnees%20%3D%20tailleCorps%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Creation%20du%20corps%20du%20message%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20int%20p%20%3D%20sizeof(enteteMessage)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Ajout%20de%20l’identifiant%20de%20la%20carte%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20quint8%20tailleIdImage%20%3D%20idImage.size()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20memcpy(%26(donnees%5Bp%5D)%2C%20%26tailleIdImage%2C%20sizeof(quint8))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%2B%3Dsizeof(quint8)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20memcpy(%26(donnees%5Bp%5D)%2C%20idImage.data()%2C%20tailleIdImage*sizeof(QChar))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%2B%3DtailleIdImage*sizeof(QChar)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Emission%20de%20la%20demande%20de%20fermeture%20de%20l’image%20au%20serveur%20ou%20a%20l’ensemble%20des%20clients%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20emettre(donnees%2C%20tailleCorps%20%2B%20sizeof(enteteMessage))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Liberation%20du%20buffer%20d’emission%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20delete%5B%5D%20donnees%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Suppression%20de%20l’Image%20et%20de%20l’action%20associee%20sur%20l’ordinateur%20local%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20((Image%20*)active)-%3E~Image()%3B%0A%20%20%20%20%20%20%20%20%7D”/]

A big step forward, the networking code has been reworked.
So the readability is improved. It is also possible to see the beginning of polymorphism. MediaContener is used but not everywhere.

[pastacode lang=”cpp” message=”Version IntermĂ©diaire” highlight=”” provider=”manual” manual=”QMdiSubWindow*%20subactive%20%3D%20m_mdiArea-%3EcurrentSubWindow()%3B%0AQWidget*%20active%20%3D%20subactive%3B%0AMapFrame*%20bipMapWindow%20%3D%20NULL%3B%0A%0Aif%20(NULL!%3Dactive)%0A%7B%0A%0A%20%20%20%20QAction*%20action%3DNULL%3B%0A%0A%20%20%20%20Image*%20%20imageFenetre%20%3D%20dynamic_cast(active)%3B%0A%0A%20%20%20%20QString%20mapImageId%3B%0A%20%20%20%20QString%20mapImageTitle%3B%0A%20%20%20%20mapImageTitle%20%3D%20active-%3EwindowTitle()%3B%0A%20%20%20%20bool%20image%3Dfalse%3B%0A%20%20%20%20%2F%2Fit%20is%20image%0A%20%20%20%20if(NULL!%3DimageFenetre)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20m_pictureList.removeOne(imageFenetre)%3B%0A%0A%20%20%20%20%20%20%20%20mapImageId%20%3D%20imageFenetre-%3EgetMediaId()%3B%0A%20%20%20%20%20%20%20%20image%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20action%20%3D%20imageFenetre-%3EgetAction()%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%2F%2Fit%20is%20a%20map%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20bipMapWindow%3D%20dynamic_cast(active)%3B%0A%20%20%20%20%20%20%20%20if(NULL!%3DbipMapWindow)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20mapImageId%20%3D%20bipMapWindow-%3EgetMediaId()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20action%20%3D%20bipMapWindow-%3EgetAction()%3B%0A%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%2F%2F%20it%20is%20undefined%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20QMessageBox%20msgBox(this)%3B%0A%20%20%20%20msgBox.setStandardButtons(QMessageBox%3A%3AYes%20%7C%20QMessageBox%3A%3ACancel%20)%3B%0A%20%20%20%20msgBox.setDefaultButton(QMessageBox%3A%3ACancel)%3B%0A%20%20%20%20msgBox.setIcon(QMessageBox%3A%3AInformation)%3B%0A%20%20%20%20msgBox.move(QPoint(width()%2F2%2C%20height()%2F2)%20%2B%20QPoint(-100%2C%20-50))%3B%0A%20%20%20%20Qt%3A%3AWindowFlags%20flags%20%3D%20msgBox.windowFlags()%3B%0A%20%20%20%20msgBox.setWindowFlags(flags%20%5E%20Qt%3A%3AWindowSystemMenuHint)%3B%0A%0A%20%20%20%20if%20(!image)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20msgBox.setWindowTitle(tr(%22Close%20Map%22))%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20msgBox.setWindowTitle(tr(%22Close%20Picture%22))%3B%0A%20%20%20%20%7D%0A%20%20%20%20msgBox.setText(tr(%22Do%20you%20want%20to%20close%20%251%20%252%3F%5CnIt%20will%20be%20closed%20for%20everybody%22).arg(mapImageTitle).arg(image%3Ftr(%22%22)%3Atr(%22(Map)%22)))%3B%0A%0A%20%20%20%20msgBox.exec()%3B%0A%20%20%20%20if%20(msgBox.result()%20!%3D%20QMessageBox%3A%3AYes)%0A%20%20%20%20%20%20%20%20return%3B%0A%0A%20%20%20%20if%20(!image)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20NetworkMessageWriter%20msg(NetMsg%3A%3AMapCategory%2CNetMsg%3A%3ACloseMap)%3B%0A%20%20%20%20%20%20%20%20msg.string8(mapImageId)%3B%0A%20%20%20%20%20%20%20%20msg.sendAll()%3B%0A%0A%20%20%20%20%20%20%20%20m_mapWindowMap.remove(mapImageId)%3B%0A%20%20%20%20%20%20%20%20m_playersListWidget-%3Emodel()-%3EchangeMap(NULL)%3B%0A%20%20%20%20%20%20%20%20m_toolBar-%3EchangeMap(NULL)%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20NetworkMessageWriter%20msg(NetMsg%3A%3APictureCategory%2CNetMsg%3A%3ADelPictureAction)%3B%0A%20%20%20%20%20%20%20%20msg.string8(mapImageId)%3B%0A%20%20%20%20%20%20%20%20msg.sendAll()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20MediaContainer*%20%20mediaContener%20%3D%20dynamic_cast(subactive)%3B%0A%20%20%20%20if(NULL!%3DmediaContener)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20CleverURI*%20cluri%20%3D%20mediaContener-%3EgetCleverUri()%3B%0A%20%20%20%20%20%20%20%20cluri-%3EsetDisplayed(false)%3B%0A%20%20%20%20%20%20%20%20if(NULL!%3Dm_sessionManager)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20m_sessionManager-%3EupdateCleverUri(cluri)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20delete%20action%3B%0A%20%20%20%20delete%20subactive%3B%0A%7D%0A”/]

Then, we implements a solution to describe map, vmap (v1.8) or image as mediacontener.
They share a large part of their code, so it becomes really easy to do it.

[pastacode lang=”cpp” message=”Actuel” highlight=”” provider=”manual” manual=”QMdiSubWindow*%20subactive%20%3D%20m_mdiArea-%3EcurrentSubWindow()%3B%0A%20%20%20%20MediaContainer*%20container%20%3D%20dynamic_cast(subactive)%3B%0A%20%20%20%20if(NULL%20!%3D%20container)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20CleverURI%3A%3AContentType%20type%20%3D%20container-%3EgetContentType()%3B%0A%20%20%20%20%20%20%20%20if(CleverURI%3A%3AVMAP%20%3D%3D%20type)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20removeVMapFromId(container-%3EgetMediaId())%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%20if(CleverURI%3A%3AMAP%20%3D%3D%20type)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20removeMapFromId(container-%3EgetMediaId())%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%20if(CleverURI%3A%3APICTURE%20%3D%3D%20type%20)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20removePictureFromId(container-%3EgetMediaId())%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A”/]

Make your own slide show presentation in QML

To prepare my conference at Pas Sage En Seine [FR], a French hacking festival, I chose to write my slide presentation in QML.
It allows me to have better control and be free to do whatever I want (such as a timeline or any kind of animation).
Of course, That comes with a price. It is longer to do it that way but now I find some solutions. So, next time will be faster.

File hierarchy:

I preferred use QML through C++ Application. It provides more helpful feature, such as: the ability to make screenshots of your presentation at any time (useful as backup plan).
At the top level, you will found all C++ classes, project files and the main qml. Then, you will have a directory with all your pages and if it is required a directory with all your images.

├── cpphighlighter.cpp
├── cpphighlighter.h
├── deployment.pri
├── LICENSE
├── main.cpp
├── main.qml
├── pages
│   ├── 01_intro.qml
│   ├── 02_presentation.qml
│   ├── 03_jdr_et_rolisteam.qml
│   ├── 043_Exemple_code_1.qml
│   ├── 04_jdr_avantages_pb.qml
│   ├── 05_avantage_jdr_virtuel.qml
│   ├── 06_fonctionnalites_rolisteam.qml
│   ├── 07_rolisteam_debut.qml
│   ├── 08_Rolistik_a_Rolisteam.qml
│   ├── 10_frise_chronologique.qml
│   ├── 11_son_usage.qml
│   ├── 12_son_fonctionnement.qml
│   ├── 13_dice_parser.qml
│   ├── 14_themes_audio_3_pistes.qml
│   ├── 15_nouveaute_1_8.qml
│   ├── 16_projet_avenir.qml
│   ├── 17_reussites.qml
│   ├── 18_les_lecons.qml
│   ├── 19_objectif_rolisteam_libre.qml
│   ├── 20_FAQ.qml
├── pasSageEnSeine.pro
├── pasSageEnSeine.pro.user
├── qmlcontroler.cpp
├── qmlcontroler.h
├── qmlcontroler.ui
├── qml.qrc
├── README.md
├── rsrc
│   ├── all.png
│   ├── cc.png
│   └── chat.png

The C++ application

The main

It can be useful to see the state of the presentation and to read some extra notes about the current slide. To manage that, I wrote a small C++ application.
The first goal is to show the QML view, then add some features and communication between the QML view and the C++ window.

[pastacode lang=”cpp” manual=”%23include%20%3CQApplication%3E%0A%23include%20%3CQQmlApplicationEngine%3E%0A%23include%20%22qmlcontroler.h%22%0A%23include%20%3CQQmlContext%3E%0A%23include%20%3CQQuickTextDocument%3E%0A%0A%23include%20%22cpphighlighter.h%22%0A%0Aint%20main(int%20argc%2C%20char%20*argv%5B%5D)%0A%7B%0A%20%20%20%20QApplication%20app(argc%2C%20argv)%3B%0A%0A%20%20%20%20QQmlApplicationEngine%20engine%3B%0A%0A%20%20%20%20engine.rootContext()-%3EsetContextProperty(%22ScreenW%22%2C1280)%3B%0A%20%20%20%20engine.rootContext()-%3EsetContextProperty(%22ScreenH%22%2C720)%3B%0A%0A%20%20%20%20engine.load(QUrl(QStringLiteral(%22qrc%3A%2Fmain.qml%22)))%3B%0A%0A%20%20%20%20QmlControler%20ctr%3B%0A%20%20%20%20ctr.setEngine(%26engine)%3B%0A%0A%20%20%20%20return%20app.exec()%3B%0A%7D%0A” message=”main.cpp” highlight=”” provider=”manual”/]

Really easy, it loads the main.qml from the resources management system provided by Qt. It defines the targeted resolution by setting two constant into QML word: ScreenW and ScreenH.

In this project, the QmlControler class is the C++ window which provides slide feedback and additional information.

Let’s take a look to it:

Feedback window

This window is really simple. It has two parts: On the left, there is a label which displays screenshot of the qml view, and the right part is a QTextArea which display any additional note about the current slide.

[pastacode lang=”cpp” manual=”void%20QmlControler%3A%3AcurrentPageHasChanged(int%20i)%0A%7B%0A%20%20%20%20m_currentScreen%20%3D%20i%3B%0A%20%20%20%20QImage%20img%20%3D%20m_window-%3EgrabWindow()%3B%0A%0A%20%20%20%20if(img.isNull())%0A%20%20%20%20%20%20%20%20return%3B%0A%0A%20%20%20%20static%20int%20count%20%3D%200%3B%0A%0A%0A%20%20%20%20img.save(tr(%22screens%2F%251_screen.png%22).arg(%2B%2Bcount%2C3%2C10%2CQChar(‘0’))%2C%22png%22)%3B%0A%20%20%20%20qDebug()%20%3C%3C%20%22screen%20shot%20save%22%20%3C%3C%20count%3B%0A%0A%20%20%20%20m_ratioImage%20%3D%20(double)img.size().width()%2Fimg.size().height()%3B%0A%20%20%20%20m_ratioImageBis%20%3D%20(double)img.size().height()%2Fimg.size().width()%3B%0A%0A%20%20%20%20m_label-%3EsetPixmap(QPixmap%3A%3AfromImage(img))%3B%0A%0A%20%20%20%20if((i%2B1%3E%3D0)%26%26(i%2B1%3Cm_commentData.size()))%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20ui-%3EtextEdit-%3EsetHtml(m_commentData.at(i%2B1))%3B%0A%20%20%20%20%7D%0A%20%20%20%20resizeLabel()%3B%0A%7D” message=”Current slide has changed” highlight=”” provider=”manual”/]

When the current slide has changed, the c++ window is notified thought the slot currentPageHasChanged, The application gets screenshot of the qml view, save it as a file, display it thought the label, then it looks for data about the current slide into the model. If any, there are displayed in the textedit.

Saving screenshots into file allows you to create a pdf file as backup plan for your presentation.

$ convert *.png mypresentation.pdf

 

QML Application

Loader system.

For readability reason, it is easier to have each page into one qml file. The application has to load those pages in the right order. To reach this goal, we have to define the order. I did it thank to a data model inside the main.qml file.
The main.qml displays all pages as item of a pathview. All items are loaded from the qt resource management system.

[pastacode lang=”css” manual=”ListModel%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20id%3A%20panelModel%0A%20%20%20%20%20%20%20%20%20%20%20%20ListElement%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Intro%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20path%3A%20%2201_intro.qml%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20time%3A%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20next%3A%20%22Pr%C3%A9sentation%20de%20Rolisteam%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D” message=”First item of the model.” highlight=”” provider=”manual”/]

A page is mainly defined by two data: name and path. The path is the name of the qml file.
All other data are here as help, the time has not been used.

Then, the loader does its job, the key lines are the following:

[pastacode lang=”css” manual=”%20%20%20%20PathView%20%7B%0A%20%20%20%20%20%20%20%20id%3A%20view%0A%20%20%20%20%20%20%20%20anchors.fill%3A%20parent%0A%20%20%20%20%20%20%20%20model%3A%20panelModel%0A%20%20%20%20%20%20%20%20highlightRangeMode%3APathView.StrictlyEnforceRange%0A%20%20%20%20%20%20%20%20snapMode%3A%20PathView.SnapOneItem%0A%20%20%20%20%20%20%20%20delegate%3A%20%20Loader%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20source%3A%20%22pages%2F%22%2Bpath%0A%20%20%20%20%20%20%20%20%7D” message=”Path View” highlight=”source: “pages/”+path” provider=”manual”/]

 

Table of Contents

To manage the table of contents, I added a listview with a model:

[pastacode lang=”css” manual=”%20%20%20%20ListView%20%7B%0A%20%20%20%20%20%20%20%20id%3A%20listView1%0A%20%20%20%20%20%20%20%20x%3A%20ScreenW*0.02%0A%20%20%20%20%20%20%20%20y%3A%20ScreenH*0.3%0A%20%20%20%20%20%20%20%20width%3A%20ScreenW%2F2%0A%20%20%20%20%20%20%20%20height%3A%20ScreenH*0.2%0A%20%20%20%20%20%20%20%20delegate%3A%20Item%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20ScreenW%2F2%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20listView1.height%2FlistView1.count%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Text%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20view.currentIndex%3E%3Dindex%20%3F%20%22black%22%20%3A%20%22gray%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%3A%20name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20font.pointSize%3A%20ScreenH%2F48%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20anchors.verticalCenter%3A%20parent.verticalCenter%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20font.bold%3A%20true%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20visible%3A%20view.currentIndex%3E0%20%3F%20true%20%3A%20false%0A%0A%20%20%20%20%20%20%20%20model%3A%20ListModel%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20ListElement%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Concepts%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%3A1%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20ListElement%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Chroniques%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%3A6%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20ListElement%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Logiciel%22%2F%2Fsyst%C3%A8me%20de%20build%2C%20code%20sp%C3%A9cifique%20par%20OS.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%3A9%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20ListElement%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%22Bilan%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%3A15%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D” message=”Table of contents in QML ” highlight=”” provider=”manual”/]

Next slide

When you have many slides it can be helpful to have indication about the next one. I chose to display the title in the top-right corner. It was the easier way.

[pastacode lang=”css” manual=”%20%20%20%20Text%20%7B%0A%20%20%20%20%20%20%20%20anchors.top%3A%20parent.top%0A%20%20%20%20%20%20%20%20anchors.right%3A%20parent.right%0A%20%20%20%20%20%20%20%20text%3A%20panelModel.get(view.currentIndex).next%2B%22%3E%22%0A%20%20%20%20%7D” message=”Next slide” highlight=”” provider=”manual”/]

 

Design a page

Each page are independent but they are all based on the same pattern. In my case, they have a listview with model. Each item of the model is an point I should talk about it.

Each item has a index. The index is controlled with keyboard (down to increase, up to decrease). The page manages what is shown or hidden given the value of the index.

For example, the feature of dice alias has 10 as index. When the index page value becomes 10, the «Dice Alias»  item is displayed with an animation. Then, at 11, I can show a screen shot about the dice alias. At 12, the screenshot disappears and another text is displayed.

Position and Size

To ensure that all items will be display at the proper position and size.  I have based all computation on anchor or the screen size.

[pastacode lang=”css” manual=”%20%20%20%20Image%20%7B%0A%20%20%20%20%20%20%20%20id%3A%20image1%0A%20%20%20%20%20%20%20%20anchors.left%3A%20parent.left%0A%20%20%20%20%20%20%20%20anchors.top%3A%20parent.top%0A%20%20%20%20%20%20%20%20anchors.leftMargin%3A%20ScreenW*0.04%0A%20%20%20%20%20%20%20%20fillMode%3A%20Image.PreserveAspectFit%0A%20%20%20%20%20%20%20%20source%3A%20%22qrc%3A%2Frsrc%2FRolisteam.svg%22%0A%20%20%20%20%20%20%20%20width%3A%20ScreenW*0.2%0A%20%20%20%20%7D” message=”Display the logo at the right position and size.” highlight=”” provider=”manual”/]

Other way

There is a module that provides Items to create QML presentation. I don’t use it for this one but it may provide interesting things.

https://github.com/qt-labs/qml-presentation-system

Get the code

You are invited to clone the code at : https://github.com/obiwankennedy/pses

Introduction Ă  Dbus avec Qt4

Dbus

Dbus est un bus système de messagerie. C’est un moyen simple pour des applications de communiquer entre elles. En plus de la communication interprocessus, Dbus est aussi un outil aidant au cycle de vie du processus. Il peut vous permettre de n’autoriser qu’une seule instance de votre application ou de votre daemon. Il peut vous permettre de dĂ©marrer votre application ou daemon Ă  la demande quand elles sont nĂ©cessaires. Il est possible d’Ă©tablir deux types de connexions: point Ă  point ou point d’accès (daemon central). Grâce au daemon, vos applications peuvent ĂŞtre prĂ©venues du branchement d’un nouvel composant matĂ©riel. Le bus de session (un par utilisateur connectĂ©) est le canal gĂ©nĂ©rique de communication. Dbus est destinĂ© Ă  la communication entre application d’un mĂŞme ordinateur. Il est cependant possible de communiquer par TCP/IP cryptĂ© avec un dossier home partagĂ© par NFS. Ce genre d’usage reste rare et expĂ©rimental.

Il est possible d’utiliser Dbus dans de nombreux langages: C/C++, python, ruby, perl, java, C#. Je vais me concenter sur l’implĂ©mentation de Dbus dans Qt4, dans cet article.

Dbus dans Qt4

Qt4 implement sa propre API Dbus. Le support de Dbus sur windows Ă©tant en cours (d’après le site internet), Qt a Ă©tĂ© sage de regrouper toutes les fonctionnalitĂ©s dans un module. Pour des environnements linux, il suffit d’installer la bibliothèque et Ă  l’Ă©dition des liens, ajouter

QT += dbus

Dans le fichier pro pour inclure le module dbus Ă  votre projet.

Les deux classes importantes sont QDbusMessage et QDbusInterface. La première permet d’envoyer facilement un message vers un service Dbus. C’est un moyen rapide pour communiquer une information vers une application dont vous ne maitrisez rien. Le QBusInterface permet une communication plus transparente, car tous les signaux/slots de votre application (d’une classe, pour ĂŞtre prĂ©cis) seront mis sur Dbus.

Monitorer Dbus

Vous ne le savez peut-ĂŞtre pas mais votre système GNU/Linux (s’il est assez moderne) utilise probablement DBUS en continue. Pour se donner une idĂ©e, il existe deux utilitaires assez intĂ©ressant: dbus-monitor et qdbusviewer.

capture de qdbusviewer

 

Connexion Ă  un service : Pidgin

Nous allons maintenant implĂ©mentĂ© une solution pour utiliser un service d’une application. Nous voulons mettre Ă  jour la petite phrase de status de pidgin. Ainsi votre logiciel de lecture audio pourra mettre en petite phrase la chanson actuellement jouĂ©e.

La première Ă©tape est d’identifiĂ© la fonction (et son chemin) qui nous intĂ©resse. En gĂ©nĂ©ral, les logiciels libres fournissent dans leur documentation l’API dbus qu’ils offrent. Cependant, l’outil qdbusviewer vous permet de voir toutes ces informations.

En terme de code, cela reste très simple. Vous déclarez un message Dbus. Il faut définir les bonnes valeurs: chemin, destinataire, noms..

[pastacode lang=”cpp” message=”Connexion Ă  un service dbus” highlight=”” provider=”manual” manual=”QDBusConnection%20sessionbus%20%3D%20QDBusConnection%3A%3AsessionBus()%3B%0A%0Aif%20(%20!sessionbus.isConnected()%20)%0A%7B%0A%20%20%20%20qDebug()%20%3C%3C%20%22Could%20not%20connect%20to%20session%20bus%22%3B%0A%7D%0A%20%20%20%20QDBusMessage%20m%20%3D%20%0A%20%20%20%20QDBusMessage%3A%3AcreateMethodCall(%22im.pidgin.purple.PurpleService%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%2Fim%2Fpidgin%2Fpurple%2FPurpleObject%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22im.pidgin.purple.PurpleInterface%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22PurpleUtilSetCurrentSong%22)%3B%0A%20%20%20%20QList%3CQVariant%3E%20args%3B%0A%20%20%20%20args%20%3C%3C%20m_p-%3ETitle%20%3C%3C%20m_p-%3EArtist%20%3C%3C%20m_p-%3Ealbum%3B%0A%20%20%20%20m.setArguments(args)%3B%0A%20%20%20%20QDBusMessage%20metadatamsg%20%3D%20sessionbus.call(m)%3B%0A%20%20%20%20if(metadatamsg.type()%20!%3D%20QDBusMessage%3A%3AReplyMessage)%0A%20%20%20%20%20%20%20%20qDebug()%20%3C%3C%20%22Error%20its%20not%20a%20message%20%22%20%3C%3C%20metadatamsg.type()%20%3C%3C%20metadatamsg.errorMessage%20()%3B”/]

La première Ă©tape est de dĂ©finir quel bus vous allez utiliser : le sessionBus ou le systembus? Dans notre cas, c’est le sessionBus. On verifie que tout se passe bien niveau connection au bus. Il convient ensuite de crĂ©er un message. Les paramètres du messages sont les suivants :

  • Le nom du service (la colonne de gauche de qdbusviewer)
  • Le chemin vers l’objet (concatĂ©nation de tous les noeuds de l’arbre sauf celui de l’interface).
  • Le chemin vers l’interface (noeud en italique dans qdbusviewer).
  • Une liste d’arguments peut ĂŞtre passĂ©e Ă  l’appel de la mĂ©thode du service. Dans notre cas, nous passons le titre de la chanson, l’artiste et le nom de l’album.
  • Dernière Ă©tape, appel de la mĂ©thode suivit d’une vĂ©rification d’erreur.

La voie royale pour faire communiquer deux applications via dbus grâce Ă  Qt est l’utilisation des adaptors. Ils traduisent les signaux et slots d’une classe Qt en message Dbus. C’est totalement transparent. Pour faire un exemple simple, je vais faire une application qui propose un service “dbus_example_Service”, de dĂ©finition de brigthness et les clients peuvent dĂ©finir le nouveau de volume. Plusieurs applications peuvent envoyer le volume au service et elles peuvent Ă©galement recevoir la brightness. Une seule et unique application peut faire office de service. Deux instance de l’application “dbus_example_Service” vont entrainĂ© des comportements inconnus.

qdbuscpp2xml -S -M mainwindow.h -o org.homelinux.renaudguezennec.xml

qdbusxml2cpp -c GuiAdaptor -a guiadaptor.h:guiadaptor.cpp org.homelinux.renaudguezennec.xml

qdbusxml2cpp -c GuiAdaptor -p test.h:test.cpp org.homelinux.renaudguezennec.xml

La première commande vous permet de gĂ©nĂ©rer un fichier xml: org.homelinux.renaudguezennec.xml qui contiendra l’ensemble de l’API de votre fichier .h.
Le resultat est de la forme suivante:

[pastacode lang=”markup” message=”Xml gĂ©nĂ©rĂ©” highlight=”” provider=”manual” manual=”%3C!DOCTYPE%20node%20PUBLIC%20%22-%2F%2Ffreedesktop%2F%2FDTD%20D-BUS%20Object%20Introspection%201.0%2F%2FEN%22%20%22http%3A%2F%2Fwww.freedesktop.org%2Fstandards%2Fdbus%2F1.0%2Fintrospect.dtd%22%3E%0A%3Cnode%3E%0A%20%20%3Cinterface%20name%3D%22org.homelinux.renaudguezennec%22%3E%0A%20%20%20%20%3Csignal%20name%3D%22brightnessChanged%22%3E%0A%20%20%20%20%20%20%3Carg%20type%3D%22i%22%20direction%3D%22out%22%2F%3E%0A%20%20%20%20%3C%2Fsignal%3E%0A%20%20%20%20%3Cmethod%20name%3D%22setVolumeLevel%22%3E%0A%20%20%20%20%20%20%3Carg%20type%3D%22i%22%20direction%3D%22in%22%2F%3E%0A%20%20%20%20%3C%2Fmethod%3E%0A%20%20%3C%2Finterface%3E%0A%3C%2Fnode%3E”/]


La deuxième commande crĂ©e une classe GuiAdaptor (dans les fichiers: guiadaptor.h et guiadaptor.cpp) qui expose l’API d’Ă©crire dans le xml. Cette classe sera utile cotĂ© service.

La troisième commande crĂ©e une classe GuiAdaptor (dans les fichiers: test.h et test.cpp) que sera capable de contacter l’API dĂ©crite dans le xml. Nous allons utiliser cette classe dans le client pour l’API dĂ©crite dans le xml.

L’Ă©tape suivante consiste Ă  gĂ©nĂ©rer une classe Adaptor qui transformera vos signaux/Slots Qt en message dbus. Il est utilisĂ© cĂ´tĂ© service. Dans le main de votre programme, il convient d’instancier un adaptor gĂ©nĂ©rĂ© (ici: ControlPanelAdaptor), comme cela:

[pastacode lang=”cpp” message=”Creation de service” highlight=”” provider=”manual” manual=”%23include%20%3CQtGui%2FQApplication%3E%0A%23include%20%22mainwindow.h%22%0A%23include%20%22guiadaptor.h%22%0Aint%20main(int%20argc%2C%20char%20*argv%5B%5D)%0A%7B%0A%20%20%20%20QApplication%20a(argc%2C%20argv)%3B%0A%20%20%20%20MainWindow%20w%3B%0A%20%20%20%20new%20GuiAdaptor(%26w)%3B%0A%20%20%20%20w.show()%3B%0A%0A%20%20%20%20QDBusConnection%20connection%20%3D%20QDBusConnection%3A%3AsessionBus()%3B%0A%20%20%20%20bool%20rel%20%3D%20connection.registerService(%22org.homelinux.renaudguezennec%22)%3B%0A%20%20%20%20rel%20%3D%20connection.registerObject(%22%2F%22%2C%26w)%3B%0A%0A%20%20%20%20return%20a.exec()%3B%0A%7D”/]


A ce stade, votre application peut-être un service dbus. Il fournit une API à travers DBus. Nous allons voir maintenant comment créer des clients qui vont utiliser cette API.

Nous allons donc construire un nouveau projet : dbus_example_client. Il contiendra la rĂ©ciproque du service. Le service est capable d’envoyer le signal brightnessChanged et reçoit son volume par l’appel Ă  setVolumeLevel. Le client lui sera capable d’appeler setVolumeLevel du service et de recevoir la valeur de brightnessChanged. Pour se faire, nous allons utilisĂ© la class GuiAdaptor (test.h et test.cpp) dans notre application cliente.

[pastacode lang=”cpp” message=”Client Qt” highlight=”” provider=”manual” manual=”%23include%20%3CQtGui%2FQApplication%3E%0A%23include%20%22mainwindow.h%22%0A%23include%20%22test.h%22%0A%0Aint%20main(int%20argc%2C%20char%20*argv%5B%5D)%0A%7B%0A%20%20%20QApplication%20a(argc%2C%20argv)%3B%0A%20%20%20MainWindow%20w%3B%0A%0A%20%20%20%20GuiAdaptor*%20adapteur%20%3D%20new%20GuiAdaptor(%22org.homelinux.renaudguezennec%22%2C%20%0A%20%20%20%20%22%2F%22%2CQDBusConnection%3A%3AsessionBus()%2C0)%3B%0A%0A%20%20%20%20QObject%3A%3Aconnect(%26w%2CSIGNAL(volumeChanged(int))%2Cadapteur%2C%0A%20%20%20%20SLOT(setVolumeLevel(int)))%3B%0A%0A%20%20%20%20QObject%3A%3Aconnect(adapteur%2CSIGNAL(brightnessChanged(int))%2C%26w%2C%0A%20%20%20%20SLOT(setBrightnessChanged(int)))%3B%0A%20%20%20%20w.show()%3B%0A%0A%20%20%20%20return%20a.exec()%3B%0A%7D”/]


Comme vous pouvez le voir, il suffit d’instancier une classe GuiAdaptor (#include “test.h”) et de connecter les signals et les slots de notre application avec cette instance. En l’occurence, notre application emet le signal volumeChanged(int), nous le lions au slot setVolumeLevel(int) de notre adapteur. Le signal Ă©mit par l’adapteur est rĂ©cupĂ©rĂ© dans un slot de notre application.

Qt4 et le design pattern Command

Bonjour,  ce petit tutorial va tenter de vous expliquer comment réaliser une opération longue tout en restant réactif dans une application Qt4.
Il existe plusieurs moyens mais je ne vais en dĂ©crire qu’un seul qui Ă  mon sens est le plus propre. Comme cas pratique, j’ai choisi d’implĂ©menter le Design patterns “Command”.
La première étape utilise des thread.

Communication entre un thread et une application Qt

Pas de solution miracle, pour faire “deux choses Ă  la fois” dans une application, il faut passer par du multi-threading. Nous allons crĂ©er une classe thread qui hĂ©rite de QThread. Cette classe sera le support du traitement long Ă  effectuer. Voyez ça comme une sorte encapsulation.

//.h de la classe

#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include "command.h"
class Thread : public QThread
{
    Q_ObJECT
        COMMAND* mycommand;
        bool undo;
public:

    Thread(COMMAND* mycommand,bool undo);

    protected:
        void run();
};

#endif // THREAD_H

Comme vous le voyez, c’est très simple. Nous hĂ©ritons de QThread, nous avons deux membres dans cette classe: un pointeur vers une instance de la classe COMMAND (utile pour l’implĂ©mentation du patterns du mĂŞme nom) et un boolĂ©en qui nous permet de dĂ©terminer le sens de l’action “annuler” (undo) ou “normal” (undo == false). bien entendu, il est possible d’avoir autant de paramètre que vous le souhaitez. Il ne faut pas oubliĂ© de redĂ©finir la fonction run().

// l’implĂ©mentation

#include "thread.h"

Thread::Thread(COMMAND* _mycommand,bool _undo)
        : mycommand(_mycommand),undo(_undo)
{

}
void Thread::run()
{
    if(undo)
        mycommand->undo();
    else
        mycommand->doCommand();
}

Dans le constructeur, je définis les données membres. La fonction run exécute la commande en fonction du paramètre undo. Il faut savoir que tout le code appelé dans run sera exécuté dans un thread différent.
Ă€ ce stade, nous avons une classe Thread qui est prĂŞte Ă  recevoir et exĂ©cuter une “command”.

Nous allons voir maintenant comment créer un design pattern command.

Implémenter le design pattern command

Ce pattern est très utile pour gèrer l’annulation d’une action ou par exemple refaire la dernière action. Il consiste Ă  crĂ©er une classe pour chaque action (du moins toutes les actions que vous voulez pouvoir modifier ou dans notre cas, exĂ©cuter dans un thread). Qt fournit des outils pour faire ça. Dans un cadre formateur, je prĂ©fère l’implĂ©menter entièrement.
Il faut tout d’abord Ă©crire la classe abstraite qui dĂ©finit une commande. Cela permettra Ă  notre classe thread de bien intĂ©ragir avec la commande.

#ifndef COMMAND_H
#define COMMAND_H
#include <QObject>
class COMMAND : public QObject
{
        Q_ObJECT

public:
    virtual void doCommand()=0;
    virtual void undo()=0;
signals:
        void Maximum(int M);
        void Minimum(int m);
        void valueChanged(int v);
        void done();
};

#endif // COMMAND_H

Il y a deux mĂ©thodes abstraites pures: l’une pour faire la commande, l’autre pour l’annuler. J’ai ajoutĂ© quelques signaux pour que la commande communique avec le thread principal pour informer l’utilisateur de l’avancĂ©e du la tâche en cours. Toutes les futures commandes de notre application doivent ĂŞtre des sous-classes de COMMAND. Vous l’avez devinĂ©, il faut implĂ©menter une commande, maintenant.
Notre commande sera vraiment basique, c’est une commande d’attente (so useless).

//wait.h

#ifndef WAIT_H
#define WAIT_H
#include "command.h"
class WAIT : public COMMAND
{
public:
    WAIT();

    virtual void doCommand();
    virtual void undo();

};

#endif // WAIT_H

Rien de particulier, juste la re-définition des fontions virtuelles pures. Voici leurs implémentations

#include "wait.h"
WAIT::WAIT()
{

}

void WAIT::doCommand()
{

int step = 10;
        emit Maximum(100);
        int i = 0;
        int k = 0;
        emit Minimum(i);
    for(int j = 0 ; j< 1000 ; j++)
    {
        sleep(0.5); //fake statement
        if(i>=step)
        {
                emit valueChanged(++k);
                i = 0;
         }
        i++;

    }
    emit valueChanged(++k);
    emit done();
}

void WAIT::undo()
{

}

Dans notre “doCommand”, nous calculons le pas de la notification de l’application principale. Ici, j’ai arbitrairement choisi 10 mais dans un contexte utile, le pas est Ă©gal Ă  la taille de vos donnĂ©es Ă  traiter divisĂ© par le nombre de notification que vous voulez.
Nous Ă©mettons la valeur maximale. Initialisation des variables temporaires (i et k). On Ă©met i (0). Nous faisons une bouble sur chaque Ă©lĂ©ment Ă  traiter, on fait le traitement et on calcule un peu pour savoir s’il faut ou pas prĂ©venir l’application principale.
Il est bon de ne pas prĂ©venir Ă  chaque fois, car si vous travaillez sur plusieurs milliers ou millions de donnĂ©es le traitement des signaux ralentira un peu votre application. Comme exemple, imaginez que vous travaillez sur chaque pixel d’une grosse photo.

Il ne reste plus qu’a Ă©crire la fenĂŞtre principale qui affichera Ă  l’utisateur l’avancĂ©e de notre traitement.

QProgressBar et QThread.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include <QProgressBar>
#include <QDockWidget>
#include "thread.h"
namespace Ui
{
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QProgressBar *workinprogress;
    QDockWidget *Progressdock;
    Thread* myThread;
};

#endif // MAINWINDOW_H

Rien d’original, une mainwindow avec juste trois membres. Une QProgressBar qui affichera la progression.
Un DockWidget pour afficher la progesse bar, je ne l’ai pas implĂ©mentĂ© dans cet exemple mais il peut ĂŞtre amusant d’afficher le dock quand une opĂ©ration est en cours et la cacher quand c’est fini. Le dernier membre est une instance de notre classe Thread.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "wait.h"



MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    workinprogress = new QProgressBar;
    Progressdock = new QDockWidget(tr("progress panel"));
    workinprogress->setValue(0);
    Progressdock->setWidget(workinprogress);
    Progressdock->setAllowedAreas(Qt::BottomDockWidgetArea);
    addDockWidget(Qt::BottomDockWidgetArea,Progressdock);
    WAIT* mywait = new WAIT();

    myThread = new Thread(mywait,false);
    connect(mywait,SIGNAL(Maximum(int)),workinprogress,SLOT(setMaximum(int)));
    connect(mywait,SIGNAL(Minimum(int)),workinprogress,SLOT(setMinimum(int)));
    connect(mywait,SIGNAL(valueChanged(int)),workinprogress,SLOT(setValue(int)));

    connect(ui->pushButton,SIGNAL(clicked()),myThread,SLOT(start()));

}

MainWindow::~MainWindow()
{
    delete ui;
}

Nous initialisons la QProgressbar et le QDockwidget. En suite, je paramètre un peu nos deux instances. Je crĂ©e alors une instance de WAIT et j’initialise le thread avec en paramètre l’adresse de notre commande.
Je connecte maintenant les signaux de la commande au slot de la QProgressBar.
Finalement, je connecte le clique sur “pushButton” sur le dĂ©marrage du Thread.
Ainsi, Ă  chaque clique, il demarrera la commande wait.

Aller plus loin

Il serait prĂ©fĂ©rable de crĂ©er une instance de WAIT Ă  chaque clique et de l’ajouter dans une pile (dans la mainwindow). Cela est nĂ©cessaire pour finir le design pattern command. En haut de la pile, se trouve la dernière commande, si elle est annulĂ©e alors il faut la dĂ©piler et exĂ©cuter undo().

Je proposerai en tĂ©lĂ©chargement ce petit exemple, d’ici quelques jours. En attendant, une petite capture d’Ă©cran. Capture d'Ă©cran Design Pattern Command, QThread et Qt.