Как временно отключить сигнал с временным интервалом в Qt?
Я подключаю слот с сигналом. Но теперь я хочу временно отключить их.
Вот часть моего объявления класса:
class frmMain : public QWidget
{
...
private:
QTimer *myReadTimer;
...
private slots:
void on_btnDownload_clicked();
...
};
В конструкторе frmMain
я соединяю myReadTimer
со слотом, так что ReadMyCom
будет вызываться каждые 5 секунд:
myReadTimer=new QTimer(this);
myReadTimer->setInterval(5000);
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
Но в слоте on_btnDownload_clicked
. Я не хочу, чтобы myReadTimer
излучал любой сигнал в области on_btnDownload_clicked
. Поэтому я хочу отключить их в начале on_btnDownload_clicked
и снова подключить их в конце. Вот так:
void frmMain::on_btnDownload_clicked()
{
//some method to disconnect the slot & singal
...//the code that I want myReadTimer to leave me alone
//some method to reconnect the slot & singal
}
Я искал в Stackoverflow и получил некоторый ответ, как вызов деструктора QObject
. Но я не знаю, как его использовать.
Я также попытался использовать disconnect
, например:
QMetaObject::Connection myConnect;
myConnect=connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
...
disconnect(& myConnect);
Но он все еще не работает. Так может ли кто-нибудь помочь мне, как это сделать?
Ответы
Ответ 1
В QObject
есть очень приятная функция, которая пригодится время от времени: QObject::blockSignals()
Здесь очень простой класс "огонь-и-забыть", который будет делать то, что вы хотите. Я не беру на себя ответственность за дизайн, я нашел его в Интернете где-то давно. Однако будьте осторожны, он блокирует все сигналы для всех объектов. Если это не то, что вы хотите, вы можете изменить класс в соответствии с вашими потребностями.
class SignalBlocker{
public:
SignalBlocker(QObject *o): object(o), alreadyBlocked(object->signalsBlocked()){
if (!alreadyBlocked){
object->blockSignals(true);
}
}
~SignalBlocker() {
if (!alreadyBlocked){
object->blockSignals(false);
}
}
private:
QObject *object;
bool alreadyBlocked;
};
Использование в вашем случае становится тривиальным
void frmMain::on_btnDownload_clicked()
{
SignalBlocker timerSignalBlocker(myReadTimer);
...//the code that I want myReadTimer to leave me alone
// signals automatically unblocked when the function exits
}
UPDATE:
Я вижу, что из Qt 5.3 в API был добавлен очень похожий класс. Он выполняет ту же работу, что и выше, с чуть большим набором функций. Я предлагаю вам использовать официальный QSignalBlocker класс, чтобы обновлять свою кодовую базу с любыми изменениями API.
Использование, однако, остается тем же самым.
Ответ 2
Синтаксис разъединения/повторного подключения
Существует много способов вызвать разъединение, в зависимости от того, что вы хотите отключить. См. страницу документации QObject для объяснения того, как они работают.
Здесь пример с использованием 0 означает "отключить все слоты".
void frmMain::on_btnDownload_clicked()
{
// disconnect everything connected to myReadTimer timeout
disconnect(myReadTimer, SIGNAL(timeout()), 0, 0);
...//the code that I want myReadTimer to leave me alone
// restore the connection
connect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
}
Или вы можете указать точную пару сигнальных слотов для отключения, скопировав синтаксис "connect", например:
disconnect(myReadTimer,SIGNAL(timeout()),this,SLOT(ReadMyCom()));
Остановка таймера
Поскольку вы работаете с таймером, это может быть проще:
void frmMain::on_btnDownload_clicked()
{
// stop the timer (so you won't get any timeout signals)
myReadTimer->stop();
...//the code that I want myReadTimer to leave me alone
// restart the timer (using whatever interval was set previously)
myReadTimer->start();
}
Отличия от вашего первоначального подхода:
- Поскольку вы останавливаете и перезапускаете таймер, при следующем запуске будет
interval
после завершения функции слота.
Вам нужно сделать что-нибудь особенное?
В однопоточном приложении Qt, если вы уже обрабатываете сигнал, другой сигнал не будет "посередине" этого кода. Вместо этого он будет стоять в очереди, как даже обрабатывать сразу после возвращения текущего слота.
Поэтому, возможно, вам не нужно вообще останавливать или отключать ваш таймер.
Отличия от вашего первоначального подхода:
- Если для выполнения
on_btnDownload_clicked
требуется некоторое время, вы можете иметь несколько событий ReadMyCom
в очереди после завершения on_btnDownload_clicked
. (Обратите внимание, что на данном этапе у вас будет операция, которая в любом случае "закроет" ваш графический интерфейс в любом случае, может быть больше смысла реорганизовать функцию или дать ей собственный поток.)