我們有一個QCheckBox
對象,當用戶選中或取消選中我們想要調用的函數時,我們將函數連接到stateChanged (int state)
信號。另一方面,根據一些條件,我們也改變了QCheckBox
對象內部代碼的狀態,並且這導致不需要的信號。防止Qt中的射擊信號
在某些情況下有什麼辦法來防止觸發信號?
我們有一個QCheckBox
對象,當用戶選中或取消選中我們想要調用的函數時,我們將函數連接到stateChanged (int state)
信號。另一方面,根據一些條件,我們也改變了QCheckBox
對象內部代碼的狀態,並且這導致不需要的信號。防止Qt中的射擊信號
在某些情況下有什麼辦法來防止觸發信號?
您可以使用clicked
信號,因爲只有當用戶實際單擊複選框時纔會發出信號,而不是使用setChecked
手動檢查信號時發出信號。
如果你只是不想讓信號在一個特定的時間內發出的,您可以使用QObject::blockSignals
這樣的:
bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);
這種方法的缺點是所有信號將被阻止。但我想這在QCheckBox
的情況下並不重要。
可以QObject::disconnect
刪除相應的信號槽連接,並且可以QObject::connect
再次一旦你完成......
在QObject
派生類,你可以調用blockSignals(bool)
防止物體從發射信號。因此,例如:
void customChangeState(bool checked)
{
blockSignals(true);
ui->checkBox->setCheckState(Qt::Checked);
// other work
blockSignals(false);
}
上述方法將改變檢查狀態,而不點擊,stateChanged
,或射到任何其它信號。
這不會阻止ui-> checkBox發射信號,所以它不會按預期工作。你應該調用ui-> checkBox-> blockSignals(true)或使用QSignalBlocker(從Qt5.3開始) – HappyCactus 2015-04-17 09:46:22
您可以始終使用QObject::blockSignals()
阻止QObject上的信號發射。請注意,對於事情是正確的,您應該記住舊的狀態(從函數調用返回),並在完成後恢復。
在我的工作中,我們更喜歡RAII這種事情。一個簡單的類這樣做可能是這樣的:
class SignalBlocker
{
public:
SignalBlocker(QObject *obj) : m_obj(obj), m_old(obj->blockSignals(true))
{
}
~SignalBlocker()
{
m_obj->blockSignals(m_old);
}
private:
QObject *m_obj;
bool m_old;
};
編輯:使用Qt 5.3開始,請參見QSignalBlocker(H/T到HappyCactus在評論)
在學習Qt時,我遇到了一個我想更新的「自動」更新的一組互連小部件的問題。我喜歡@ cjhuitt的解決方案,但發現它基於proxy objects的一些語法糖更好。以下是我使用的方法...
首先,我爲阻塞代理對象定義了一個類模板。像Caleb's一樣,這會阻止施工中的信號,然後恢復以前的破壞狀態。然而,它也重載->
操作者的指針返回到阻塞對象:
template<class T> class Blocker {
T *blocked;
bool previous;
public:
Blocker(T *blocked)
: blocked(blocked),
previous(blocked->blockSignals(true)) {}
~Blocker() { blocked->blockSignals(previous); }
T *operator->() { return blocked; }
};
接下來,我所限定的小模板函數來構造,並返回一個攔截器:
template<class T> inline Blocker<T> whileBlocking(T *blocked) {
return Blocker<T>(blocked);
}
把此一起,我會使用這樣的:
whileBlocking(checkBox)->setChecked(true);
或
whileBlocking(xyzzySpin)->setValue(50);
這爲我帶來了RAII的所有好處,在方法調用周圍自動配對阻塞和恢復,但我不需要命名任何包裝器或狀態標誌。它很好,很容易,而且相當乾淨。
非常優雅,謝謝 – galinette 2015-04-02 22:10:46
Qt5.3引入了QSignalBlocker類,它完全以異常安全的方式執行所需的操作。
if (something) {
const QSignalBlocker blocker(someQObject);
// no signals here
}
即使在QT5中,當有很多/幾件事情要封鎖時,它有點麻煩。這裏有一個多對象版本是簡潔易用:
class SignalBlocker
{
public:
SignalBlocker(QObject *obj)
{
insert(QList<QObject*>()<<obj);
}
SignalBlocker(QList<QObject*> objects)
{
insert(objects);
}
void insert(QList<QObject*> objects)
{
for (auto obj : objects)
m_objs.insert(obj, obj->signalsBlocked());
blockAll();
}
void blockAll() {
for(auto m_obj : m_objs.keys())
m_obj->blockSignals(true);
}
~SignalBlocker()
{
for(auto m_obj : m_objs.keys())
m_obj->blockSignals(m_objs[m_obj]);
}
private:
QMap<QObject*,bool> m_objs;
};
用法:由liaK提出
void SomeType::myFunction()
{
SignalBlocker tmp(QList<QObject*>()
<< m_paramWidget->radioButton_View0
<< m_paramWidget->radioButton_View1
<< m_paramWidget->radioButton_View2
);
// Do more work, ...
}
的斷開再連接場景似乎比阻塞所有信號這一特定任務更好。 – Longfield 2010-08-24 13:58:09
@Longfield:我認爲使用'clicked'信號最適合這個特定的任務。 – Job 2010-08-24 14:01:33
好吧,狀態也可以通過程序進行更改,而不一定由用戶通過UI進行更改。無論如何,我們在這裏討論的細節,我猜metdos已經找到了適合他的解決方案。 – Longfield 2010-08-24 21:19:08