2010-07-02 14 views
9

Generalnie mam wiele sygnałów zdarzeń, które chcę podłączyć do tego samego gniazda. Chcę wiedzieć, w jaki sposób mogę przekazywać parametry oparte na łańcuchach do tego samego gniazda, aby gniazdo wiedziało, z którego źródła pochodzi ten sygnał. Jedną z możliwości jest utworzenie tak wielu gniazd, jak sygnały, a następnie połączenie ich w sposób 1: 1, ale jest to efektywne, biorąc pod uwagę, że kod dla całego przetwarzania jest bardzo podobny. Próbowałem to zrobić, ale ja dostaję jakieś błędy:Wiążące argumenty do sygnałów/gniazd

connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); 
connect(button1,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button1"))); 
connect(button2,SIGNAL(clicked()),this,SLOT(backgroundTypeChoiceMade("button2"))); 

Błąd jest związany z parametrami Ja przejeżdżających w ciągu ostatnich 2 komend .. I backgroundTypeChoiceMade jest zadeklarowane tak:

void backgroundTypeChoiceMade(QString); 

Czy ktoś może mi powiedzieć, jaki jest błąd w powyższym kodzie?

Odpowiedz

4

Nie można przekazać stałych do connect(), ponieważ efektywne parametry są wydedukowane w czasie wykonywania, a nie w czasie kompilacji.

Mimo że jest to sprzeczne z zasadą OO, można użyć QObject::sender(), która podaje wskaźnik do emitera QObject.

Przykład poniżej:

void YourClass::YourClass() : 
    m_button1(new QPushButton()), 
    m_button2(new QPushButton()) 
{ 
    connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot())); 
    connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot())); 
} 

void YourClass::yourSlot() 
{ 
    if ((QPushButton* button = dynamic_cast<QPushButton*>(sender())) 
    { 
    // Now button points to a QPushButton* that you can compare with the pointers you already have 

    if (button == m_button1) 
    { 
     // Whatever 
    } else 
    if (button == m_button2) 
    { 
     // Whatever 
    } 
    } 
} 

Jeśli masz wiele przycisków, można również użyć QSignalMapper dostarczając identyfikator dla każdego przycisku.

+1

Można również użyć właściwości objectName: http://doc.trolltech.com/4.5/qobject.html#objectName-prop –

8

Można użyć QSignalMapper. Chociaż QSignalMapper jest odpowiedzią na twoje pytanie, myślę, że odpowiedź Jona Hansona jest drogą, którą powinieneś podjąć. W ten sposób dostajesz o wiele bardziej czysty kod.

+0

Myślę, że w tym przypadku * QSignalMapper * jest drogą do zrobienia. Wyobraź sobie zmianę przycisków na * QComboBox * lub dowolną inną zmianę interfejsu użytkownika. Z QSignalMapper, trywialna zmiana w pliku .cpp. W przypadku oddzielnych gniazd wymagana jest zmiana sygnatury klasy. – hyde

5

Co jest nieefektywne w korzystaniu z oddzielnych gniazd? Jeśli w procedurach obsługi slotów występuje wspólność, przenieś to do funkcji, np. rozszerzenie przykład ereOn za:

void YourClass::YourClass() : 
    m_button1(new QPushButton()), 
    m_button2(new QPushButton()) 
{ 
    connect(m_button1, SIGNAL(clicked()), this, SLOT(yourSlot1())); 
    connect(m_button2, SIGNAL(clicked()), this, SLOT(yourSlot2())); 
} 

void YourClass::common(int n) 
{ 
} 

void YourClass::yourSlot1() 
{ 
    common (1); 
} 

void YourClass::yourSlot2() 
{ 
    common (2); 
} 
+0

Całkowicie się z tym zgadzam. – ereOn

+0

Wyobraź sobie, że interfejs użytkownika musi zostać zmieniony w dowolny sposób i porównaj potrzebne zmiany kodu z tym podejściem i podejściem * QSignalMapper *. – hyde

+0

A co, jeśli twój interfejs użytkownika zmieni się w czasie wykonywania? Nie ma mowy Jose. –

8

Cztery metody. Jeden nie jest do dupy.

  1. QSignalMapper. Działa, ale tworzy niechlujny kod.
  2. Nazwane gniazda. Brudny dla znacznej liczby nadawców i nie działa dla dynamicznie generowanych nadawców (np. Przycisków na liście).
  3. sender() -porównanie. Potrafi obsługiwać dynamiczne nadajniki, ale nadal jest dość brzydka.
  4. Podpis Nie ssać. Daje ci to, czego naprawdę chciałeś przez cały czas: sparametryzowane sygnały.

Szczególnie jeśli używasz niewielkiej liczby sygnałów i typów nadajników oraz kiedy nadawcy są generowani dynamicznie, podklasy nadawcy są najczystszym sposobem. Pozwala to na przeciążenie istniejących sygnałów, aby zawierały potrzebne parametry.

i teraz, okablowania się sygnały i sloty prostu działa:

Keypad::Keypad(QWidget *parent) : QWidget(parent) 
{ 
    for (int i = 0; i < 10; ++i) 
    { 
     // KeypadButton keeps track of the identifier you give it 
     buttons[i] = new KeypadButton(i, this); 
     // And passes it as a signal parameter. Booyah. 
     connect(buttons[i], SIGNAL(clicked(int)), this, SIGNAL(digitClicked(int))); 
    } 
    createLayout(); 
} 

void Keypad::digitClicked(int digit) 
{ 
    // The slot can find the clicked button with ease: 
    dial(button[i]); // or whatever 
    //... 
} 

i dodatkowy kod jest out-of-sight w podklasie nigdy nie będziesz musiał ponownie dotknąć.

Zobacz przykładową implementację podklasy do emisji sygnałów QPushButton w celu emisji sygnałów . Omówiono również wszystkie cztery metody - nazwane gniazda ("rozwiązanie trywialne"), nadawcę(), podklasę i program odwzorowujący sygnał.

Zastrzeżenie: Oczywiście działa najlepiej w przypadku niewielkiej liczby typów nadajników. Ale tak się zwykle dzieje. I w tym przypadku warto.

+1

Myślę, że powinieneś unikać QSignalMapper za każdym razem, gdy (rozsądnie) możesz. Najlepsze rozwiązania (które prowadzą do czystszego kodu) to: 1) podklasa i wyślij żądany identyfikator w sygnale, jak napisał @Foamy. Używamy tego w naszym projekcie Qt, kiedy tylko możemy (ale nie jest to dobry pomysł na podklasę, aby to zrobić) I 2) zdobądź nadawcę() w swoim gnieździe, typuj go i porównuj z jakimkolwiek ID/tagiem, który chcesz i nadawca ma. –

+0

@ViktorBenei Zgadzam się z tym wyjątkiem, że uważam, że warto dla tej podklasy tylko dla sparametryzowanych sygnałów. To trochę więcej pracy w podklasach, ale pozostawia resztę kodu znacznie czystszą. –

0

Jeśli naprawdę nie chcesz używać QSignalMapper, można zrobić coś takiego:

class SignalForwarderWithString: public QObject 
{ 
    Q_OBJECT 
public: 
    SignalForwarderWithString(QString data = "", QObject *parent = 0) : QObject(parent), _data(data) {} 
    QString _data; 
signals: 
    void forward(QString); 
public slots: 
    void receive() { emit forward(_data); } 
}; 

... 
connect(selecter1,SIGNAL(selected(QString)),this,SLOT(backgroundTypeChoiceMade(QString))); 

SignalForwarderWithString *sfws; 
sfws = new SignalForwarderWithString("button1", this); 
connect(button1,SIGNAL(clicked()), sfws, SLOT(receive(QString))); 
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); 

sfws = new SignalForwarderWithString("button2", this); 
connect(button2,SIGNAL(clicked()), sfws, SLOT(receive(QString))); 
connect(sfws, SIGNAL(forward(QString)), this,SLOT(backgroundTypeChoiceMade(QString))); 

ale QSignalMapper jest tak proste ...

QSignalMapper *mapper = new QSignalMapper(this); 
connect(button1, SIGNAL(clicked()), mapper, SLOT(map())); 
mapper->setMapping(button1, "button 1"); 
connect(button2, SIGNAL(clicked()), mapper, SLOT(map())); 
mapper->setMapping(button2, "button 2"); 
// you might have to tweak the argument type for your slot... 
connect(mapper, SIGNAL(mapped(const QString &), this, SLOT(backgroundTypeChoiceMade(QString))); 
0

można teraz naprawdę wiążą wartość podczas łączenia. Qt5 dodał wsparcie dla tego.

przykład:

connect(sender, &Sender::valueChanged, 
    tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1)); 

Patrz more info.

NB: można oczywiście użyć std :: bind lub boost :: bind zamiast tr1 :: bind.