QtDay: Back to Basics: writing a model - a workshop by Giuseppe D'Angelo, KDAB

QtDay: Back to Basics: writing a model - a workshop by Giuseppe D'Angelo, KDAB

Show Video

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 09:57

Show Video

Other news