QTimer does not emit timeout.....
-
Hello people.... I have:
QTimer * t = new QTimer(this); /* Timer dot */ connect(t,SIGNAL(timeout()),this,SLOT(dotDisplay())); t->start(10000); qDebug()<<"t thread is: "<<t->thread(); qDebug()<<"this thread is: "<<this->thread();
So t and this, live in the same thread... I've already checked. Also dotDisplay is declared as a slot.
I've checked and the timer is running... by means of qDebug()<<t->remainingTime()
Thing is, when timer reaches remaining time 0, the event loop won't trigger the signal... that's what I understand from the documentation.
The thread, the slot's object and timer signal, are living at is the same.... as I've already pointed out.... what do you think I can do?
Salutations.
-
@VRonin Yes. In fact the code posted is running inside that thread.
The object where the code lives has been moved previously to the thread that this->thread() make reference.
Uldfilterproject::Uldfilterproject(QObject* parent) : QObject__(parent), m_pGrab(nullptr), net({"192.168.1.5",8989}), m_worker(new UldWorker()) { /* Change worker threads */ m_worker->moveToThread(&m_thread); /* Now Connect signals, with a queue: why? this object and m_worker live in different threads now :) */ connect(this,&Uldfilterproject::awakeWorker,m_worker,&UldWorker::uploadStart_WORKER,Qt::QueuedConnection); /*Start the thread */ m_thread.start(); /* Start */ emit awakeWorker(net.m_hostName,net.m_port);
The uploadStart_WORKER slot is queued executed when I emit awakeWorker.... inside that slot I ran:
void UldWorker::uploadStart_WORKER(QString hostName, quint16 portNumber){ QTimer * t = new QTimer(this); /* Timer dot */ connect(t,SIGNAL(timeout()),this,SLOT(dotDisplay())); t->start(10000); qDebug()<<"t thread is: "<<t->thread(); qDebug()<<"this thread is: "<<this->thread();
-
@VRonin Indeed:
class UldWorker:public QObject{ Q_OBJECT public: UldWorker(QObject * parent = 0); ~UldWorker(){} public slots: void uploadStart_WORKER(QString hostName, quint16 portNumber); private slots: void dotDisplay(); private: };
However I guess I'm doing something awfully wrong.....
My UldWorker object is as well as the timer in the same thread:
http://doc.qt.io/qt-4.8/images/threadsandobjects.png
Let's suppose Obj5 is my UldWorker and Obj6 is my timer. They are indeed in the same Thread. So signal and slot connection can be done directly.
But according to documentation:
In multithreaded applications, you can use QTimer in any thread that has an event loop. To start an event loop from a non-GUI thread, use QThread::exec(). Qt uses the timers thread affinity to determine which thread will emit the timeout() signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread." http://doc.qt.io/qt-5/qtimer.html
I'm sure the new thread was started by means of QThread:exec(), why because I called
m_thread.start()
and according to docs:void QThread::start(Priority priority = InheritPriority) Begins execution of the thread by calling run(). The operating system will schedule the thread according to the priority parameter. If the thread is already running, this function does nothing. void QThread::run() The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec(). http://doc.qt.io/qt-5/qthread.html
As m_thread is basically a non reimplemented QThread class..... It is a QThread.... the default implementation of run, is called when I did
m_thread.start()
, so exec() was called. The event loop was initiated.This is the whole implementation of the slot which runs in the new thread:
void UldWorker::uploadStart_WORKER(QString hostName, quint16 portNumber){ qDebug()<<"Connection Manager Thread"; QTimer * t = new QTimer(this); QTcpSocket * s = new QTcpSocket(); QTimer * t = new QTimer(this); /* Error Handling */ typedef void(QAbstractSocket::*QAbstractSocketError)(QAbstractSocket::SocketError); connect(s,static_cast<QAbstractSocketError>(&QAbstractSocket::error),this,&UldWorker::errorNotify); /* State changed */ connect(s,&QAbstractSocket::stateChanged,this,&UldWorker::socketStateDisplay); /* Timer dot */ connect(t,SIGNAL(timeout()),this,SLOT(dotDisplay())); t->start(10000); qDebug()<<"t thread is: "<<t->thread(); qDebug()<<"this thread is: "<<this->thread(); while (m_alive){ QAbstractSocket::SocketState ss; ss = s->state(); if (!m_qi.isEmpty()){ /* Si hay al menos una imagen */ if (ss == QAbstractSocket::UnconnectedState){ /* Si no está conectado el socket Conectarse */ //qDebug()<<"Attempting Connection"; s->connectToHost(hostName,portNumber); s->waitForConnected(); //t->setInterval(1000); //t->setSingleShot(false); qDebug()<<t->remainingTime(); continue; } else if (ss == QAbstractSocket::ConnectingState){ } else if (ss == QAbstractSocket::ConnectedState){ /* Stop pending connection timer */ t->stop(); /* Si el socket está conectado, enviar la información */ /* pop from q and sent Information */ QImage i = m_qi.dequeue(); continue; } else { /* Socket is doing whatever I don't care */ continue; } } else { if (s->state() == QAbstractSocket::ConnectedState){ /* So there's no workload, free connection */ s->disconnectFromHost(); continue; } } } s->disconnectFromHost(); delete s; }
-
you are defining
QTimer * t = new QTimer(this);
twicecould you try and change
connect(t,SIGNAL(timeout()),this,SLOT(dotDisplay())); t->start(10000);
into
connect(t,&QTimer::timeout,this,&UldWorker::dotDisplay); t->setInterval(10000); QMetaObject::invokeMethod(t, "start",Qt::QueuedConnection);
-
@VRonin Thank you. Well timer is running, i did this:
void UldWorker::uploadStart_WORKER(QString hostName, quint16 portNumber){ qDebug()<<"Connection Manager Thread"; m_alive = true; QTcpSocket * s = new QTcpSocket(); /* Error Handling */ typedef void(QAbstractSocket::*QAbstractSocketError)(QAbstractSocket::SocketError); connect(s,static_cast<QAbstractSocketError>(&QAbstractSocket::error),this,&UldWorker::errorNotify); /* State changed */ connect(s,&QAbstractSocket::stateChanged,this,&UldWorker::socketStateDisplay); /* Timer dot */ connect(t,SIGNAL(timeout()),this,SLOT(dotDisplay())); t->start(10000); qDebug()<<"Metatest:"<<QMetaObject::invokeMethod(t,"timeout",Qt::AutoConnection); qDebug()<<"t thread is: "<<t->thread(); qDebug()<<"this thread is: "<<this->thread(); while (m_alive){ QAbstractSocket::SocketState ss; ss = s->state(); if (!m_qi.isEmpty()){ /* Si hay al menos una imagen */ if (ss == QAbstractSocket::UnconnectedState){ /* Si no está conectado el socket Conectarse */ //qDebug()<<"Attempting Connection"; s->connectToHost(hostName,portNumber); //s->waitForConnected(); //t->setInterval(1000); //t->setSingleShot(false); qDebug()<<t->remainingTime(); continue; } else if (ss == QAbstractSocket::ConnectingState){ if (!t->remainingTime()){ dotDisplay(); t->start(10000); } } else if (ss == QAbstractSocket::ConnectedState){ /* Stop pending connection timer */ t->stop(); /* Si el socket está conectado, enviar la información */ /* pop from q and sent Information */ QImage i = m_qi.dequeue(); continue; } else { /* Socket is doing whatever I don't care */ continue; } } else { if (s->state() == QAbstractSocket::ConnectedState){ /* So there's no workload, free connection */ s->disconnectFromHost(); continue; } } } s->disconnectFromHost(); delete s; }
So in the part
if (ss == QAbstractSocket::UnconnectedState){ /* Si no está conectado el socket Conectarse */ //qDebug()<<"Attempting Connection"; s->connectToHost(hostName,portNumber); //s->waitForConnected(); //t->setInterval(1000); //t->setSingleShot(false); qDebug()<<t->remainingTime(); continue; } else if (ss == QAbstractSocket::ConnectingState){ if (!t->remainingTime()){ dotDisplay(); t->start(10000); } } else if (ss == QAbstractSocket::ConnectedState){ /* Stop pending connection timer */ t->stop(); /* Si el socket está conectado, enviar la información */ /* pop from q and sent Information */ QImage i = m_qi.dequeue(); continue; } else { /* Socket is doing whatever I don't care */ continue; }
When in ConnectingState, I checked if the timer is done, and manually call the slot. That's how I know the timer is running and behaves as much as I want. I follow your suggestion of invoking, but rather than invoke the start signal of the timer I did it with the timeout one, and indeed the slot was called.. I used an AutoConnection because timer and this object (uldworker), both live and operate in the same thread.
So this way it behaves as needed... Im making a "dot waiting for connection" for debugging purposes (all this for debugging sake).
I'd like that it works by triggering automatically the dotDisplay slot, but seems I have tho check this manually.
After that I also made what you suggest, of testing start slot, and it returned true.... So I think the case for nonrunning timer is that timer is running.
-
Ok, found the issue here, the problem is with
while (m_alive)
if the loops keep running the thread event loop will never have the chance to trigger slots from signals. You can fix it either by changing design (make it asynchronous) or addingQCoreApplication::processEvents
just below the whilewhile (m_alive){ QCoreApplication::processEvents(); // the rest
-
@VRonin Could you suggest any good practice to replace a while loop? In my case, I start a thread by moving my QObject into a QThread instance. The worker only has a single-shooting timer and a while loop that checks a given requirement as long as the timer is running (termination condition). If the condition is true, the timer is stopped, thus preventing timer firing and calling the connected slot.
Until I read your comment I had the same problem as OP. Adding QCoreApplication::processEvents below while solved my problem, but I'm curious, which one of your suggested solutions is the better practice and how could the while loop be replaced.class Worker(QObject): finished = pyqtSignal() error = pyqtSignal() def __init__(self, port, parent=None): super(Worker, self).__init__(parent) self.port = port self.portData = port.portData self.getPosition = port.getPosition def process(self): timer = QTimer(self) timer.setSingleShot(True) timer.timeout.connect(self.portData.sender.pulseSignal) timer.timeout.connect(self.printFiring) timer.start(self.portData.retention) while timer.isActive(): position = self.getPosition() if position < self.portData.start or position > (self.portData.start + self.portData.window): self.timer.stop() self.finished.emit() def printFiring(self): print("timer fired")
-
move the check to a different method
I'll try to write python but I'm not proficient with the language.
class Worker(QObject): finished = pyqtSignal() error = pyqtSignal() def __init__(self, port, parent=None): super(Worker, self).__init__(parent) self.port = port self.portData = port.portData self.getPosition = port.getPosition self.timeoutTimer = QTimer(self) self.timeoutTimer.setSingleShot(True) self.timeoutTimer.timeout.connect(self.positionFinished) self.checkTimer = QTimer(self) self.checkTimer.setSingleShot(False) self.checkTimer.timeout.connect(self.checkPosition) def process(self): self.checkTimer.start(100) self.timeoutTimer.start(self.portData.retention) def checkPosition(self): position = self.getPosition() if position < self.portData.start or position > (self.portData.start + self.portData.window): positionFinished() def positionFinished(self): self.checkTimer.stop() self.timeoutTimer.stop() self.portData.sender.pulseSignal() self.printFiring() self.finished.emit() def printFiring(self): print("timer fired")
checkTimer
will check 10 times per second the position whiletimeoutTimer
holds a process timeout. Whenever the position condition is satisfied or the maximum timeout is reachedpositionFinished
is called