Introduction to Qt / QML (Part 41) - Connecting Signals to Lambda Expressions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome back! I'm Jesper Pedersen from KDAB.  We are in the middle -- well, towards the end -- of   programming with Qt and QML. We're currently down  in the machine room looking at signals and slots.   We saw the concept in the previous  video, but previous plus one video.   In the previous one, we saw the macro-based and  the PointerToMemberFunction connect and now   we're gonna see how to  connect signals to lambda expressions. Actually, there are three  different ways that we can connect our signals   using function objects. We do that  to lump the functions, as    I just said. We do that using functors. Do you  know what a functor is? And we use that with standard functions. Let's see the code  for this. What you're looking at here in the slide   is connecting a signal to a lambda expression. Please  do hang in there. There are a few caveats when we talk about connecting a signal to  a lambda expression. Do not just run away and say,   "hey, cool. I'm never going to connect my signals to  slots anymore," because you're likely not going to   be very successful down that route. So let's see. To  connect the signal to a lambda expression, I have a   widget QPushButton here, again. It is connected. So,  it takes three parameter connects: who is emitting   the signal, which signal (so that's a pressed signal),  and then which slot (it's just replaced by a lambda   expression). So, here we have a lambda expression.  What we have inside the square bracket is the   closure, where I'm taking this button so that  I can use it in this second part which is the   the body of my lambda expression. And this is  simply just regular Qt   Widget code for setting the text of a push button.  Let's try it in Qt Creator so that we can see   what it looks like. Actually, the  example that I got here is a   slightly more complex example, simply because  it's showing you also this slider to the   SpinBox over here and setting up the layout. So,  that's what we have up until this line here. So,   the line that we have here behind the dialog  box says connect my lineEdit textChanged signal   to LogString log. Now what is LogString? Well,  that's our first example. This is a functor.   A functor is an object that implements the operator 'parenthesis open,' 'parenthesis close.'   That's a functor; that's what we  see here. So, it implements this   operator, 'parenthesis open,' 'parenthesis closed,'  and that thing can take a number of parameters.   And what that really means is that, if I create  an instance of this LogString class, then on   that instance I can write 'parenthesis open,' give  it a QString, 'parenthesis closed,' which makes   it feel very much like if it was just a function  that I was calling there. And what my functor here   does is that, in the constructor, it is taking  a string that it's using as a prefix and then, in   this operator, 'parentheses open,' 'parenthesis close,'  it simply prints out my prefix plus the string. So,   down here again, my functor says LogString  Log colon. It's connected to LineEdit   textChanged. And if I go up here  in this line edit here and I start typing, 'hello   world,' then we can see in our output down here  it says "Log: hello w," "hello wo," "hello wol," and so on.   So, for each time I type something, the textChanged  signal is emitted. That goes into that   functor that does the printout. Of course, that's a very seldom one that you do.   Let's just jump to this other one. This  is a standalone function, so my quitButton...   it's click signal is connected to my  standalone function. So, that's a global function   that you have in there that just calls quit. So, it  looks like this. Yeah. Well, it quit the application.   Again, this is not something that you'll do very  often. It's not like you want to connect all   your signals to standalone functions, but  you can do it. The last one, however, is   one that you will likely do more often.  So, here I have a connect, my PressMeButton's pressed signal goes into this lambda  expression where it sets the text to "Release Me,"   and, when it's released, it goes  into another lambda expression that says "Press Me."   And let's just run the example again,  so you can see what it looks like.   "Press Me" -- now you can see the text  changed to "Release Me." I still hold down the   the mouse button. Now, I release it. Now, I press it.  Now, I release it. Now, I press it. Now, I release it.   So, that's an example of a  lambda expression connected there. Jesper, does that mean that we can skip  all our...? Okay, I already gave that away. Yes,   you should not write all of your slots and lambda  expressions going forward. The first reason is   that, if you write all of your slots in lambda expressions, then whatever goes in the body of   that lambda expression cannot be reused elsewhere. It might be that you wanted to call   that function somewhere else. Second, where do I  write those connection statements? Very likely in   my constructor of whatever my class is doing. And  now you have changed your constructor into being   5000 lines of code of 10 lambda expressions that  each have 400 lines of code in them. That's likely   not going to be a very good design for your code.  You'd rather have those 10 long lambda expressions   in separate functions that had a good name that  told what are we going to do here, and so on.   There's a tiny little problem with lambda  expressions that I'm going to show you now.   So if you see here, I got a second window. It's  a QLabel. It is sized and word wrapped and   whatnot, and then it has this set attribute --  Qt::WA_DeleteOnClose.   What this means is that, whenever I close that  window, Qt will actually notify and go and   delete the object. So, that's a nice little  facility of Qt in the widget world that you can   have your top level window. They own all of the  content of those windows using the parent-child   relationship. And you say Qt::WA_DeleteOnClose   and then whenever the top level window  is closed, it will be deleted. All the children will   be deleted. But I'm using that feature now to make  sure that that window is explicit closed. Of course,   that will show you a problem that you often will  face when you're working with lambda expressions.   See, I set up a new lambda expression here from the  Press Me button. Whenever it's clicked, it goes into   this lambda expression here. It takes a second  window (that's this secondWindow we have there)   as a closure and then it's on that  secondWindow; it's called setText (after all, the   secondWindow is just a QLabel. setText  with whatever text we had in there and then   Button clicked. We go out here again and see the  two windows. You can see Press Me, Press Me, Press   Me, and over here it updates with Button clicked.  Okay. I'm done with this window; I'll close it. Remember, I closed the top level  window due to the Qt::   whatever; I cannot remember. It has now been  deleted. And now when I click, what will happen? Hmm...busy cursor. And on my standard output, it  says, "The program has unexpectedly finished."   That's, of course, a total lie because I did expect  it to finish. Well, it crashed. And why did it crash?   It crashed because of my lambda expression in  here, from here to here. That lambda expression   was still referencing secondWindow,  which is now a dangling pointer --   and those, when you reference them,  have the tendency of crashing. You just gave me a shock, Jesper. I just need to  go to get my...are you saying to me that when   I connect a signal to a slot that I need to be  careful to disconnect if my slot is going out of   services because otherwise if my application  is going to crash who wants to use this kind   of crappy software? Calm down. Get your  breath under control. That's not what I'm telling   you. If you are just doing one of those that  takes four fingers,   who's emitting the signal, which signal, who's  implementing slot, in which slot either the macro-  based or the other one, you're all fine once that  that ring finger here goes out of   service. It will, as part of its destruction. It's a  QObject after all. It's part of its destruction.   It'll go to Qt and say, "you know what? I was just  killed. If you happen to know anybody connected   to me, can you please take those connections  down because I will not be around for much more. However, for our signal/slot connection  where the slot part is done with a lambda,   Qt has no chance to detect that. The lambda  expression is still a perfectly valid expression   and Qt has no way of actually seeing that inside  that lambda expression we are referring an object   that has gone out of scope. However, that is a  problem that we've been facing for years and at   some point somebody came around and said, "why don't  we just add that missing...what was it? that missing   third finger here, namely, who is implementing the  slot. People are like, "Adding who's implementing a slot?   It's a lambda expression. You know, right?" Well, let's  simulate that it's who is implementing that slot.   And that's what it looks like when I do like  this: so, first parameter, second parameter, and   then I would add a third parameter saying who is  implementing the slot. That is secondWindow.   Of course, it's not implementing any slot; it's  a lambda expression. But it tells the connect   statement, or the connect statement tells Qt, "if  you happen to hear that secondWindow is being   deleted, can you please take down this   signal/slot connection to a lambda expression?" And that's what we'll see here, if I run it again. We'll notice that it works just fine. Now I close  that window and I continue. It doesn't crash   anymore. Oh, Jesper, does that mean that you can do  like a list of things? Then you can do   a list of things  here. So, you do have secondWindow and thirObject   and bah and buh, and whatnot. No, that's  not what it means. It's just a special case for   that one special case where we connect to  a lambda expression that uses just one object.   Because one of the things that you often do with  lambda expressions in Qt is have a signal,   it brings out a QString, you want to connect  that to a slot that takes an integer. But    QString to the integer, you cannot do that  conversion in your signal/slot   connection. But what you can do is  connect your signal that brings out the QString,   then in a lambda expression you convert that QString to a number using QString:: to int.   Now, you have an integer and you call  that function on that object with that.   And there, the third parameter -- who is  implementing that slot -- is just perfect. That's it. We've seen lambda expressions  and signal/slot connections.   I could have gone on for an hour telling you  about lambda expressions in general, but that   would be slightly beyond this training here. In the  next video, I'm going to show you how you implement   your own signals and your own slots, because, had  the story ended with this video and said   you can take these Lego bricks out of your Lego  box and they happen to be coming from Qt   and you can connect them, that would have been  somewhat boring. No, the real power is when you have   something that emits a signal and you  can implement a slot that listens to   that. So, until next time, do subscribe to  our channel. See you, and have a great day.
Info
Channel: KDAB
Views: 2,674
Rating: undefined out of 5
Keywords: C++, Cpp, QML, Qt, KDAB
Id: CqGjti9mPhs
Channel Id: undefined
Length: 13min 31sec (811 seconds)
Published: Tue Dec 22 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.