Good morning, everyone, and welcome! We have a very special workshop for you today by Giuseppe D'Angelo called Back to Basics: writing a model. Just a moment... we see some people coming in. All right. Hi, everyone. Good morning, good morning. Hello. Welcome, everyone! Happy to have you here at Qt Day! Okay, fantastic.
We're going to do a workshop today, Back to Basics: writing a model, and let me go ahead and introduce you all to our workshop speaker today, Giuseppe. Let me bring him live for this part. -Here we go. Hi, Giuseppe! Glad to have you here. -Thank you! Good morning, everyone. -Good morning! Before we get started, I'm just going to tell them all about you, a little bit. Giuseppe is an improver of the Qt project and a software engineer at KDAB. He is a long time contributor to Qt, having used Qt and C++ since 2000. His contributions in Qt range from containers and regular expressions, to GUI widgets and OpenGL.
A free software passionate and UNIX specialist before joining KDAB, Giuseppe organized conferences on open source around Italy. He holds a Bachelor's in Computer Science and, about our workshop today in 2020, let's go back to basics. Writing model and view code is somehow taken for granted. It's, however, a mandatory skill for any proficient Qt developer. Professionals are supposed to be able to write robust, fast, and tested models. And so we'll see how to do
that together in this workshop. And any non-trivial Qt application will feature usages of the model/ view controller framework. And MVC is a well-known design pattern embracing separation of concerns between managing the data and the user interface that is supposed to visualize and manipulate it. So, what exactly is a model in Qt's MVC design? How do we write one? What is the developer's responsibility, and what, instead, is provided by Qt, maybe as convenience? What are the performance characteristics of the models, and how do we test one? So, the answers are not so trivial and it's important to nail them down to write robust model code that will drive the rest of our user interface. So, in this workshop we'll write together some models, use them in combination with some other views and, while doing so, show all the best practices associated with such development. Alright. So, I hope you all enjoy. We'll have a short video and then get started. So, talk to you soon!
[intro music] Okay. Here we are. Let me just get started and share the presentation slides. Okay. That's very good. Fantastic. Alright. So once more, good morning, everyone. Thank you very much for the kind introduction to me, and to the workshop. This was actually the subject of the first couple of slides I had here, regarding presenting myself.
I'm kind of a old-timer here at Qt Day. So, I work for KDAB and I write a lot of code related to Qt. So, I decided, this year, not to talk about something new, something bleeding edge, something that is coming with Qt 6. Instead, I decided to go back a little bit and focus on something that I find foundational. And the thing I found foundational this year was writing model/view code. Why is that important to me? Why does this kind of code have this special place in my heart? Well, because I think that, as someone who uses Qt everyday, it's extremely important to understand what are the basic foundations of Qt programming, because the basic stuff is used literally everywhere, even more than the bleeding edge stuff. Maybe you're not running Qt 6 but
might be running Qt 5.15. So, let's focus on something which is there which has been there for a very long time and I don't believe it should be ignored. I believe that this stuff needs to be discussed more and thought more and even improved. And although what I'm going to talk to you about today is model/view programming and model/view programming is a very old thing in Qt, it does not mean that it's somehow perfect and there's nothing to improve there. There is a
lot to learn, regarding the basics. There are still some rough edges around Qt and there is still some evolution happening. Even in Qt 6, we're adding new stuff into model/view programming. So why, specifically, I decided to talk about model/view is model/view is a central framework inside of Qt. Something that I always say when I give this kind of trainings and I give this kind of workshop is that any non-trivial Qt application will use some model/view and some applications actually use model/view, extensively. In any application you're going to write using Qt, there is going to be something which is a list of things, or a table of things, or a tree of things, and these will be data-driven. So you may have a list of wi-fi networks somewhere that you gather somehow and then you need to show it in your UI.
so uh if you're a developer and using qt maybe you may not end up writing view code maybe you will have designers handling the presentation part of this data but as a developer you will write models you will write lots of models so it's i believe important to master this this should be like not an acquired skill anymore you should become part of your daily life using cute you should not be afraid of writing one extra model if you need that so what are going to see today i've prepared a little agenda uh so the first part of this workshop we are going to introduce a little bit of theory so what is about all about motor view programming acute i will start really really from the foundational parts i will start with the definitions and the basic concepts what is a modeling cube that is a good question we need to answer that question what is a view what are the responsibilities of the model what are the responsibilities of the view and then i will focus exclusively on the model part of things so uh what is the structure or modeling qt what kind of data can be represented by a model and how do we access this data into a model uh this will naturally go into discussing the base class of all models which is this class called the abstract item model we will have an overview over over its api and then we will switch into kind of a hands-on part so uh i will do some live coding yes i will risk uh my career on this and we will actually together create a list model uh which is the simplest model that we can create but that will actually uh make us learn a lot about modern design because lots of things we're going to see in there actually apply also to other kind of models so we will spend quite some time creating one refining one trying to understand what are all the implications of its api and uh in its first iteration we are going to just create one which is like read only and then we will change it to one which is actually modifiable somehow so we'll see how we can handle modifications of the model data and then time permitting we can see how this can evolve into something more complex so stuff like table model or tree model or other other more complicated model concepts okay so i've got some slides at the end time permitting so without further ado let's get cracking and let's start discussing uh the very basic steps about model view programming uh in acute okay so uh what uh what are our goals for now our goals for now is if you want definitions we want to understand uh yeah i've used the word model a lot but what is a model exactly what is a view exactly uh if you ever encounter the model view apis in qt you may also wonder what is a delegate and if you have used model view in other frameworks than qt sometimes this pattern is not called model view delegate it's called the model view controller so what is the controller thing right what is that and how do i use it so let's start by defining what a model is and what it does okay a model in qt is better thought as a class which functions as an adapter class an adapter between two words between two sides on one side the job of your model is to access some data so you have some data somewhere i'm going to discuss in more detail what is this data about but you have some data that you want to access and the job of the model is to access this data and that's one side of things on the other side the model exposes this data using a standard unified api which is the api of this class in qt called q abstract data model okay so it really functions as if you want an adapter something that takes data which is customized can have different shapes different forms and exposes it in a unified way okay what's the idea of having such an adapter that is that once i have a model once i have such an adapter then the upper layers typically the views can simply target the q abstract item model api and therefore they can they can completely ignore the underlying details about how the data is actually accessed because these details are completely encapsulated by your model so if i have to represent this graphically i would probably use something like this okay so you have your data on the bottom that's your data you only know how to access it uh on the middle we have a model which is a key abstract tag model subclass something that we are going to write in a short while and on the top you have something that uses this model somehow okay so you may have for instance if you're writing a widgets application you may have a widget view that shows the data in the model if using cute quick you may have acute quick view that shows that in the model but perhaps you may also not have a view directly you have some other c component that gathers the data through your model so you have some piece of logic that analyzes the data that filters the data that does something with it okay and it knows how to do that because it simply uses your model as a to abstract data model so it implements the api that is required okay so this is what a model is and i believe it's extremely helpful to think of its responsibility like this okay as this intermediate layer between the actual data and uh generally speaking the views what is a view so i've just mentioned them let's define them uh a view is a graphical component of some sorts that displays the contents of a model right so it's a widget if we're using widgets or it's an element a component if we're using qt quick and the job overview is to display the contents of a model so uh when we use cute cute actually ships with many built-in views uh in widgets and cute quick that we can just grab and use because they just work i have a couple of slides here just to give you an overview of what kind of views you get out of the box when you use cute so if you're using widgets because maybe you're writing a desktop application uh the views that you're likely going to use are one of these three we have a so-called queue list view which is a widget that simply simply shows a list of elements like here on the right uh we may have a tree view a cube tribute to say it all which is the one in the middle and as you can see this actually shows a tree like structure and also has several several columns okay so this now looks like a file manager of some sort and then we have a cute table view and a queue table view is a table which looks like a spreadsheet if you want this look it's the one on the bottom looks like a little bit like excel or something like that there's also some other but they are really seldomly used so i'm not even going to mention them these three are by far the most common ones if you said you're using cute quick you also have views in there so if using it quick you may have something like a list view the one on top that simply arranges the elements in a list you have a grid view the one in the middle i mean just the elements in a grid you have a path view which arranges elements across a path like this one on the bottom you might also decide to use a repeater which is a special q-tweak component that gets a model and simply creates a number of objects out of that model and typically these gets combined with something to position the elements something like a row or a column those are elements into tweak that allow you to lay out the objects created by the repeater okay so a view is just this is something that gets used to visualize the contents of a model we create one of these and we show it and that's it today we're good to go oh sorry uh yeah in kid quick uh i believe uh in 5.14 or something like that 5.12 we also have a table view now and on the cute marketplace there is now also treeview element okay so this shows like a table and this shows like a tree so there is something important to say about views and cute at least the idea about the view in cube is that the views read the data from the model but they don't actually display the data themselves they don't display the data directly uh the view uh the main purpose of a view is actually to lay out other objects that we're going to call delegates and these other objects are laid out of course according to the kind of you were using so if you're using a list view then we lay the delegates one below each other right vertically if we're using a table view then we actually have a two-dimensional grid of delegates and what is the point of this other object well the point of a delegate is actually having an object whose responsibility is to draw to edit and to interact with one single piece of data okay so if to represent this graphically and to understand the response of different responsibilities between a delegate and the view suppose i have this list view here represented in the middle okay so a list view as a whole has an overall idea of a view over the model so what it needs to do is to for instance provide scrolling provide an idea of having selection or a current item okay uh but a list view itself does not actually draw any of these lines the lines themselves are drawn by something else something we're going to call a delegate and the responsibility of a list view is simply to lay down these delegates since it's a list of you vertically okay and then it tells delegate draw the first element draw the second element draw the third element in this position that's what a listview does there are some minor differences between how this works in a few widgets and into quick but the theory is pretty much the same so regarding views and delegates uh there is something to say here when we use cute model view we typically don't create views in the sense that we don't create our own customized view we just use the ones that come with like youtube right so if if cube provides a list view we're just going to use a list view element we create one and we go with it but we don't create a brand new view and a new view class that should i say the reason is that typically we don't need any of that okay however sometimes we may need to customize the look and feel of the elements inside a view and for doing that sometimes we're going to create a custom delegate and set it on the view uh when do we do that when do we create custom delegates this depends so if we're using kit widgets there are actually built-in delegates that just work okay i can create something like a cue list view as i'm going to do in a second and i just show it and the cue list view actually shows something on the screen it could quick we are not so lucky in the sense that if i'm using it quick and i create something like a list view a list of inquid quick does not have a built-in delegate and we must write one ourselves so we must write some code which is responsible for dealing with the drawing and interaction with each line inside a list view okay uh q two controls true has some delegates that we may decide to just use but those are like nothing too quick they're in this controls library uh by all means use them as starting points if you want uh but then [ __ ] quick sometimes requires us to actually write our own delegates okay but this is not the focus of today the focus of today is writing models not delegates okay and what about the controller so if you're coming from another framework you may wonder where is the controller well there isn't a controller uh as traditionally uh as is intended traditionally uh in the cute motherboard framework uh the traditional responsibilities of a controller are a bit split between the view and the delegate itself but that's fine it's just a matter of terminology it's just a matter of understanding who does what in qt okay and this diagram on the right should make it clear about who does what okay so to summarize this little bit of theory we have three things and one models models are just for the data number two we have views okay and the point of review is to display a model number three we have delegates okay delegates are objects which are sometimes created created destroyed but especially laid out by views and the purpose of a delegate is to draw one single unit of data and to manage the interaction with that unit of data okay that should be fine so let me actually show some of this in action if i manage to share my screen and voila and switch back to uh switch back to kit creator so we can actually see a little bit of this theory uh practically done in practice so what i have here is a little a little application that creates a model right here on line 34 i'm creating this simple model called the string list model and uh i'm populating it with some color names and then i'm creating two different views to showcase that model the first view is created right here line 37 in which i'm creating a cue list view so that's a q widget view i'm placing um setting this model on it and i'm showing it okay these are these three lines uh i'm also using the same identical model in qt quick so in order to use this modeling kit quick i'm doing something else which is first for most i'm exposing my model using in kit quick so uh this is not a kid quick course but if you want to know more please google this when i have some time uh this is a way to expose my list model object in qml and then down here i'm going to actually load some qml and show it so what is inside this main qml well let's find out so inside that main qml i have now a cute quick list view right that's a list view so it's a kid quick component its model is going to be my model coming from c plus plus so it's the same list of strings and in cute quick i also have to specify this object called a delegate which is responsible for drawing each line inside my list view so let's say that i want each line to be like a rectangle whose color is alternating and inside that rectangle i got something else i got some text i got some other elements this is all i need to write manually or sometimes my designer writes this for me because i'm not that good at actually writing uis but this is required for listview to actually show something on the screen because listviewingkid quick does not come with a built-in delegate if i run this i end up with these two views showing the same model so on the left i have my list widget cue list so it's a cue list view it's a widget coming and on the right i got a list view in gmail okay you can see this qml it's got all this fancy kinetic scrolling and whatnot right and i had to specify exactly how to draw each and every line but the contents of these two views are actually the same they both feed from the same model so as you can see there are the same names in both lists okay all right back to the slides here we go so that's a little bit of theory about the modern writing now let's go more in detail uh and focus on what is a model exactly and how does it look like what is the structural model okay so i'll go back to this slide and let's remind ourselves once more what is the responsibility of a model okay the responsibility of a model is to access the data and you know the data you know what the data looks like okay and expose this data to views and to other consumers to anything you want using the abstract item model public api that's the responsibility of the model so what do i mean by data i keep using this word what is data data data okay so the data itself can really be anything you want or almost anything you want data is something which is specific to your application and it can be something that you use in your business logic okay so it could be something like an in-memory data structure like a vector of objects that you want to represent in some view maybe a tree of objects a tree of nodes who knows it could be not in memory maybe it's on disk the contents of some file that you have and you want to represent it into a view maybe it's not even in a file but it's like metadata for instance the file system structure below a given directory so you got like a root directory and inside a directory you have files and you have directories which also have files inside okay this is kind of metadata of the file system it may not even be local maybe you won't represent uh the contents of a table in a sql database elsewhere or maybe the results of some sql query that's fine you can represent this inside uh acute model view maybe the result of a rest call to some cloud service okay this doesn't matter uh for us oh this doesn't matter for cube why does not match for qt because as the author of your models you know how to actually access the data you know what kind of library you have to use you know what kind of sql queries you have to run in order to access the data that's the responsibility of your model and that's what you need to do so inside your model you're supposed to write any sort of glue code which is nec required to access your data and present it to the views using the q abstract atom model api now i said almost anything why almost anything well because uh typically in a user interface we don't actually have like arbitrary data visualized uh cute models are able to show and describe these kind of data structures and only these kind of data structures that these cute models are able to describe things like lists of elements so if you want one-dimensionals things that just have rows they are able to represent tables of data so two dimensionals you have rows and your columns and also trees of elements so you have rows you have columns but also at each level you may have for the children now all right good models can describe these because these are the ones that you actually have typically in your user interface some other fancy data structure like i don't know a graph or something like that sorry that that's not so common you don't represent it using the abstract data model you need something else okay now let's look at this picture on the right right so you see the tree and that looks like a tree yes it's got a root it's got some rose the first row also has children and whatnot but let's move a little bit to the left let's look at the table i saw the table yes it's a rose it's got columns but it's also got this strange root item over there which does not really belong to a table does it and also a list i mean yeah i got individual rows and also the least it's got it's got this root item what's the deal with that well the deal with that is pretty simple uh for cute lists and tables are actually special cases of trees so they are just trees uh without children without further children so a table is just a tree without children just the first layer and the list is actually a table with one column just the first column okay these are considered if you want uh special cases of the more the most general case which is the tree model why does this matter uh why do we care uh that's because these canvas structure is present in the q abstract item model apis but when we use the javascript model apis we will see that everything works thinking of trees even if we don't have a tree even if we just have a list you know we are constantly reminded that uh things are derived from this more generic data structure which is the tree even if we are in a simpler case okay so let's let's start dealing let's start delving a little bit more in depth about uh how do we access the data inside a model okay i got a model it may be a list maybe a tree maybe a table how do we actually navigate into it so the first class that we need to discuss in order to understand that is a helper class that we use in order to navigate a model this helper class is called a q modal index okay and what is a q model index a q model index is if you want a pointer double quote pointer uh pointing into to a specific position inside a model pointing to a specific cell into a model so what we can do in order to access the data would what we must do in other drugs data is first and foremost obtaining a model index pointing to a specific piece of data for a specific row to a specific column that will be the first step and then given a model index we will actually be able to access the data so how do i actually do that how do i get a model index from uh from a model well it works like this okay and uh it's again less obvious than it needs to be because everything is done in terms of trees the first thing that we can do is obtaining a model index pointing to that root item that every model has even known trees i keep insisting on this okay so if i have this table model here on the right okay the first thing i may want to do is to get a model index pointing to the root item of the table model and getting a model index pointing to there is extremely simple all i need to do is default construct a model index so if i write q model index parenthesis okay what i get is a model index that represents the root item of the model okay this third item over here is obtained by simply creating a default constructed model index now i wish that when once you do that i wish that once you write q modal index uh that model index is called a root model index or something like that that is not the case unfortunately for some reason like lost in history the model index that represents the root item it is simply classified as the non-valid or invalid model index okay and in fact if i have a moodle index and i want to know if that model index is pointing to the root or it's pointing somewhere else the call that i have to do to distinguish these two cases is called is valid okay the usual joke at this point is that if i had a time machine i could go back when this thing was designed i probably would not name this as is valid i would probably call this like east root or something like that but that's what we have so it's valid consider these in your mentor model that if you have a valid modeling index it means it's pointing somewhere in the contents if you have an non-valid model index then it's pointing at the root that's the mental model we should have okay so default constructed model index points at the root fair enough how do i get into the table now so the way i get a model index pointing somewhere into the table is by using the index method on the model each model has to provide this index method okay which returns a q modal index pointing to a specific position now this index call takes three parameters it takes the row that you're interested in the column that you want and also of course the parent index okay so the index of the parent element relative to the one that you're asking for so suppose we want to ask about a i want the model index to point to a how do i get that well a is at road zero column zero and what is its parent its parent index is the root so its parent index is the invalid molar index the non-valid molar index okay so a way to ask for the index of a is getting my model and asking for index row zero column zero below the root okay remember what this means the invalidable index means the root to get an index for b this is now row one column one again below the root so one one cumulative index open and close parenthesis and c c is row two column one below the root two common one blood root yup so this is what i meant when i said that the fact that even a table is a tree appears in the api you have this third parameter around which does not make any sense in case of tables but it's still there right and you need to know that it's there and what it means uh let's actually make it to good use let's see another example of getting an index pointing to somewhere but this time if we have a tree so if ever if i actually have a tree model uh now this parent parameter becomes actually meaningful so suppose i want to get the index for b okay how do i get the index for b which is down here in order to get index for b i need to ask for row 1 column 0 below a but wait a minute i don't have the index for a right you need to go one step further so first you need to ask for the for the index of a what is a a is row zero column zero blood root okay i can do that that's the first line the index of a is index zero zero blood root and once i have the index of a i can actually for the i actually actually ask for links of index of b so what i do is it asks for the row one column zero below a okay so the third parameter now the parent index can be meaningful it's not always the default constructed one in trees it's different okay so now i know how to build one uh let me discuss briefly the api of a model index so if i gave a key model index with an index you can actually ask a few questions about it because a model index has somehow a little bit of awareness regarding its position in the model given an index you can ask it what is your row what is your column what is the parent index that uh so if you are if you're a valid index what is your parent this might be the root this may be something else are you the root is valid think think of it this way think of it it's a query if you're asking are you the root index or not okay if you're valid you're not root if you are not valid you are duped and what is your model where do you come from i mean what model created you you can ask all these questions to a model index okay now there is one important thing that serves one slide regarding a model indexes so now we know how to get them uh but there is one important note which is this uh given a modal index a modal index as an object is meant to be created using the index call that we've just seen use it to access a model okay and then immediately discard it uh you're not supposed to store model indexes the reason is that remember i use the word there are pointers that will quote into a model well yes just like pointers they may become dangling they may become pointing to illegal data so you're not supposed to keep them around for longer than strictly necessary sometimes you may have the need to actually keep some of these for longer and for if you need something like that then please do not use a q model index use another class which is very similar it's called a q persistent model index uh the difference between the two is that a q persistent model index does not become dangling like a q model next might and of course there is a trade-off in the other direction using a q persistent model index is much more expensive than using a plain key model index so you shouldn't just use persistent indexes just because you should know when to use them to give you an idea of a possible use case a keyboard system model index is used for instance if you need to track which one is the selected item into a model okay so you have a model and you want to like to know that the fifth element is the selected one and you need to keep track of that somewhere um what happens if the model is modified later and maybe you remove the first row then of course the fifth element becomes now the fourth but it's the same element i need to remember that it's the same element is simply moved okay so the point of a cube system model index that it will follow the element as it moves through the model because the model has been changed a q model index will not follow it and you will likely crash or access the road the wrong data all sorts of bad things okay now let's use a model index to actually access data how do we do that uh there is one extra thing to know before we can actually jump and access data that is each cell inside a model can provide multiple data model viewing qt a model in qt can provide if you want different aspects of the data which is required to represent one single cell let's look at what i have right here okay this is a list of elements if you want but each element has multiple things that they're shown there is a string which is shown here on the right there is also a color next to it okay and if you over lay your mouse cursor over it and wait for a tooltip to appear there is also a tooltip that appears and as you can see it does not even contain the same data which is shown in the model it contains some other data okay so for each cell for each index in the model uh the model may provide different data and how do you distinguish these pieces of data the way we distinguish them is because each different piece of data has a so-called different role okay different roles are used to identify different if you want use cases for the kind for the data that i want i'm requesting from your model uh there are some roles that are provided by qt they have like if you want built-in meanings but you can always provide your own roles in case you need something like that so to give you an idea if i have a model and ask to this particular cell for its so-called cute display role the model is supposed to return the string to display so in this case red as a string okay with uppercase r if instead i ask for the decoration roll that's another piece of information and typically the grocery roll is supposed to return a color or a fix map or an icon which then gets used to draw the decoration next to my text okay and there are many others if you open the documentation of a cute item data role i might do it later that documentation will tell you that cute defines some like 15 20 uh couple dozens of built-in roles with built-in semantics like this okay but the actual role is just an integer you are free to define your own role with your own special meeting meaning if you need to return data from the model that has a special meaning to you okay and now we are ready now i got an index and i know which role i want to fetch data from i can actually go to the model and request some data and way we request some data from the model is by calling the data function so i have my model i call data and data takes two parameters it takes an index and takes the role that i want the data from right i want to focus on this first line because this makes sense right but what is the return type of data i mean data has to return a string if i ask for a string just to return a color if i ask for the decoration so if you ask for the color there how can the same function return multiple data types well say hello to my little friend uh in cute we have this so-called q variant type which is a generic container for uh for types okay inside a q variant you can store any type that you want you can store strings you can store icons you can store fix maps you can store colors and pretty much anything you want can be stored into a variant so model data actually returns a variant it does not return uh something else okay so suppose then i want to ask for the text suppose that i want to ask for the display role of this cell right here i got the index and what i need to ask is model data display role and this returns a variant so then i need to extract the string out of the variant and the way i extract the string at the band is by calling tostring on the variant but through the same mechanism you can return custom data types q variant is actually extensible you can add support for your own data types inside variant so it is legal for to ask your model that index and your specific role and return some specific data here okay once you do that i mean if you have specific roles typically the built-in delegates don't know what to do with your role but you can always write a delegate that also knows how to handle this in a special way okay so to summarize this little bit of theory so far by using cute classes we're using cute model classes such as key abstract item model okay we can describe lists we're going to have tables you can skip trees a q modal index is used to identify one element in the model okay and a non-valid key model index is used to represent the root element and remember that even lists and tables have roots because they are seen as specialized cases of trees now a model uh is composed of cells it's composed by different elements and each element can provide multiple data under different roles yep and we've seen why and how so uh by the way it's time for if there are a few pending questions by all means um uh please ask them in the chat or in the q a room uh so feel free to interrupt me at any time i will from time to time keep an eye on the chat and reply to anything that uh that is happening in there i don't see much activity so i'll just probably continue a little bit then we have a short break let's see and let's start with an overview over the api of a abstract data model so let's see what what a model can do and what a mod is supposed to do the reason why i want to do with this overview and over the javascript at the model api is because then we are going to actually implement some of these functions in order to create our custom model okay so uh as a broad as a broad api what does construct item model need to do in regarding to the views regarding to the upper layers uh cubby the model needs to describe what is the structure of your model and by that i mean things like how many rows do you have how many columns do you have stuff like that it needs to provide the contents of the model as we have just seen through the data and also models are not necessarily static models may change over time okay so when a model changes it must notify the views that something has changed so that the views have a chance to update themselves and therefore the job structure model api also has uh change notifications okay that we must implement uh there is also something else about this that is uh views sometimes may want themselves to change data into a model maybe you want a model which is editable from a view so there is also a writing api not just a reading api that allows of you to change the data contents or change a model structure maybe you want to rearrange rows or something like that okay so let's have a quick glance a quick overview about uh the apis of javascript data model the first thing which is also the first thing that we're going to encounter is there is a number of functions that we have to implement in order to describe our model structure to the views top views may ask how many rows are inside your model and how many columns and actually how many rows you have at each level because maybe you have a tree and the rows below the first child are not the same rows below the second child right so this query is slightly more complex than just asking how many rows do you have but anyhow you have these functions called like row count or column count or has children and so on that views may use to query to ask information about your model you also have the index and the parent calls calls that allow a view to get an index to a specific element so a view asks you how many rows have you got five okay give me the index for row number three here's the index okay this is part of of the scheme between describing a model to the view then of course of you can ask what is the data at one specific index okay because i want to draw it so i need to ask for the data i have seen the call that data that views use to ask for the data is actually called literally data git six this is also augmented by another function called multi-data which allows a view to ask for multiple data in one go for multiple roles in one go there is also something extra for instance some views display headers on the top or on the side so this information is also provided by the model to the views and almost finally there is also something else related to notifying use regarding changes into the model so if something changes into the model the model has to tell the view hey this is happening please update yourself and models have like 20 of something like that different signals to do this notification mechanism so they can tell the view hey look some data has changed in semi-model or hey look some rows have been inserted inside my model you need to refresh yourself you need to update the scrolling position right there's plenty plenty of these signals and we're going to see how to use them also views may want to modify the data inside your model so excuse me so optionally you also have to implement functions like set data or set header data if you want a view to change the data inside your model and finally you may also want to let a view modify the structure of your model for instance by moving a row for some for somewhere to somewhere else okay what is the moral lesson of all this why did i list all of this well because the actual overall api of the abstract data module is huge if you look at the documentation and you look how many virtual functions are inside the object data model there is plenty of them dozens of them okay now please don't get scared about these okay most of those are not viewer virtuals okay most of those are not abstract most of those have a default implementation that does nothing at all okay you're not required to implement them so most of this api is actually optional maybe your model does not need to be modified by you ignore the sectors don't bother with them and there is even something extra on top of this that is uh there are convenient subclasses of club strike data model for some common tasks right and we are supposed to use them because they will simplify our implementation for instance if you are implementing a list or if you're implementing a table as we are going to do uh we are going to use these convenience subclasses that implement something for us and inside cute there are actually concrete classes that sometimes are useful if i just need a model somewhere and i don't want to implement it myself i just want to use one coming from cute so just a quick word about these about these helper classes about these convenience classes if we are implementing a sorry a list model or a table model we are supposed to use the convenience we are supposed to use to abstract list model and capture table model okay these two are sub classes of abstract item model okay and they simply have already some implementation of some methods for handling lists and handling tables okay so most of the mandatory part is still implemented there are just a couple of virtuals that we need to implement in this church and of course we can still implement all the optional parts so stuff like support for modifications if i need a modifiable list model i'm going to sub class to abstract this model and implement just the reminder of the methods to handle notifications and uh to quickly see also some to say something about concrete models so concrete model classes in qt there is some classes that we may just decide to use as is stuff like a q string list model this is a ready made model that simply exposes a list of strings now is this really useful maybe for prototyping yes it's really useful i need i have got some view i want to put some contents in there i don't have the model the actual model yet let's just populate it with a string list model maybe a generalization of this is a something called a q standard item model now this is a complete misnomer because there is nothing standard about it you're not required to use it at all this standard forget about it it's uh it's really i don't know why why it's called like that q standard model is simply a model which can be a list it can be a table it can be a tree and it's a model that you build in program code you create one of these objects and then you say okay please add the 10 rows to yourself and it will add ten rows okay and then you say set the data of the first row to this and it sets the date of the first row to whatever you want okay so this is also useful for prototyping or something like that but there are actually good models models that are useful something like q file system model this is a model that represents uh the file system structure okay so if you have to just visualize some folder structure then don't create the model yourself that's extremely tricky just use q file system model in the qt sql module there is also something called the q sql query model and also q sql table model this can be used to represent the result of a sql query okay and there's even many more available elsewhere so in our kedab kd toolbox library on github or in kde in k8 item models you've got ready-made models to show data so you don't need to implement a model yourself okay sorry we're almost whatever in i would say uh there are any questions let me check not yet uh shall we take just five minutes break so i can get some water to drink and we'll be right back and we actually turn this into hands-on this is a workshop so i'm going to create a list model live and see what does it move sounds good all right everyone as you heard him we'll have a five minute break so it's 59 now and we'll see you at four past the hour have a good break sounds good thank you thanks all right everyone welcome back from the break it's been about five minutes hopefully you got some coffee or a bit of water and are ready to go back so let's go ahead and bring giuseppe back so he can come here um and also i wanted to say i see hi welcome back hello i see that you have a question here from daniele if you wanted to answer that uh yeah i managed to scroll the chat and okay so the question is what about performance system models can have a huge amount of data within a model layer on top of it to view display items yes you can have a huge amount of data within a model remember that the data is not inside the model right the model the data is somewhere and you can have a big data structure somewhere and the model is simply supposed to adapt that data structure for the purposes of a view so yes you can be as big as you need and of course well we're actually going to show this right now but views don't ask for the entirety of the contents of a model views are smarter than that uh they typically just request what you can see on the screen at any given time plus or minus a little buffer but that's not important really uh but by all means they don't ask for the entire contents of the model to to be fetched uh just to show three lines that would make no sense and also another part of the same question what about single notification of let's say 1000 cells each of which can change every 30 milliseconds that is also perfectly possible yes um there should be no major concerns uh of course as i say that uh the the rule of thumb is of course profile profile profile don't just don't give anything for granted and don't assume that the bottleneck is somewhere without measuring it first but that is not absolutely uh a problem you can have you can have a many many notifications uh per second really even more many more than you're saying right here right now uh thousands of notifications every 30 milliseconds that is perfectly doable um as i'm going to show in a second actually uh you could do better than that in the sense that the notification that some cells have changed you can either meet them if you want atomically one self buys one cell but you could also submit them in batches the actual notification takes a range it does not take just the individual cell so you may actually optimize that out and say this one this block of 1000 cells has changed not this cell and this cell and this cell and this cell emitting then 1000 different notifications i don't know if that answered the question but please if you have further questions let's keep them coming and i will continue now if i manage to go back to the slides yep here we go uh by asking uh how about creating a list model so it may sound simple but this allows us to actually explore all these different aspects of creating a model how do we go about it okay and what are the performance implications what are the correctness implications all these sort of small things that uh we have to we have to think about when we design a model okay so how do we go about it how do we start creating a very simple model uh that shows something on the screen uh the simplest one that we can implement is something called a list so what we're going to do is very simple we're going to sub class q abstract list model okay and the mandatory api to get a model done is actually composed by these two methods only these two are pure virtual thinking abstracted model so the first one is something called the row count which obviously as is a way to for the view to know how many rows are inside my model and then the data function want to actually fetch data uh out of my model okay so that's pretty much all we need to do now if we also want to use this model in qml then we also may need to implement these role names function but i'm going to show that uh later i think i got a second slide for that so let's look at let's look at that after we make this one upper running so i'm going to switch now to code share to screen share i'm sorry see if it works okay i hope yes yes that's great okay i'm hoping it's working um but i'm going back to my code okay uh and uh we're going back to uh that very code in which i had a list view and suppose i want to create my custom model for it so i'm not going to use this q string list model i'm going to use my own list model okay just to to populate a list view how do we go about it so okay let's expand this and let's get started as you can see i'm starting literally from scratch okay i'm not doing uh i'm not using any prepaid pre-made stuff because i want to really show you what is the typical workflow in creating a model it's pretty much straightforward work but still it deserves some attention so as i said a model is an adapter so we have to figure out what is our data that we are acting upon let's see let's say for the purpose of the exercise that we start by exposing the data contained into a string list okay so we have a string list is our data okay and we are simply going to expose it through a model okay how does that work so again for this for the scope of the exercise i'm going to put the list as a data member of the model uh this may or may not be the case actually typically the data is not inside the model that is somewhere else and the model just knows how to access it okay but i'm of course i'm not actually doing that directly right now in the short time that we have so let's start actually creating this so i'm just going to give this guy a constructor okay i hope this is straightforward code for all of you this model find and i'm going to populate this list right in the constructor by using some lists of strings coming with cute one that i love to use are the color names uh that are removed from cucumber so these makes my list contain some data that's fine so the setup is done let's implement the queue abstract list model api what do i need to implement as as i said there are two functions saying to implement let me see if this actually works yeah but there is a little bit of a hint inside cube creator if you right click on your class definition you go into refactor and uh insert virtual functions of base classes yep that's exactly what i want and what do i want to add it's already selected it's the row count and the data which are the only pure virtuals they must implement i'm going to click ok voila and they have been added down here raw count and data so this is convenient so i don't get to type them and make silly mistakes in front of a live audience and these are the two really it's the row count how many rows have i got at the data fetch the data for each index under a given row okay so i don't like them down here let me just move them a little bit up right and let's start implementing them so we can discuss uh what what's the deal with them okay by the way i'm sorry but at the moment i don't see the chat because i'm sharing the screen so please i can leave the questions i will switch back to the chat in a second and we can i can address any questions that you may have so let's start by row count how many rows does my model have well i'm supposed to have one row for each element inside my list so i could just return list. yep that would work this is correct though i may ask am i answering it correctly because remember that even this this is a list look at the signature of row count row count takes apparent index because what i'm supposed to answer here is uh it's a broader question is how many rows do you have below each possible index inside your model and all models are trees as far as qt is concerned so even though i have a list it is legally it is a valid question to ask my model okay i understand that you have this many elements let's say below the root but if i ask how about below one of those elements in the list you have does it have children well then the answer can't be no i it cannot be 42 or whatever this size is it must be zero because i don't have children apart from directly below the root so even something so simple like returning a correct row count is actually a contains a little bit of a trap if you want you are not supposed to ignore the parent element you are supposed to honor it by returning the correct information so what i'm supposed to ask here is this is is the parent is the parent that i'll be asked about the root of the list because even lists have a root if this pirate is the root then i'm returning the sides of my list which sits just below the root if the parent is not root so if this parent element is some element it's in my list then i'm returning zero because my list elements don't have further children and what is the query how do i know if the parent is the root remember that the queries to ask if the part is valid or if the part is not valid okay so if the part is not valid then that's the answer otherwise return zero okay this is the correct way to encode to code row count okay so now of you is asking me okay give me how many elements you've got i've got 42 elements perfect give me the data for element number three so in order to give that answer i need to implement this function okay so how do i know which index i mean i don't know something about the index that i'm passing remember that an index remembers its row its column its position in the model so from the index i can know which element of my data of the list i am referring it to yep i can actually extract it right away by saying index.row i don't really care about the column because i already have one column i'm a list right and then all i need to do is extract the list at that position so i'm really using straightforward coding here i'm going to call this like a result or whatever which is list at row yep and then i return this result if i build this these bills eventually no it doesn't oh i'm sorry what what did i do ah here we go with live coding oh oh sorry yeah this creator being bit to gallon and adding definitions out of class that i didn't need okay that's fine it's complaining that i'm ignoring the world well yeah that is something to keep an eye on but this is it really it's it's built so this was the api that i needed to implement now what i'm going to do is to take this this model uh create an instance down here 958 okay and uh use it as a model inside my list view okay so i'm setting it as a model right here and i'm showing the list view i'm ignoring it quick for the moment so let's see what happens when i run this a nice empty list what is going wrong here what i'm doing wrong well remember the warning you are ignoring the role yeah i should not actually be ignoring the role why is that because the the built-in delegate inside a cube list view asks for asks a different kind of data or in order to draw each line of my list view okay and it asks different things under different roles and i am supposed to not ignore the role but return the right thing depending on the role that i've been asked so the delegate may ask okay what's the string that i need to represent that i need to use to draw your element and string so these are color names it's the color blue okay string i need to draw the string blue and then he asks okay what kind of icon do you want and by using a different roles by using a decoration role and my code is returning blue as a string okay the delegate's now slightly confused what kind of font should i use to draw your element and i'm returning the font is called blue see where this is going you know by ignoring the role i'm starting to return garbage to delegate which gets super confused and eventually draws nothing on the screen so i'm not supposed to do that so i'm not supposed to you know the role okay i'm supposed to handle the world correctly but what i want to do here is to handle at least one roll correctly the so-called display roll for the display roll i'm supposed to return a string and i'm supposed that the string is the string visualized by the view and i can then ignore any other role for the moment being uh by returning an empty variant and that means my model does not understand does not handle that particular role so what what this what code looks like here i'm just going for the simplest approach so if the role is not the display road just return an empty covariant okay i don't handle anything but the display row uh more in general you could think of this as a switch statement right on the different roles that you may want to handle uh oops yeah thank you compiler saved me from a silly mistake there okay now it builds and if i run it okay now we're up and running now i got my list of colors okay which is being fed from my model okay so more in general you may want this not to be just an if but you may want this to be a switch over different kind of roles that you want to handle and for each possibility you return the right thing let's make this already a little bit more if you want fancy or complex let's also handle the decoration roll which is the icon that should appear next to each element in my list okay so let's turn this into something better for instance yeah i can do this which is if you want common code and then i put a switch on the wall okay so if it's it's the display roll then just return salt right otherwise if it's the decoration roll and for the decoration we can return different things we can return an icon we call it on a color or something like that let's see turn a color and since my string is a color name i can actually just return that's a cue caller from the result okay and uh sorry okay so i am so i just decide what to do depending on the world asked these returns a string these returns are colored uh otherwise return covalent okay if i don't know the role i don't know how to handle it sorry i cannot give you any data about this run it and now it looks even fancier look at that okay now for each element there is a little bit a little square that's what the cue list view default delegate gives to me right and it's able to handle uh the two different roles that i'm returning yep by having a little color in there wow okay so that was relatively simple wasn't it except for you know these little things that you must be uh that you must pay attention to i'll switch quickly on the chat if there are any questions but i don't see any yeah thank you i i knew about that oh i don't i didn't know about that the compiler knew about that i'm happy about it okay so back to the code uh off we go and let's let's see something else about this uh so uh let let me take these working model and let me also expose it to html to make it to make it possible to use this model from the qml side now there is something peculiar about humal which is this in qml when you write a view
2021-01-27