Introduction to Qt / QML (Part 29) - ListView Basic Usage

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome back to this magic show called Programming with Qt and QML. I'm Jesper Pedersen from KDAB. We saw grids or we saw a row or column or whatever it was we saw in the previous video together with a repeater. The repeater spit out a number of elements, they went into this column which meant that we had a number of elements on top of each other in a column. Here's what you should try now. Pause the playback, create a repeater that spits out 200 million items and, when you've seen that come back...yes, your computer will likely have crashed or your qml engine will have complained too many elements in the scene or whatever. That's just not doable. For that, we have ListView. Let's not go down the road of discussing usability of your application if you have two hundred million elements. But nevertheless, with a ListView you'll only get as many elements out of the or into the view as is needed on the screen, give and take. Now, let's see what a ListView looks like. We have it up here. My ListView - I anchor it as usual, I give it a model, I give it a delegate and I say "clip : true." The "clip : true" is actually needed with a ListView, otherwise it will actually draw the items outside of the ListView when you're scrolling up and down and you likely do not want that. Let's see the complete code here. It is really just what's on the screen plus what we've seen previously. Here is my model. I got a number of elements generated in my model. Here is my component. It creates this text element. And here's my ListView. The ListView just has the code that we saw there. I run it and this time we can see that I can actually scroll the ListView. All of the elements are visible on the screen at the same time, so that's less fun. But let's just add a few more to see how fast I can type. And now when I scroll it, you can see that I can actually scroll the elements that we have here. I told you that the ListView actually allows us to not create all the elements up front, and for that we can - I can - prove it to you. I know how you like me to prove things, so how would I prove it? Press the pause button now, and ask yourself how could I actually see that? Yeah, I'm just shutting up for a while because I know you're not going to press the pause button. Yes, Component.onCompleted and Component.onDestruction - these are the two signal handlers that will be executed whenever either the element is created or deleted. So, Component.onCompleted console.log Welcome welcome and it's a um model.index and model.name. Similar component - I'm too lazy to type all that. Component.onDestruction, or Destroyed, same thing. Cannot assign to non...it's not called onDestroyed... onDestruction - I was right! There we are and now we can see it says down here, Welcome 9 10 11 12 13 14. And I scroll a bit more 15 and 16 and if I scroll a lot more, well I guess I should have not covered the bases that much because I don't want the other one to be saying die so now we'll handle it soon get to kill somebody we are welcoming people, there we are! Now I I killed Bob and Jane and Victor and Wendy because I ran out of memory for how much memory I had. That's one of those things that you control with Qt ListView. You can control down to the pixel level how many pixel lines that you want in your cache. The cache is called cacheBuffer. So my ListView here...cacheBuffer... Let's just give it, um, 40. That's 40 pixel lines. It doesn't mean that I'll have 40 elements - It's just 40 lines of pixels and when I scroll down it will reload in at the bottom extra elements up to 40 pixels lines of elements. And you can see I welcomed Bob now...that's Alice and now I welcome Jane here's Bob I welcomed just before. I welcome Victor and so on. You can see I welcome them a bit and I also start killing them off at the other end, a ways sooner simply because I only had 50...40 lines... I had 40 lines above and 40 lines below for caching so when I started scrolling they would fall off the lists. Okay - How many do I have by default? Well again Component.onComplete console.log cacheBuffer and you can see no...where did the print out...there we are! I got 320 pixels on each side of my ListView for the cacheBuffer. You're gonna observe something really unique now! You're gonna observe me being stupid. Okay, I am gonna act stupid because it's really difficult being stupid by command here. I have even written down my list of how stupid I can be. I'm just gonna simulate you when you start playing with ListView the very first time or I'm gonna simulate me the first 250 times I was working with ListView because there's a number of things that can go wrong and every time you do one of those things and you have seen before you'll go like mmm what was it again? So, bookmark this part in this video production so that you can go back and see this because well, as I said, there are quite a few things that can go wrong. Let's go back to this example. Did I just close it down? See that was step number one of being stupid - I shouldn't close it down. There we are. I would like now to have the possibility of highlight. If I run my application again you can see there is no highlight of the current item here. I would like to add that myself. In the next video, we're gonna see how to do that, but up until that next video we could do it ourselves. QML, on this view of course, does support this building anything else would be pretty stupid of the ListView and it's only me that's stupid now so it is building but I'll show you how you could do it yourself. It goes like this: So, first of all I would like to update a current index and if we just go and look at my ListView, we'll actually notice that the ListView it has a current index. So I'll update that. It goes on my component here I'll add it on my text I'll add a MouseArea and I will say onClicked...current index...let's just give it id for one root here...root.currentIndex equal to index yada yada model.index and yes index is one of those that are so special that it's okay to say just index rather than model.index and then I want to go down on my ListView here. It's of course...see I'm stupid...that's okay that was not stupid by mistake that was a true stupid. I need a ListView here. It's a ListView's current index that I want to update and then I'll go down here and say component - let's not do that anymore - We'll say onCurr...CurrentIndexChanged console.log currentIndex. And we run! And now when I click on the item I have proven to you that I can be stupid on command because nothing works. Poor sit and think over what did go wrong and welcome back yes I did forget to anchor my my MouseArea so it filled exactly zero pixels that is likely not what I want. Now it works not anchors.fill onClicked this view of currentIndex equal to index on the currentIndexChanged console.log...oh sorry I of course need to see my application output. Now in my application output we can see that when I click on the items here that the currentIndex updates. That was the first one. Now what I would like to do is that I would like to highlight that currentIndex so I would like to add a rectangle around the element here. My rectangle will have a color and the color should be if currentIndex well this view of currentIndex is equal to index, then it should be great, and otherwise it should be transparent. Well, that's making yellow just so that we are not blaming transparent for the mistake of things. We'll make this text element inside of my rectangle let me just select it all and re-indent so that it we can see it looks right. My rectangle here has the text element inside it and it looks pretty good, right? Let's run it. This is a celebration! It was the 150,000th time I had this exact problem. It's so annoying! Every time I forget what is it really? All my elements are standing on top of each other. Why is that? hmm...all right, yes...My rectangle here doesn't have height, so therefore its height will be 0 pixels. Well, wait a minute I saw the text. Well that, my dear friend, is because I had "clip: false," meaning that the children are still able to draw outside of the parent. Now "clip: true" and you see absolutely nothing. Hmm...let's just try and run this in GammaRay and see why, because GammaRay is actually kind enough to tell us - for each of those it tells us. My dear friend, item is visible but out of view. Okay. Item has size of 0. Oh yeah, that is this height. So let's go and kill it again. It is the height that I need to specify here. You know better by now it's of course the implicit height that I want to specify - and the implicit height should be...and let's not call it root, let's call it txt and txt.implicitHeight. Run it, and still nothing's going on. Let's see... yes...implicit.Width obviously and txt.implicitWidth. Hallelujah! We see something now if...we even see that I can select an item and it becomes the current item. However, if you look at this it looks somewhat weird, doesn't it? The yellow extends only to the end of the name so obviously we wanted to take the full width here. And how do we do that? Well of course I need to specify not just the width here, I need to anchor it to the left and to the right. So instead of the implicitWidth, let's just anchor it. So, anchors left: parent.left, right: parent.right. And now it takes the full width and I can click on my items here. Everything is beautiful. Oops...yeah, I can only click on the text, not outside of the text...hmm... So what's the problem here? Well, the problem of course is the MouseArea. The MouseArea only fills the text. So, maybe it would make sense to take them out there right here. Move it one level and now it fills the whole item and I can click out here. Nice! There is one problem. If you're in for creating reusable components, then you might want to have this component - the name delegate here - actually work also with other ListViews. So how do we do that? See it only works with this ListView because it says lv.currentIndex and for that we need to go to our ListView. We need to look at the documentation for our ListView. My documentation down here has this section on Attached Properties and one of the attached properties, View. So whenever I got a delegate, then that delegate has an attached property ListView.view, which is actually a pointer to the actual ListView using that delegate. So I can go down here and I can say, instead of lv here, I can say ListView.view.currentIndex and you see everything works fine still. I do the same thing come down to the other place I was here ListView.view.currentIndex and now it doesn't work anymore. The key thing to understand is that the attached properties are only attached to the root element of the delegate - not throughout the whole hierarchy. That's an optimization that, hmm...yes, okay, it's an optimization. Sometimes optimizations hurt. This one hurts every time you forget about it. But I'll show you a simple trick to avoid this. The first way I could could solve this problem with would be to simply say parent.ListView.view.currentIndex and here we are again back in action. But if I had an element that was several layers deep then I I couldn't do parent.parent. parent.ListView.view.currentIndex. I would need to have an ID on the root element id: root and then root or root delegate or whatever. Root delegate.ListView.view. and so on. There is another way to do this. Up here on my ListView, I will simply create a new property. Let's call that underscore underscore LV and the value is ListView.view. Let's make that a readonly property even. readonly property underscore underscore lv: ListView.view. Why are you giving me arrows? invalid name readonly property...hmm...I think it's just confused. That's okay - everybody can get confused at some point...and then down here underscore underscore LV and down here, instead of the parent.ListView.view underscore underscore LV, let's just see it running...hmm so am I the one really confused? readonly property underscore underscore LV:...Hey, that's when you just go and restart. readonly property type - oh of course, gee - and the type is hideous or we could even make it ListView and then underscore underscore LV and it's ListView.view. Unfortunately none of our us are confused anymore. You can see I can click on each of the items. So let that be a pattern that you will apply to your delegates going forward. You'll have this readonly property ListView: underscore underscore ListView: ListView.view with this whole thing you can just, throughout your delegate, refer to the actual list view as underscore underscore ListView or underscore underscore lv. That's quite a few challenges that I put myself to and quite a few problems that I had there with the items and trust me especially the one where you see all the items up at the top standing on top of each other I doom you you're gonna and jinx you that you're gonna see that in a number of different setups going forward. Just remember you need to specify the implicitWidth and implicitHeight or at least some other way to avoid that problem. In the next section, we'll go even deeper into the ListView. So thank you for listening in and if you haven't done so already, you know what am I gonna say now. Please do subscribe to our channels. you
Info
Channel: KDAB
Views: 9,984
Rating: undefined out of 5
Keywords: KDAB, Qt, QML, ListView, Programming, Tutorial, Online Course, UI, User Interface
Id: oslnmLQ5Zh8
Channel Id: undefined
Length: 19min 50sec (1190 seconds)
Published: Thu Jun 11 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.