Antonio Del Sannio
Antonio Del Sannio

Reputation: 315

QT doesn't properly processes events in loop

I want to repeat the action performed in a SLOT until a qpushbutton is down.So I've done the following connections:

connect(ui->button,SIGNAL(pressed()),this,SLOT(button_hold()));
connect(ui->button,SIGNAL(released()),this,SLOT(button_released()));

and I've implemented the SLOTS in the following way

void My_class::button_hold(){
     //CLASS ATTRIBUTE key_is_released , i
     QThread::msleep(200);
     int wait_lock = 500; 
     i++; //GOAL OF THE SLOT
     QCoreApplication::processEvents(QEventLoop::AllEvents);
     while(!key_is_released){
         QThread::msleep(wait_lock); 
         i++;
         cout<<i<<endl;                
         if(wait_lock > 50) wait_lock -= 50;
         QCoreApplication::processEvents(QEventLoop::AllEvents);
     }
     key_is_released = false;
}

void My_class::button_released(){
      key_is_released = true;
 }

The goal is to repeat the action in the button_hod() Slot (In the example it's to increase i) even more quickly decreasing wait_lock and keeping the button down.The issue is that if i click button two times or more in a quick way the loop never ends,like if processEvents() would not work.The firts msleep is used in order to do not enter the loop (i is increased just once)if the button is just clicked and it is not keep down too long.If i do not click quickly but I keep down the button the method works well.

What am I doing wrong?

Upvotes: 1

Views: 947

Answers (2)

Reentering the event loop is a bad idea, and leads to spaghetti code.

Qt provides a wonderful state machine system with UML semantics. It's often best to model the system you're designing as a state machine. Here, there are two states: an idle state, and an active state: the value is incremented periodically while active.

The state machine decouples the Ui particulars (a button) from the core functionality: that of a periodically incrementing value.

Ideally, you would also factor out the state machine and the value to a controller class, and only connect the controller and the Ui from main() or a similar function.

// https://github.com/KubaO/stackoverflown/tree/master/questions/48165864
#include <QtWidgets>
#include <type_traits>

class Ui : public QWidget {
   Q_OBJECT
   int m_value = -1;
   QStateMachine m_machine{this};
   QState m_idle{&m_machine}, m_active{&m_machine};
   QVBoxLayout m_layout{this};
   QPushButton m_button{"Hold Me"};
   QLabel m_indicator;
   QTimer m_timer;

   void setValue(int val) {
      if (m_value == val) return;
      m_value = val;
      m_indicator.setNum(m_value);
   }
   void step() {
      if (m_value < std::numeric_limits<decltype(m_value)>::max())
         setValue(m_value + 1);
   }
public:
   Ui(QWidget * parent = {}) : QWidget(parent) {
      m_layout.addWidget(&m_button);
      m_layout.addWidget(&m_indicator);
      m_machine.setInitialState(&m_idle);
      m_idle.addTransition(&m_button, &QPushButton::pressed, &m_active);
      m_active.addTransition(&m_button, &QPushButton::released, &m_idle);
      m_machine.start();
      m_timer.setInterval(200);
      connect(&m_timer, &QTimer::timeout, this, &Ui::step);
      connect(&m_active, &QState::entered, [this]{
         step();
         m_timer.start();
      });
      connect(&m_active, &QState::exited, &m_timer, &QTimer::stop);
      setValue(0);
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   Ui ui;
   ui.show();
   return app.exec();
}

#include "main.moc"

Upvotes: 1

Antonio Del Sannio
Antonio Del Sannio

Reputation: 315

The problem was that clicking fast let say 10 times ,the release slot was called yet 10 times before entering in the loop, so key_is_released would be never updated again to true.Instead the button_hold slot was not yet been called for ten times.So adding a counter, increasing it in the button_hold slot and decreasing it in the release slot and adding a second condition counter>0 besides !key_is_released in the while loop fixes all.(It enters the loop only if not all the release slot have been run)

Upvotes: 0

Related Questions