One function in connect() and QTimer::singleShot()
Hello!
I'm very new to C++ (learning while writing this app). I do have some experience with QT using pyside2, but i want to write my app in C++.
My current problem is calling one function with connect() AND QTimer::singleShot().
some snippets of my code:
mainwindow.h
Code:
private slots: // in MainWindow class
void searchTable(int searchID);
mainwindow.cpp
Code:
#include "mainwindow.h"
#include "systemdialog.h"
#include "ui_MainWindow.h"
#include "ui_SystemDialog.h"
MainWindow
::MainWindow(QWidget *parent
): ui(new Ui::MainWindow)
{
ui->setupUi(this);
// SEARCH TABLE UPDATE
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(searchTable(int)));
// LOAD TABLES
QTimer::singleShot(8500,
this,
SLOT(searchTable
(50)));
etc..
}
search.cpp
Code:
#include "mainwindow.h"
#include "ui_MainWindow.h"
void MainWindow::searchTable(int searchID){
//my code
}
App works ok and connect() works ok. And if i call function searchTable(50); after connect() also works ok. Only when i want to use connect() and QTimer::singleShot() i get this issue in Qt Creator App Output:
Code:
QObject::connect: No such
slot MainWindow
::searchTable(50) in ..
/NewApp
/mainwindow.
cpp:70 QObject::connect: (receiver name
: 'MainWindow')
So what am i doing wrong? :confused:
Re: One function in connect() and QTimer::singleShot()
Quote:
QTimer::singleShot(8500, this, SLOT(searchTable(50)));
First thing: this is not allowed. You cannot pass an argument in a connect() statement (which is what this does, in effect - it says to connect the QTimer's timeout() signal to your searchTable() slot). However the function signatures for the signal and slot must match. QTimer::timeout() does not pass any arguments, so you can't make something up for it to pass to your slot.
Quote:
QObject::connect: No such slot MainWindow::searchTable(50) in ../NewApp/mainwindow.cpp:70
Second, this occurs because SLOT() is a C++ macro, which converts its argument into a literal string. So your SLOT() argument is being passed as the string "searchTable(50)" and internally the connect() method is trying to match it up with a method defined in MainWindow named "searchTable(50)", which of course there isn't one. That's what the compiler error message is telling you.
So, if what you want to do is to fire off an initial search of the table 8500 ms after your program starts (technically, 8500 ms after the MainWindow constructor exits), then you need to create a slot that takes no arguments, connect that to the timeout signal, then within that slot, call searchTable( 50 ):
Code:
private slots: // in MainWindow class
void doInitialSearch();
void searchTable( int searchID );
Code:
MainWindow
::MainWindow(QWidget *parent
): ui(new Ui::MainWindow)
{
ui->setupUi(this);
// SEARCH TABLE UPDATE
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(searchTable(int)));
// LOAD TABLES
QTimer::singleShot( 8500,
this,
SLOT( doInitialSearch
() ) );
// etc..
}
void MainWindow::doInitialSearch()
{
searchTable( 50 );
}
void MainWindow::searchTable(int searchID)
{
//my code
}
What are you trying to do with this 8 1/2 second delay before you do the initial search? There are probably better ways to do it than make your user sit and watch the main window for that long while nothing happens.
If all you want is to ensure that the MainWindow is visible before doing this search, then you can implement an override of the showEvent() and make a direct call to searchTable() in that:
Code:
{
QMainWindow::showEvent( pEvent
);
// same as super.showEvent() in python
searchTable( 50 );
}
or you can use a QTimer with zero timeout to accomplish the same thing. The timer signal will fire immediately after control returns to the Qt event loop (ie. after the showEvent() method exits).
Code:
{
QMainWindow::showEvent( pEvent
);
// same as super.showEvent() in python
QTimer::singleShot( 0,
this,
SLOT( doInitialSearch
() ) );
}
But I suspect there is no need for you to wait for the MainWindow to be visible before doing the initial search. Even if the window is not visible, all of the QWidgets in it are complete after the call to setupUi(), so you can fill tables, etc. and they will become visible when the MainWindow does.
Re: One function in connect() and QTimer::singleShot()
Thank you for all the info.
App will be running on fairly old PC and there is some other stuff that needs to be processed and loaded (from DB). I was forced to install x64 OS, to install QT on it and is not as fast as i would like (PC and python version App). I did search for lighter versions of Linux, because i don't need anything extra on that PC.
Search table is not first thing user will see, when MainWindow is visible (i'm using stacked widget). This and OS/App speed was my main reason why i'm using QTimer. To let other stuff get processed and loaded before.
QTimer with 8500 might be premature at this point, so i might change it later. I started working on C++ version 5 days ago, so i have plenty of time to change QTimer etc.
I will be using this for now. But as i said, i might change it later.
Code:
// LOAD TABLES
QTimer::singleShot( 8500,
this,
SLOT( doInitialSearch
() ) );
// etc..
}
void MainWindow::doInitialSearch()
{
searchTable( 50 );
}
Re: One function in connect() and QTimer::singleShot()
Quote:
This and OS/App speed was my main reason why i'm using QTimer. To let other stuff get processed and loaded before.
Think about a design where each of the parts of "other stuff" has the ability to emit a signal when it is finished, so the next piece of "stuff" can start immediately after that. It doesn't have to be an actual Qt signal, just some flag or other indication that can be used to indicate a "finished" state. In your app, you can implement a state machine that starts the next task when each previous task says it is finished.
For example, create an enum of status flags:
Code:
enum TaskStateT
{
eInitialState = 0,
eTask1 = 1,
eTask2 = 2,
// ...
eTaskN = N,
eFinalState = N+1
} TaskState;
bool MainWindow::updateTaskState( TaskStateT state )
{
bool bFinished = false;
TaskStateT nextState = TaskStateT( state + 1 );
if ( nextState == eFinalState )
bFinished = true;
else
doTask( nextState );
if ( bFinished )
searchTable( 50 );
return bFinished;
}
void MainWindow::doTask( TaskStateT state )
{
switch( state )
{
case eTask1:
doTask1();
break;
case eTask2:
doTask2();
break;
// ...
}
// Current task is done, update and do the next
updateTaskState( state );
}
When the MainWindow constructor is exiting, the last thing it does is call updateTaskState( eInitialState ), which kicks off the set of things that must be done. When all are finished, the initial search can start immediately. You don't need to rely on magic timing numbers which might waste the user's time or start something off too early, depending on how much time the startup stuff takes.
If you want to keep the GUI "alive" while the initial processing is going on, you could a a call to QCoreApplication::processEvents() in the updateTaskState() method. This will run the event loop and ensure all pending events are handled. There are other ways to do it as well, such as making the state update and task execution into a signal / slot based implementation.