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.