2010-08-24 69 views
44

我們有一個QCheckBox對象,當用戶選中或取消選中我們想要調用的函數時,我們將函數連接到stateChanged (int state)信號。另一方面,根據一些條件,我們也改變了QCheckBox對象內部代碼的狀態,並且這導致不需要的信號。防止Qt中的射擊信號

在某些情況下有什麼辦法來防止觸發信號?

回答

69

您可以使用clicked信號,因爲只有當用戶實際單擊複選框時纔會發出信號,而不是使用setChecked手動檢查信號時發出信號。

如果你只是不想讓信號在一個特定的時間內發出的,您可以使用QObject::blockSignals這樣的:

bool oldState = checkBox->blockSignals(true); 
checkBox->setChecked(true); 
checkBox->blockSignals(oldState); 

這種方法的缺點是所有信號將被阻止。但我想這在QCheckBox的情況下並不重要。

+4

的斷開再連接場景似乎比阻塞所有信號這一特定任務更好。 – Longfield 2010-08-24 13:58:09

+3

@Longfield:我認爲使用'clicked'信號最適合這個特定的任務。 – Job 2010-08-24 14:01:33

+1

好吧,狀態也可以通過程序進行更改,而不一定由用戶通過UI進行更改。無論如何,我們在這裏討論的細節,我猜metdos已經找到了適合他的解決方案。 – Longfield 2010-08-24 21:19:08

12

可以QObject::disconnect刪除相應的信號槽連接,並且可以QObject::connect再次一旦你完成......

5

QObject派生類,你可以調用blockSignals(bool)防止物體從發射信號。因此,例如:

void customChangeState(bool checked) 
{ 
    blockSignals(true); 
    ui->checkBox->setCheckState(Qt::Checked); 
    // other work 
    blockSignals(false); 
} 

上述方法將改變檢查狀態,而不點擊,stateChanged,或射到任何其它信號。

+2

這不會阻止ui-> checkBox發射信號,所以它不會按預期工作。你應該調用ui-> checkBox-> blockSignals(true)或使用QSignalBlocker(從Qt5.3開始) – HappyCactus 2015-04-17 09:46:22

29

您可以始終使用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在評論)

+2

是否有任何特定的原因遵循這種方式?在這些情況下,只要問B'cos我們習慣了'QObject :: disconnect()'。 – liaK 2010-08-24 13:36:47

+6

這取決於有多少東西可以連接到對象的信號,你如何確信你斷開了合適的連接,以及重新連接它們是多麼容易。我自己,我認爲這通常更容易,但使用斷開連接的作品也是如此。 – 2010-08-24 13:55:56

+0

好啊..謝謝.. :) – liaK 2010-08-24 14:00:41

-1

當一些UI元素應該不迴應用戶可以禁用它。所以用戶會知道這個元素不接受輸入。

+2

你說的是真的,但與OP的問題無關。 – bdesham 2015-03-06 21:36:30

11

在學習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的所有好處,在方法調用周圍自動配對阻塞和恢復,但我不需要命名任何包裝器或狀態標誌。它很好,很容易,而且相當乾淨。

+2

非常優雅,謝謝 – galinette 2015-04-02 22:10:46

2

Qt5.3引入了QSignalBlocker類,它完全以異常安全的方式執行所需的操作。

if (something) { 
    const QSignalBlocker blocker(someQObject); 
    // no signals here 
} 
0

即使在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, ... 
}