QtDD – Insights From Building A Desktop Productivity App Using QML

So, it’s six o’clock. Prashanth should be just about to enter. Man, I’m good! There we are! Welcome, Prashanth! We will see a presentation now from Prashanth. Prashanth, over to you. Hi. Thanks Jesper. For the presentation today, I have recorded a video. I saw a speaker at the Qt virtual conference earlier this year use a video for his presentation and I thought it was a very good idea So, that way I can be active on the chat and also later on. And also you know I can avoid things like launch times, and compile times, and crash during demos. So, I figured video works best So, I’m going to play back this video now. I hope you find it useful. So here we go. Namaste. My name is Prashanth Udupa. I’m a software developer from Bengaluru, India. I am co-founder of Teriflix, also creator of Scrite. Scrite is a free and open source screenplay writing app, built using Qt. Thanks to Qt, you can write in English and 10 other Indian languages. You can find a lot of information about how to use Scrite in the help and support section of our website. Please visit scrite.io to know more As a project, Scrite is five months old. I started working on Scrite on the night of 24th March 2020, after our prime minister announced the first lockdown for 21 days due to COVID-19 Personally, for me, Scrite is very different from the apps that I’ve developed before This is my first desktop productivity app. I have been a Qt user since 2001 Much of my career has been about building desktop applications using the widgets library. This is the first time I’ve used QML for the entire UI of a desktop app. What’s even more special for me is that this UI has more to do with keyboard and mouse interaction than touch. Working on Scrite has been a wonderful learning opportunity for me In this talk, I want to share some of the insights I’ve gathered while working on Scrite. Well, there are several things that I would love to share In this talk, I want to focus on these six points Let’s get straight into it. The first insight I want to share is this: When we are building a QML app, we have to break down everything into a model view problem. It was a good idea to do this back in the widgets days as well, but with QML we are forced to break down everything into a model view problem. When I started working on Scrite, I had to break down screenplay into a model view problem and it was not very difficult after all. A screenplay is simply a list of scenes. Put in other words, screenplay can be thought of as a list model of scenes. Every scene is a list of paragraphs, or we could think of every scene as a list model of paragraphs. Each paragraph has a type and some text associated with it. All of this might be getting a little too abstract for some of us. Let me show you a demo and this will become Here, we have a screenplay. A screenplay is play a list of scenes. In this screenplay, we have 376 scenes. Each scene is a list of paragraphs here We have an action paragraph. We have a character paragraph, parenthetical dialogue, action, character, parenthetical dialogue, and so on. So, a screenplay is play a list model of screenplay elements. Each screenplay element holds a pointer to a scene List scene is a list model of scene elements, and each scene element represents a paragraph. It has a type and the paragraph text. Let’s see how we have captured this in the source code. So this is what the screenplay class looks like. It’s a subclass of QAbstractListModel and it’s a list model of screenplay elements. Now, each screenplay element provides access to a scene Width scene is another QAbstractListModel, and the scene is a list model of scene elements where each scene element provides us access to its paragraph type and the text of the paragraph The screenplay element class implements the QAbstractListModel interface. We’re defining a new role called the ScreenplayElementRole. That’s the agreement between our model and the QML UI queries for screenplay element roles from this model. So, if we look at the implementation of row count, it returns the number of screenplay elements in the screenplay. The data function returns the screenplay element object itself against the requested role, and the role names

maps the screenplay element role to the screenplay element role value inside the screenplay model Similarly, in the scene class we’ll also notice the QAbstractListModel interface implementation In Scrite, the UI modules we use are the same Whether we are editing the entire screenplay or we are editing any one of the scenes in the screenplay, this is made possible because of the screenplay adapter class and Scrite. So, this is what the screenplay adapter class looks like It’s the subclass of QIdentityProxyModel and, as with all proxy models, this one should have a source model too. And the way we assign the source model in screenplay adapter is by assigning a value to the source property and you can notice here that the source property is a QObject pointer in the set source function It accepts the QObject pointer as value and it tries to look for a screenplay inside this queue object pointer. If it finds the screenplay, then it’ll hook up to that screen play as the source model. If it doesn’t, then it will take a scene from within the source model, put that scene in the screenplay, and hook up to that screenplay as a source model. So, in summary, this is what we have: both screenplay and the scene class implement the queue abstract list model interface. That means both of them are models and we access the screenplay through the screenplay adapter which is an identity proxy model. That means the adapter is also important. Let’s now look at how we use this in the UI. This is the screenplay editor. This is where one would type the content of the screenplay What we’re looking at is a screenplay of a film in my native language Kannada. As you can imagine, the screenplay editor is a list view of the screenplay model accessed through the screenplay adapter What this list view in the screenplay editor does is it makes use of a delegate to render each scene from the model as items. The blue boxes here kind of showcase what the delegate is doing. It’s rendering one scene from the model. And each scene is rendered as a combination of the scene heading, the character list, and the content of the scene itself. Let’s go look at how all of this happens in the code. So, we’re looking at the screenplay editor.qml file. We’re making use of an instance of the screenplay adapter class that we saw earlier. Now, through the screenplay adapter, we are accessing the screenplay of the script document and, like I said before, screenplay adapter is a model and this model is set on the list view which is the content view. The model and view association is in this line, as you can see here. The delegate that the ListView uses to visualize the contents of our screenplay model is this item right here We’re making use of a loader to determine whether to load a content component or a break component What I did not show you before is that our screenplays can have breaks like an interval, or an act, or a chapter break. If we are rendering any of those breaks, we make us a break component If we are rendering scenes, then we are going to make use of the content component. The content component is basically a rectangle inside this There is a column. The first item in the column is the scene heading. The second item in the column is a text area in which we show the contents The screenplay editor is not the only place where we are showing the screenplay adapter model We are also showing it in this list right here. This is called a scene list. It shows a snapshot of all the scenes in the screenplay, so that I can quickly switch to a scene. Let’s look at this in the source code. So, this is the scene list view We are assigning the same screenplay adapter as a model of this list view as well, it’s just that the delegate changes. Here, we’re making use of a simple rectangle delegate that has just one text inside it and this text constructs the scene heading as the text value to show in the list view It constructs the text as square brackets, scene number, followed by the scene heading text And because this is Model/View, we get some really good functionality for free. For example, I can go to the screenplay editor here and change the scene heading of the scene. So, I can say int and someplace and you’ll notice that the scene heading updates in the scene list view on the left. Also, I don’t have to do anything. It’s all model/view. This is the structure tab in Scrite. I can go to the scene heading menu here and change the color of my scene. Notice that, as soon as I associate blue color here, the same color shows up in the timeline as well. Let me do that once again You see that? And this functionality comes for free, because both the timeline and the screenplay — everything on the screen — are working with the same model This is the structure tab in Scrite here. Not only can we edit the screenplay, but we can also reorder scenes by using the timeline view here. So, to reorder scenes, we provide a simple

drag and drop UI. If I take my mouse to the bottom right corner of the scene box here in the timeline, notice that an icon kind of zooms out. I can then click on that icon and drag my scene to another place in the timeline. As soon as I drop the scene here, the timeline gets updated and the screenplay editor also gets updated. This again comes for free, because it’s all model view If I pull out this panel on the left, I can notice the structure panel. Structure is where we capture the flow of story in our screenplay We start from this scene, and then we explain the scene through track one, come back to the same incident, explain the scene through track two, and then come back to the same incident, and then go to the climax. I want to show you an interesting thing. You’ll notice that the same inciting incident is occurring three times in my timeline They’re all the exact same scene. So, because it’s model/view, I can type something here The same text will show up in all instances of the scene. I didn’t have to do anything extra Why? Because in this one that was the first insight I wanted to share with you in QML, we have to break down everything into a model view problem. And it turns out that it’s not a bad idea at all. Not only will we be able to build great apps in QML, but we also get a lot of additional functionality for free. As we progress, we’ll be able to see more uses of model view in Scrite. For now, let’s move on The next insight I want to talk about is this: For a long time in my career, I have built desktop Qt apps that made use of the canvas framework and later the graphics view framework But graphics view is a widgets class. How do we use graphics view in QML? Well, it turns out that we can creatively make use of flickable item repeater and pinch handler to do a lot of things that we used to do in graphics view and we are using this in Scrite Had we built Scritd using widgets, then we would have probably used the graphics view framework for the structured canvas in Scrite. But since we are using QML, we make use of flickable and French Let me show you a demo and walk you through the code. We have already seen the structure tab before In the structure tab, there is this area here which we call the structure canvas. It’s on this canvas that writers can plot their scenes, drag and drop them into the timeline here, annotate them with text and images, you know, and kind of capture the shape and flow of the story. So I have this non-linear screenplay out here. So, I have two tracks of scenes, and I have dragged and dropped them so that the story kind of flows between these two tracks. Let’s say I wanted to add another scene. So I can go here, zoom in on this a little bit, and then go on this plus icon here, and say i wanted to add a scene. I’ll add a scene here. Let’s say this is a song scene Because it’s a song scene, I want to mark it as such. So I’m going to right click on this element, say mark scene as, and I’ll say song. Now, can you see there’s this song icon that shows up on the bottom left corner of the element in the structure canvas And you’ll see the same icon showing up here, again, model view. So I’m going to drag this song scene and put it on the timeline, and you’ll notice that the song scene kind of showed up in between the scenes in my screenplay editor and my timeline as well And you’ll notice that these arrows kind of readjusted themselves. So I can now go ahead and move the element on my canvas, and you’ll notice that the arrows are recreating themselves We talked about annotations before. There are already a few iterations on the structure canvas now. For example, this is a text annotation. I can select a text annotation, move it around To edit the properties of the text annotation, I can click on this button here and it will open a dock widget in which I can edit the properties of the annotation Let’s say I wanted to create a brand new annotation. So, I’m going to right click here, and let’s say I’m going to select image. Let me add an image annotation We have an image annotation here, and I can add some caption as well. I’ll also add another kind of annotation called a STP website link to add this YouTube video which shows me a location in Himachal Pradesh. Maybe I want to shoot this part of the film in Himachal Pradesh, so I’ve captured it as a note. So, when I click on this preview icon here, what I see is a preview panel which allows me to get a preview of the entire structure and it allows me to navigate through

the structure as well. I have the structured canvas looking very rich and it’s conveying a lot of information about my screenplay. If I was building Scrite using the widgets library, I would have used the graphics view framework. But, since we’re using QML, we can’t use the graphics view framework, fortunately Flickable item repeater and pinch handler provide a set of tools that mimic a large portion of the graphics framework. So, let’s go ahead and take a look at all of this in the source code ScriteDocument is the main document class It is through an instance of this class that we can access the structure and screenplay There are two properties called structure and screenplay. Through the screenplay property, we access the screenplay and we already looked at this class before. Through the structure property, we can access the structure of the script document Structure is a subclass of QObject. It’s not a subclass of QAbstractListModel, like the way screenplay was. It’s just a QObject subclass The structure class has a property called as elements. It’s a list property. Similarly, it also has another property called as annotations, which is also a list property. It is through these two properties that we get access to list of elements and annotations that needs to show up on the structure In QML, models can either be QAbstractItemModel subclasses or they could simply be lists Let’s look at the StructureElement class itself It is a subclass of QObject. What it has are properties x, y, width, and height It is through these properties that we can figure out the position and size of the structure element Annotations are instances of the annotation class, which is a QObject. And what this has is a property called as geometry, through which we can capture the placement and size of the annotation. And the type property reports whether an annotation is a circle or a line or a website link or image or whatever. So, this is the way in which the structure class reports information about elements and annotations Here, we are looking at the StructureView.qml file. This QML component gets created to show the structured canvas aspect of the structure tab Within the StructureView.qml, we notice the use of scroll area. Scroll area is a flickable item. Inside this flickable item, we place a grid background item, which is the canvas part of our structure canvas. It’s a very large item. Only a part of that gets shown inside the flickable. So what we do on the grid canvas is that we keep an item for the annotations layer. All the annotations are showing up in one single item. Within that, we make use of a repeater to loop through all of the annotations and create delegates for each of them. This point repeater will create instances of this loader and put it on the annotations layer. Depending on the type, we create a specific annotation component Similarly, we make use of a repeater to create all of the structure elements as well. The structure element delegate is an item and you can notice in this line here we are capturing the element in question in this particular line. Model data will be pointer to one structure element in the elements list. The way we place these elements spread out on the structure canvas is by assigning values to the x and y property So, we make use of position binder to fetch the elements x and y values and keep that in sync with the items x and y value on the structured canvas. This is also model/view in nature Like I said before, the structure canvas makes use of a scroll area. The scroll area is the flickable item, but we add some additional functionality to make it easy for us to use. We have the zoom in function to help us zoom in. We have the zoom out function. Zoom one is to one function, and a zoom fit function that allows us to fit a given area into the viewport of the flickable. We also make use of pinch handler to handle the pinch gesture on the trackpad for zooming in and zooming out For drag and drop, we take the mouse cursor to the bottom right corner of the element and you’ll notice an icon zooms out. I then click on that element, drag it, and pull it down towards the timeline area. As I hover the mouse over the timeline area, you’ll notice that certain drop

sites become visible. I can drop the element on any of these drop sites and the scene gets inserted into the timeline at that position. What we’re looking at is the image element that shows this icon on the bottom right corner of the structure element in the canvas. This image item has a mouse area which is configured to drag the parent whenever the drag operation is initiated by the user. Now, as soon as drag mouse area dot drag becomes active, the drag dot active attach property in the element item becomes true. At this point, we trigger the drag operation in the windowing system Now, we drag the element over to the timeline, and in the timeline there are drop areas waiting in between every two elements within the timeline Whichever drop area receives the entered, have exited, or dropped, they handle it in these signal handlers. So, this is how we handle drag and drop operation in the structured canvas. In summary, I want to say that, in QML, we can creatively make use of flickable, repeater, pinch handler, mouse area, and the drag attached property to do almost everything that we were doing using QGraphicsView, QGraphicsScene, and QGraphicsItem in the Qt Widgets world. When we are building a UI using QML, for the most part, item, rectangle, image, and other built-in items are sufficient. But sometimes we may have to build specialized items by ourselves, maybe because the built-in items don’t provide the required functionality or because we have to combine a lot of built-in items to offer functionality that a single item could have potentially offered. In such cases, we’ll have to build custom items in C++ and use them in QML. The next insight I want to talk about is this: It’s okay to sub class from QQuickItem and QQuickPaintedItem every once in a while. Let me show you a few places in Scrite where we have done this On the structure tab, we can optionally enable the background grid. The background grid allows us to align elements in a visually appealing way One way to go about achieving this would be to create lots of rectangle items of one pixel height or one pixel width and then place them on a big item. But that would mean a lot of items, potentially thousands and thousands of them Another approach would be to sub class from QQuickPaintedItem and then paint the grid using q painter. That’s not a good idea either, because we would need a very large texture on which we have to paint these lines. The best approach would be to subclass from QQuickItem and then create a branch in the scene graph all by ourselves The structure canvas makes use of a grid background within its scroll area. This grid background is configured by making use of a lot of properties Whenever an instance of grid background item is created in QML, what actually happens is that it creates an instance of the GridBackgroundItem class. This is a subclass of QQuickItem, and in this sub class we are having properties like tickDistance, majorTickStride, majorTickLineWidth, and so on These are the properties that we’re setting in the QML file. The update paint node implementation of gridBackgroundItem actually creates a qst node branch. And it is in this branch that we create the geometry and the material properties of our grid. It turns out that rendering a grid this way is highly efficient and very, very fast Notice these tabs on the right. These tabs make use of curved shapes in Scrite. We make use of a subclass of QQuickPaintedItem to render these shapes. There are a few other places where we have subclass from QQuickItem within Scrite, for example, the item we use for drawing the connector line between elements on the structured canvas That’s the subclass of QQuickItem, but then I think we get the point. It’s okay to subclass from QQuickItem and QQuickPaintedItem once in a while. Whenever we write a reasonably sized app whose UI is in QML, we will end up writing a QObject subclass or two whose signals slots properties and invokable methods are accessed from QML. All of these are aspects of Qt’s meta object system, and that’s the next insight I want to talk about. To write a really good QML app, we have to get pally with QMetaObject and friends

Let me show you a few places in the UI where we make use of the met object system. For example, in this menu we list all the importers, exporters, and report generators. We do this by querying the meta objects managed by the factories that create importers, exporters, and report generator object Let me go ahead and click on the location screenplay option in this menu. You’ll notice a dialog box pops up. This dialog box is constructed at a run time by querying the meta object supplied by the location screenplay generator. In fact, when I interact with these controls to configure the report generator, those configuration options are applied through the meta object system as well Scrite supports transliteration to a set of languages. These languages are captured in this enumeration called as language, within the transliteration engine class We query this enumeration at runtime and populate the language options menu in the UI In fact, whenever we create a screenplay using script and save it, we actually save a json representation of key objects in the application. For example, you can see the structure json object here. This json object captures the properties of the structure instance in the Scrite document class. You can notice all of the elements here, the text and paragraphs and their types, captured very nicely within this json object This kind of QObject to json serialization is accomplished by making use of the meta object system. So, this is how we show the reports menu Inside this menu, we make use of a repeater to look through all of the supported reports and we create menu items for each supported report When any of these items are triggered, we call the click function, which triggers a report generator timer, and when this timer times out it launches a model dialog box with its content as the report generator configuration component. This is what the component looks like. This is what the actual report generator component item looks like Inside the OnCompleted signal handler, you’ll notice that it is making use of a function call to configuration form info to query some information about the report generator This function is implemented to simply call the object configuration form info, which looks like this. We are querying the meta object of the object in question, and from this meta object we are populating a q json object, which contains title, description, information about various fields to show in the dialog box We actually supply two kinds of fields: plain list of fields, and something called as group fields When we query the location report generator, this is what we get as a json response from the function we just saw. Now, the group fields consists of three items: basic, pdf options, and locations. Those are the three pages that we notice in the dialog box. Let’s go ahead and open the basic fields. You can see there’s a text box called watermark text, another text box called common text, and a check box for list characters for each scene. That’s what you get here So, that’s how we are able to dynamically construct a dialog box at runtime, for things like report generators, exporters, and importers Let’s go back to the code and see how the location screenplay generator is actually supplying all of this information. This is the LocationScreenplayReport class. Notice that they’re making use of Q class info to supply some meta data things like title and description. There are properties that can be configured in each of these report generators. These properties also have class informs of the form property name underscore field group, field label, and field editor. Through these, we are able to supply some meta information about the report generator. All of this is then queried at runtime, matched up into the json, and goes into the dialog box. You go to the base class, you can notice basic field group, List characters for each scene as a check box, and you see that’s how this checkbox showed up in the dialog box. We use a similar mechanism for exporters as well. For example, here’s the dialog box for an adobe pdf export configuration, and if we look at the pdf exporter, this is what the class looks like. All of the control knobs that we see on the dialog box are coming from meta object information supplied by this class By creatively making use of the meta object system, we can dynamically generate UI for a lot of objects at runtime

Like I said before, this is the main document class in Scrite That’s why it’s called Scrite document. It’s through an instance of this class that we’re able to access the structure and the screenplay and the other bits that make up the document of the Scrite application. This class has a method called a save. We call this method whenever we want to save the currently edited document Ultimately, this function calls save as, which calls a method called as toJson within the QObjectSerializer class we pass as parameter, a pointer to this Scrite document instance. What we expect this function to do is to serialize this instance of the Scite document into a q json object and return it to us. This json will then save it into a file. Let’s look at how the toJson function works. This is what it looks like. We are querying the meta object of the QObject that is passed to it, and then we look through all of the properties in the meta object. And for each property we create a QMetaProperty, and we fetch the name from it, and we read the value of that property. All we have to do is to store them as key value pairs within the return q json object. If the value is of list type, then we construct a q json array by looping through the list. If the value is of q object start type, then we create a json representation of that QObject. For all other types, we simply store them as key value pairs except when we have custom types. Then, we can register helpers that will help us in serializing and de-serializing custom types. So we have this toJson function, which can accept an object and return a json representation of it. And we also have a fromJson function which can take a json object and then apply those properties onto a given QObject In fact, we make use of the serialization mechanism to offer copy paste on the structured canvas. For example, on this canvas I’m going to select this annotation and click on this copy button in the toolbar. Now, when I go to a text editor and paste the contents of the clipboard, this is what I’ll see. This is a json representation of the item that I just copied. So, when I go back and click on paste, it’s going to create another item from this json representation. We have a class in Scrite called as DeviceIOFactories. This class has three factory objects. There’s an importer, exporter, and report factory. All of these factories are instances of QObjectFactory We initialize these factories by passing a byte array as parameter to its constructor. What this does is it informs the factory to look up the value of the class info against this key in each of the meta objects we add to the factory and use that value for, you know, creating instances against that key, for example. The final draft importer makes use of the value for the format key in the queue class info as final draft. So, whenever I request for creation of an importer against the key final draft, an instance of this final draft importer class is created. The same is the case with the exporter factory reports factory, as well So, once we have initialized the various factories, we register classes with them Here we are registering all of the importer classes with the importer factory, all of the exporter classes with the exporter factory, all of the reports classes with the reports factory The factory classes are instances of QObjectFactory. The QObjectFactory is a typedef. It creates an instance of a template class called QFactory This is what the QFactory class looks like, the way we add classes to the factories by making use of the add class function. The add class function registers the static meta object provided by the class that we added. The add function that we use here is implemented like this: we take the queue meta object query for the class info key which is title or format or whatever, and then we register that with a hashmap, which is this guy here Now, the supported reports method simply has to return all the keys registered in the reports factory The supported reports method is called whenever the value of the supported reports property against Scrite document needs to be read. We query the value of the supported reports property in Scrite document in a repeater here, and use that to create menu items within the reports menu So, when you want to create an exporter, the Scrite documents create exported method accepts

as parameter the format against which we need to create this exporter. And then we call the create method on the exporter factory. Whenever we want to create instances from this factory, we call the create method. We accept the key for which we have to create an instance within this create method We call new instance on the queue meta object associated with that key. For this new instance to work, we have to make sure that the constructor is marked as q invokable method. As you can see in the final draft class here, we have marked the constructor as q_invokable. This is how we are able to make use of the meta object system to support a factory design pattern To sum up, in Scrite, we make use of the meta object system for displaying menus from enumerations and from factories. We use the meta object system to implement the factory functionality itself, and we also use the meta object system to dynamically construct configuration dialog boxes for exporters and report generators, and we use it to serialize queue objects to json, which we then use to provide functionality like load, save, copy, and paste. Like I said before, there is no escape from the meta object system, when we make use of QML. We might as well make really good use of it beyond just using it for creating properties signals and slots. A lot of fun functionality comes for free. Like I said, it pays to get pally with QMetaObject and friends. One of the things that I really missed in QML as a C++ developer is the lack of new and delete operators It is really powerful to be able to create and destroy items at will when you’re developing an application. When I first learned QML, I would creatively use repeater to accomplish this A repeater with model as 1 is as good as a new operator When I change that to 0, it becomes delete. After some time, I created my own QML component, which had a repeater in it with active flag. Whenever I set the value of active flag to true, an item in it would get created. When I set it to false, the item would get destroyed. This was great and I was using it for a long time, until I discovered loader Loader is an item that can, on demand, place another item on itself. It can also, on demand, unload that item. Loader can be used for both memory management and also as an item factory. In Scrite, we use loader for both purposes. Let me show you how This is the main tab bar in Scrite. The contents of each tab are different, and we create the contents of the tab only if it’s the active tab. This is the main tab bar in the UI. It is a row element that places the tabs in a row. The tabs that we’re creating are specified by this array here We loop over these tabs in a repeater and create the tab items. Whenever the user clicks on any of the tab items, it sets the current index value Then, we use that index inside the loader in the content area. Depending on the index, we create the rest of the UI in the window. If a current index is zero, we create the screenplay editor, which is the first tab. If it’s one, we create the structure editor, which is the second tab. If it’s true, we create the notebook, which is the third tab Similarly, we have the side panel here When I click on this button, it pulls out a scene list view. There’s really no need for us to keep this view in memory all the time. We can just create it whenever the user actually requests for it, and then destroy it when the user doesn’t We actually use the same side panel here as well. So, when I click on this button, it pulls out a notepad of sorts where I can type down the synopsis of the scene. And, even in this case, the notepad component gets created only when the side panel is expanded. Otherwise, it’s removed from memory We also use loaders to ensure that contents of dialog boxes are created only when asked for. For example, when I click on this icon here, it launches the about dialog box The contents of this dialog box is created only when asked for. Otherwise, it’s not even in memory We have another dialog box here, which is the settings or the preferences dialog box Again, the contents of this dialog box is created only when asked for This dialog box makes use of a tab view. Depending on what tab is active, the corresponding page is loaded and shown. Again, we make use of loader In this tab view, another place where we use loaders is the dock widget. I can click on this menu item and launch the shortcuts dock widget

The contents of the shortcuts dock widget is created only when the dock widget is visible When I click on this annotation, I see an annotation properties dock widget here I can move this around. We are seeing a lot of different kinds of property editors within this view here. Text provides me a text area. Color provides me with color selector. Fonts provides me with a font select font size, which is a spin box. The font style says bold, italics, underline, and so on. The different kinds of property editors, depending on the type of property we are editing. We are looking at annotation property editor.qml. We are making use of repeater to loop through all of the meta data made available by the annotation. Meta data is an array, contains a list of property info objects We extract the property info object for a specific item in the meta data like this, and then we show the title inside the text and we load the editor for that property using this loader. Notice how we assign a function as value to the source component property of loader This function behaves like a factory. Depending on the type of the property that we are editing, it creates the corresponding property editor. So, this way we can make use of loader as a factory for our items. The last insight that I want to talk about is GammaRay. I’m a Qt and C++ developer and that means I’m a specialist in messing things up. GammaRay is this very cool object introspection tool for Qt applications It can visually point out issues in your applications when things go wrong, and, once an issue is identified, it becomes easy to fix it. Let me show you a few issues that I was able to identify in Scrite by using GammaRay and I’ll also show you what I did to fix those issues I have GammaRay here. I’m going to launch Scrite using GammaRay. Click on this launch button Let me load a screenplay, and notice that there are a lot of these scenes that are plotted on the structure tab here. If I go to GammaRay now, I can go to this section here that says problems, and check all of these guys out, and say scan for problems. It gives me a huge list of items that are visible but are out of view. This is actually a problem. And the reason is because, even though items are not visible, they’ll still participate in the render cycle, which is a very bad idea. If I go to quick scenes here, I can search for canvas and it takes me to the canvas This is the canvas. Inside the canvas, there are a whole lot of items. A lot of these items are not even visible. For example, this item is visible, as you can see here. But this one is partially visible There’s an item floating around here. It’s completely outside the viewport of the user, but it’s still setting its visible flag. Let’s also see this problem by making use of the qsg visualize environment variable. So, I’m going to go to Qt Creator. Go to projects and set the value of qsg visualize environment variable to overdraw I’m going to run the application from within Qt Creator, and now we will see this nice cool overdraw visualization. Let me go ahead and open the screenplay, and you can notice here that all of the elements of the structure tab are always visible. This is really a bad idea. To solve this problem, I created a class called tight bounding box item. It’s a subclass of QObject. This is used as an attached property. That’s why I’ve implemented the QML attach properties function. We also have these two macros here, which makes it available as an attached property. The QML attached properties function is implemented to create a new instance of tight bounding box item for the object passed as parameter here. And, because of this, we are able to create an instance of tight bounding box item for the element item, which represents the structure element. We are assigning the value of the viewport rect property inside the tight bounding box item to canvas scroll dot viewport rect. Now, we pass that rectangle into this property, so the set function accepts this rectangle. In determined visibility, we find out the rectangle that the item occupies, and then, based on the visibility mode, we determine whether the item should be visible or not. So, this way, we’re able to drastically reduce the number of items that should be visible and this time we should ideally notice that the number of items visible on the canvas is really less See that? A lot of items are not visible anymore Right, we’re showing far fewer items than before Another issue I uncovered using GammaRay was something to do with timers. We have a class called the exact later timer. This class behaves a lot

like Q basic timer, but QObject defines it That has a property called repeat and the default value of repeat is false. Let me go ahead and launch GammaRay and show you the problem Switch to this timers section here and then sort by state and I stare in horror to the sheer volume of repeating timers that i have in my application And then I found out the source of this problem I was making use of Q timer, but then by default Q timer’s single shot is false That means Q timers are repeating. That’s not what I wanted. I wanted to do this simply by making this small change. There is a huge improvement in performance. Let me launch GammaRay again Now, when I sorted, I can see that there are very few repeating timers Most of them are inactive. They come on and go as in when necessary, but they are mostly inactive This was another instance where GammaRay saved the day for me. So, that was my talk on the six key insights I’ve gathered while developing my first desktop productivity application whose UI is completely written in QML. Having used Qt Quick and QML for several years and especially within the Scrite project, I feel that Qt Quick and QML can be used to build desktop apps Sure they will look very different from native desktop apps, but that’s probably not a bad idea You can build fresh and cool looking UIs like never before. And, hey, you can do that without using style sheets. Visit scrite.io and download the app today. Take it for a spin. Tell us what you think. We are eager to hear your feedback. Even better, pull the code from github and build it on your systems Take a look at the core. If you like what you see, please consider contributing. We would love to have you as a contributor. Thank you very much for your time today. Namaste Thank you very much. That was a truly impressive presentation. Awesome code there. It’s the first presentation I ever have seen with somebody wearing four different sets of clothes –Yes, yes, my wife pointed that out too. I recorded for several days –So, I’d like to start with the very last question that just came in the second. How many people were working on this project? –So far, it’s just me Well my sincere hope is that some of the people who are visiting the talk will find some interest and want to join in Okay, awesome. You watched the backlog of questions, so I don’t know if you want me to reach up individual questions, or if you already have your favorite among those that came in. –Oh, no. but we can take them from the beginning. However you please. That’s okay. I could answer one question on the chat ,maybe I can just uh We do have another question, that was Sérgio asking if there was any moment that you wished that you’d be using QWidget rather than QML and what was the largest problem you had with QML? The thing that frustrates me the most is not being able to catch errors at compile time. I wait for something to go wrong in order to fix it. So, with Qt 6, hopefully that’s going to get addressed. So, that’s my main issue. But, other than that, I really like the flexibility that QML provides, you know? It’s very smooth. The whole UI is rendered in another thread. I like that you know the animations are neat. Reimagine the way UIs can be presented to users. I really like all of that. My biggest problem is not being able to catch errors at compile time. –Okay, and you mentioned Qt 6. There’s a follow-up question here from somebody else, so let me just see if I can find it again, from Cirche, who said biosyntex highlighting, I guess rootscope excess is often used. This is going to be a problem, he states, in Qt 6. Have you considered the cost of getting rid of globally scoped objects? –No, no That’s a good question. I’m gonna note it down. But, no, I haven’t And then there’s a question here from a person that would like to know if the flicker slash pinch area combo provides the same performance as q graphics view. –I haven’t really measured it as such, but I find it really smooth. I have written graphics view applications in the past and I find this a lot smoother. Especially, I have areas, not in this app, but some of the other apps as well, where when the — maybe even in this app —

active item switches. I have to fly from where the canvas was before to where the canvas should now be and this fly through is really smooth and I don’t have to, you know, bend over backwards to make that animation happen. It’s just a matter of changing the xyz and setting a behavior animation on it. So, I think it’s really, but I haven’t measured the FPS. From the looks of it, it looks great. –Okay, cool. there is a Stan, asking: Is your menu a standard QML menu? Yes. I’ve been…because I just need some additional bookkeeping, but, aside from that, it’s a standard menu, well, QML menu. –And I understood correct that this application is open source, right? –Yes. –So, this almost renders the next question not needed here anymore He writes: Is the object serialization generic enough to be released to the public? Yes. It’s already in public. I use this class in a bunch of other projects, as well. So, I think it’s generic enough, at least for me. But whether it will be usable for all projects across the world, I don’t know. But it’s generic enough. It’s already in public, so you can take a look. –Okay, there’s a guy here asking: I’m a QGraphicsView user. The main issue I have with the move to QML is that I’m unable to show custom paths How do you approach this problem in Scrite where you have circles and connectors between them? So, I guess it’s the connectors he’s referring to. –Right. So, now there’s a shapes module in QML. You can use that But, in Scrite, I don’t use that as such I have my own subclass of QQuickItem, where I’m able to construct these painter paths, and then fill out triangles for the filled shapes, and then render them. I prefer that to QQuick painted item, because QQuick painted item requires you to you can work with small items you know when you go to very large canvases. You cannot have such a big image in memory, so a Q quick item is is more. It’s a much better fit there. So, the way I do this in Scrite is a class on Q quick item, and then use Q painted path, and convert that into geometry and materials in qsg node branch Okay. While this last few questions comes in now, anybody have questions too hurry typing it up I have a question of my own here If you look at the stereotypic QML material out there, for example, the QML videos that KDAB has released, you’ll find that you build up your user interfaces in QML with items inside items, and so on, and see a typical dialog box. You would see that you create the different components, and you go to a QML file and you can see exactly the checkboxes created there, and so on. Being that there’s a different approach of providing the content for this dialog box via the meter object system, why did you did you choose that path? –I did not want to build separate. I wanted all of the exporters and imported dialog box to look very consistent, so that a user uses one of the exporters he knows that that’s likely how all of them will show up And I did not want to build a new dialog box QML every time I added a new exporter. Hopefully, at some point, we’ll have an extensions mechanism for this so that you plug in an extension and you know the dialog box gets constructed automatically So, I wanted that automatic. So, I come from the component framework mindset. So, I wanted to compartmentalize everything –Fair enough. Okay, cool. Any final question? Doesn’t seem to be the case. Thank you very much for a truly interesting presentation