Custom signal to slot : The slot requires more arguments than the signal provides.
-
I want to emit a custom signal
void qmovieFrameChanged()
whenvoid QMovie::frameChanged(int frameNumber)
is emitted, so it can
execute the following :void QtDice::stop_last_frame(QMovie* movie) { if( movie->currentFrameNumber() == (movie->frameCount() -1)) { movie->stop(); if(movie->state() == QMovie::NotRunning) { emit movie->finished(); } } }
So :
//What I want: connect(movie, &QMovie::frameChanged, this, &QtDice::qmovieFrameChanged); connect(this, &QtDice::qmovieFrameChanged, this, &QtDice::stop_last_frame); //What I get : The slot requires more arguments than the signal provides.
The error makes totally sense but I need a way to call this function when
QMovie::frameChanged
is emitted without a lambda.Declaring the
signal
asvoid qmovieFrameChanged(QMovie*)
doesn't help either.I understand that
QMovie::frameChanged(int frameNumber)
requires the signal to accept an int but I can't testmovie
's frameCount etc inside thestop_last_frame
if I make it accept integer.I need to pass the object
movie
in order to call it's methods and do what I have to do. Is there something I am missing here and/or can I overcome compiler's denial? -
Hi,
Why not make
movie
a class member ? -
@SGaist Like this?
... private: QMovie* movie; ...
I don't know if I am correct here, but it reminded me of global variables. I created (and destroyed) the
QMovie
object inside the function that needed it; not in the constructor for example.The only objects that are visible everywhere in my program are those (widgets) that I created with Designer. So, the
label
that shows the movie is included fromqtdice.h
(actually fromui_qtdice.h
), but for some reason I felt like it's wrong to fill my header with private objects all over the place.Correct me if I am wrong. :-)
@ mrjj
No, there is only one QtDice which callsvoid QtDice::image_update(int image_number)
which in turn creates in scope aQMovie
.But please, elaborate on this :
You can subclass QMovie and add such new signal but its odd design to have a class emit a "this" pointer
I suppose you mean I can create a custom QMovie class that will inherit from the original one and will add my functionality. So there will be no need to emit custom signals to custom slots...? It's quite convenient, like when you want to press a button and call some function you need.
connect(m_ui->m_button, &QPushButton::clicked, this, static_cast<void (QtDice::*)(void)>(&QtDice::reload)); //The whole cast thing is because reload is overloaded
-
@Petross404_Petros-S
Hi
You would have to hook up the old signal to private slot and
emit your new signal with the "this" pointer in that slot since you want to change what is
emitted.If dice is using QMovie, it would be fine if it had it as a private member as then
its very self contained. -
@Petross404_Petros-S
Not the old syntax, the old signal frameChanged.
You would bind it to a private slot and
void MyMovie::FrameChanged() {
emit MynewFrameChanged(this);
}But please check if move could just be in Dice as its the main user and
should just have the object then. -
@mrjj OK before powering off my pc, I thought let's try to declare
QMovie* movie
as a private member of QtDice.Afterwards, just before using the
movie
, I setted the filenamemovie.setfilename(":/images/etc")
but the compiler ranted something about static variable movie but I am quite sleepy to remember.Perhaps I must also use
new
somehow on an existing variable to actually create it. I think I found what I will be looking the next day.All the best, Petros.
-
@Petross404_Petros-S
Yes , yes you MUST new it in QtDice constructor before use.
(movie = new QMovie(this) )and its
movie ->setfilename not the . (dot) syntax :)
(-> for pointers DOt is for non pointers) -
Yes , yes you MUST new it in QtDice constructor before use.
Actually, when I new it in
QtDice::QtDice
I get a segfault when I try to set it's filename in the slotvoid QtDice::image_update(int)
.The moment I write in the latter,
QMovie movie = new QMovie(this)
and afterwards set the filename, it works.Anyway that isn't Qt specific anymore but about my lack of proper C++ understanding. I will keep this for a little longer UNSOLVED and will post any findings to close it. Thank you very much.
-
hi
My guess is that u do the classic error/facepalm
QMovie movie = new QMovie(this) ; // this make a LOCAL variable. not what u want
To use the one from .h
movie = new QMovie(this); // NOTICE No TYPE FIRST. we set the member to something. -
Indeed that worked! Compiler didn't warn about anything and as a matter of fact I was thinking of QSignalMapper and std::bind but thought it would be very complex. It's a chance to take a closer look at your code and practice on toy-programs with bind.
Anyway, with your solution I don't need a lambda for this anymore.
In the ctror I wrote
movie = new QMovie(this)
but there is another problem. Now that I use a private class member movie, the animation doesn't play. The gif is shown, but it's frozen. It doesn't matter if I usestd::bind
as VRonin proposed or if I use the lambda.As long as the QMovie is created in the ctor like this :
// In the ctor movie = new QMovie(this); movie->setFileName(":/images/rolling_infinite.gif"); ... //In void QtDice::image_update(int image_number) .... m_ui->label->setMovie(movie); //Animation works!
But I set the movie's filename inside the function :
// In the ctor movie = new QMovie(this); .... // In the void QtDice::image_update(int image_number) movie->setFileName(":/images/rolling_infinite.gif"); m_ui->label->setMovie(movie); if(movie->isValid()) { //again is valid is printed, Qt continues to accept movie as valid. qDebug() << "is valid" << '\n'; movie->start(); } else... //Gif is shown, but doesn't animate
I even tried to append
this->
infront ofmovie
but of course this wasn't the problem. It can access the variable as there isn't anymovie
in scope. I can drop the issue here because the custom signal to signal connection is accomplished, but I want to know how to debug the stopped animation problem. GUI isn't unresponsive, I guess it's not a crash... -
Hi
it really should work the same as in ctor.
Must be something else.
In what function is example 2 ? -
@mrjj It goes like this :
Ctor
connects a pushbutton click withQtDice::reload
which in turn callsQtDice::image_update
to animate the gif and after it's finished, show an image.I found out what the problem is.
QtDice::image_update
is called multiple times and therefore it tries tosetFileName
every time. So a check iffileName()
is empty must be done each time before attempting to setFileName.void QtDice::image_update(int image_number) { //Make sure we don't constantly re-append a fileName! if(movie->fileName() == "" ) { movie->setFileName(":/images/rolling_infinite.gif"); //If it still is empty print error and exit. Not good at all //TODO wrap the setFileName in some Qt assert to throw an exception if (movie->fileName() == "") { qDebug() << "Error! Couldn't set a fileName to read for the animation"; QApplication::quit(); } } m_ui->label->setMovie(movie); ..... }
I don't know exactly why this failed ( I mean I could change the fileName as many times as I like), but I checked and rechecked that with this
if (movie->fileName() == "" )....
the animation works.That could also explain why some (but not all) times the animation played the very first time I push the button (ie called the function) and the second time it stopped. If someone thinks that other reasons come into play, please inform me, I am more than glad to learn.
For any of you, that is curious what is the code and/or want to c&c, this is the QtDice and this is a screenshot
Have a nice afternoon!