{"id":46,"date":"2016-08-25T15:24:29","date_gmt":"2016-08-25T15:24:29","guid":{"rendered":"http:\/\/liberty.fdn.fr\/?p=46"},"modified":"2025-08-17T20:12:33","modified_gmt":"2025-08-17T20:12:33","slug":"make-your-own-lide-show-presentation-in-qml","status":"publish","type":"post","link":"http:\/\/renaudguezennec.eu\/index.php\/2016\/08\/25\/make-your-own-lide-show-presentation-in-qml\/","title":{"rendered":"Make your own slide show presentation in QML"},"content":{"rendered":"<p>To prepare my conference at <a href=\"https:\/\/www.youtube.com\/watch?v=FAMBDJ9Tkf0\" target=\"_blank\" rel=\"noopener\">Pas\u00a0Sage En Seine [FR]<\/a>, a French hacking festival, I chose to write my slide presentation in QML.<br \/>\nIt allows me to have better control and be free to do whatever I want (such as a timeline or any kind of animation).<br \/>\nOf 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.<\/p>\n<h2>File hierarchy:<\/h2>\n<p>I preferred use QML through C++ Application. It provides more helpful feature, such as:\u00a0the ability to make screenshots of your presentation at any time (useful as backup plan).<br \/>\nAt 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.<\/p>\n<pre>\u251c\u2500\u2500 cpphighlighter.cpp\r\n\u251c\u2500\u2500 cpphighlighter.h\r\n\u251c\u2500\u2500 deployment.pri\r\n\u251c\u2500\u2500 LICENSE\r\n\u251c\u2500\u2500 main.cpp\r\n\u251c\u2500\u2500 main.qml\r\n\u251c\u2500\u2500 pages\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 01_intro.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 02_presentation.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 03_jdr_et_rolisteam.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 043_Exemple_code_1.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 04_jdr_avantages_pb.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 05_avantage_jdr_virtuel.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 06_fonctionnalites_rolisteam.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 07_rolisteam_debut.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 08_Rolistik_a_Rolisteam.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 10_frise_chronologique.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 11_son_usage.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 12_son_fonctionnement.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 13_dice_parser.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 14_themes_audio_3_pistes.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 15_nouveaute_1_8.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 16_projet_avenir.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 17_reussites.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 18_les_lecons.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 19_objectif_rolisteam_libre.qml\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 20_FAQ.qml\r\n\u251c\u2500\u2500 pasSageEnSeine.pro\r\n\u251c\u2500\u2500 pasSageEnSeine.pro.user\r\n\u251c\u2500\u2500 qmlcontroler.cpp\r\n\u251c\u2500\u2500 qmlcontroler.h\r\n\u251c\u2500\u2500 qmlcontroler.ui\r\n\u251c\u2500\u2500 qml.qrc\r\n\u251c\u2500\u2500 README.md\r\n\u251c\u2500\u2500 rsrc\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 all.png\r\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 cc.png\r\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 chat.png\r\n<\/pre>\n<h2>The C++ application<\/h2>\n<h3>The main<\/h3>\n<p>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.<br \/>\nThe first goal is to show the QML view, then add some features and communication between the QML view and the C++ window.<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;%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&#8221; message=&#8221;main.cpp&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>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.<\/p>\n<p>In this project, the QmlControler class is the C++ window which provides slide feedback and additional information.<\/p>\n<p>Let&#8217;s take a look to it:<\/p>\n<h3>Feedback window<\/h3>\n<p>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.<\/p>\n<p>[pastacode lang=&#8221;cpp&#8221; manual=&#8221;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(&#8216;0&#8217;))%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&#8221; message=&#8221;Current slide has changed&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>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.<\/p>\n<p>Saving screenshots into file allows you to create a pdf file as backup plan for your presentation.<\/p>\n<blockquote><p>$ convert *.png mypresentation.pdf<\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<h2>QML Application<\/h2>\n<h3>Loader system.<\/h3>\n<p>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.<br \/>\nThe main.qml displays all pages as item of a <a href=\"http:\/\/doc.qt.io\/qt-5\/qml-qtquick-pathview.html\">pathview<\/a>. All items are loaded from the qt resource management system.<\/p>\n<p>[pastacode lang=&#8221;css&#8221; manual=&#8221;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&#8221; message=&#8221;First item of the model.&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>A page is mainly defined by two data: name and path. The path is the name of the qml file.<br \/>\nAll other data are here as help, the time has not been used.<\/p>\n<p>Then, the loader does its job, the key lines are the following:<\/p>\n<p>[pastacode lang=&#8221;css&#8221; manual=&#8221;%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&#8221; message=&#8221;Path View&#8221; highlight=&#8221;source: &#8220;pages\/&#8221;+path&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>&nbsp;<\/p>\n<h3>Table of Contents<\/h3>\n<p>To manage the table of contents, I added a listview with a model:<\/p>\n<p>[pastacode lang=&#8221;css&#8221; manual=&#8221;%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&#8221; message=&#8221;Table of contents in QML &#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<h3>Next slide<\/h3>\n<p>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.<\/p>\n<p>[pastacode lang=&#8221;css&#8221; manual=&#8221;%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&#8221; message=&#8221;Next slide&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<p>&nbsp;<\/p>\n<h2>Design a page<\/h2>\n<p>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.<\/p>\n<p>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.<\/p>\n<p>For example, the feature of dice alias has 10 as index. When the index page value becomes 10, the \u00abDice Alias\u00bb\u00a0 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.<\/p>\n<h3>Position and Size<\/h3>\n<p>To ensure that all items will be display at the proper position and size.\u00a0 I have based all computation on anchor or the screen size.<\/p>\n<p>[pastacode lang=&#8221;css&#8221; manual=&#8221;%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&#8221; message=&#8221;Display the logo at the right position and size.&#8221; highlight=&#8221;&#8221; provider=&#8221;manual&#8221;\/]<\/p>\n<h2>Other way<\/h2>\n<p>There is a module that provides Items to create QML presentation. I don&#8217;t use it for this one but it may provide interesting things.<\/p>\n<p><a href=\"https:\/\/github.com\/qt-labs\/qml-presentation-system\">https:\/\/github.com\/qt-labs\/qml-presentation-system<\/a><\/p>\n<h2>Get the code<\/h2>\n<p>You are invited to clone the code at : <a href=\"https:\/\/github.com\/obiwankennedy\/pses\">https:\/\/github.com\/obiwankennedy\/pses<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>To prepare my conference at Pas\u00a0Sage 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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[81,23],"tags":[34,10],"class_list":["post-46","post","type-post","status-publish","format-standard","hentry","category-en","category-tutorial","tag-qml","tag-qt"],"_links":{"self":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/46","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/comments?post=46"}],"version-history":[{"count":13,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/46\/revisions"}],"predecessor-version":[{"id":5078,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/posts\/46\/revisions\/5078"}],"wp:attachment":[{"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/media?parent=46"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/categories?post=46"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/renaudguezennec.eu\/index.php\/wp-json\/wp\/v2\/tags?post=46"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}