Verbinden überlasteter Signale und Slots in Qt 5

Lesezeit: 6 Minuten

Verbinden uberlasteter Signale und Slots in Qt 5
dtruby

Ich habe Probleme, mich mit der neuen Signal-/Slot-Syntax (mit Zeiger auf Member-Funktion) in Qt 5 vertraut zu machen, wie in beschrieben Neue Signal-Slot-Syntax. Ich habe versucht, dies zu ändern:

QObject::connect(spinBox, SIGNAL(valueChanged(int)),
                 slider, SLOT(setValue(int));

dazu:

QObject::connect(spinBox, &QSpinBox::valueChanged,
                 slider, &QSlider::setValue);

aber ich erhalte eine Fehlermeldung, wenn ich versuche, es zu kompilieren:

Fehler: keine übereinstimmende Funktion für Aufruf an QObject::connect(QSpinBox*&,
<unresolved overloaded function type>, QSlider*&, void
(QAbstractSlider::*)(int))

Ich habe es mit clang und gcc unter Linux versucht, beides mit -std=c++11.

Was mache ich falsch und wie kann ich es beheben?

  • Wenn Ihre Syntax stimmt, dann könnte die einzige Erklärung sein, dass Sie nicht auf die Qt5-Bibliotheken verlinken, sondern zB auf Qt4. Dies lässt sich leicht mit QtCreator auf der Seite „Projekte“ überprüfen.

    – Matt Phillips

    28. Mai 2013 um 14:37 Uhr

  • Ich habe einige Unterklassen von QObject (QSpinBox usw.) eingefügt, sodass QObject enthalten sein sollte. Ich habe versucht, dieses Include hinzuzufügen, aber es wird immer noch nicht kompiliert.

    – dtruby

    28. Mai 2013 um 14:40 Uhr

  • Außerdem verlinke ich definitiv mit Qt 5, ich verwende Qt Creator und die beiden Kits, mit denen ich teste, haben beide Qt 5.0.1 als ihre Qt-Version aufgeführt.

    – dtruby

    28. Mai 2013 um 14:41 Uhr

Verbinden uberlasteter Signale und Slots in Qt 5
Pepe

Das Problem hier ist, dass es gibt zwei Signale mit diesem Namen: QSpinBox::valueChanged(int) und QSpinBox::valueChanged(QString). Ab Qt 5.7 werden Hilfsfunktionen bereitgestellt, um die gewünschte Überladung auszuwählen, damit Sie schreiben können

connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

Für Qt 5.6 und früher müssen Sie Qt mitteilen, welches Sie auswählen möchten, indem Sie es in den richtigen Typ umwandeln:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

Ich weiß, dass es hässlich. Aber daran führt kein Weg vorbei. Die heutige Lektion lautet: Überlasten Sie Ihre Signale und Slots nicht!


Nachtrag: Was an der Besetzung wirklich nervig ist, ist das

  1. man wiederholt den Klassennamen zweimal
  2. man muss den Rückgabewert angeben, auch wenn es normalerweise so ist void (für Signale).

Ich habe mich also manchmal bei der Verwendung dieses C++ 11-Snippets wiedergefunden:

template<typename... Args> struct SELECT { 
    template<typename C, typename R> 
    static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { 
        return pmf;
    } 
};

Verwendungszweck:

connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)

Ich persönlich finde es nicht wirklich sinnvoll. Ich gehe davon aus, dass dieses Problem von selbst verschwindet, wenn Creator (oder Ihre IDE) automatisch die richtige Besetzung einfügt, wenn der Vorgang zum Erfassen des PMF automatisch abgeschlossen wird. Aber in der Zwischenzeit…

Hinweis: die PMF-basierte Verbindungssyntax erfordert kein C++11!


Nachtrag 2: In Qt 5.7 wurden Hilfsfunktionen hinzugefügt, um dies zu mildern, die meiner obigen Problemumgehung nachempfunden sind. Der Haupthelfer ist qOverload (Sie haben auch qConstOverload und qNonConstOverload).

Verwendungsbeispiel (aus den Dokumenten):

struct Foo {
    void overloadedFunction();
    void overloadedFunction(int, QString);
};

// requires C++14
qOverload<>(&Foo:overloadedFunction)
qOverload<int, QString>(&Foo:overloadedFunction)

// same, with C++11
QOverload<>::of(&Foo:overloadedFunction)
QOverload<int, QString>::of(&Foo:overloadedFunction)

Nachtrag 3: Wenn Sie sich die Dokumentation eines überlasteten Signals ansehen, ist die Lösung des Überlastungsproblems jetzt klar in den Dokumenten selbst angegeben. Zum Beispiel, https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 sagt

Hinweis: Das Signal valueChanged ist in dieser Klasse überladen. Um eine Verbindung zu diesem Signal mithilfe der Funktionszeiger-Syntax herzustellen, bietet Qt einen praktischen Helfer zum Abrufen des Funktionszeigers, wie in diesem Beispiel gezeigt:

   connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged),
[=](const QString &text){ /* ... */ });

  • Ah ja, das macht sehr viel Sinn. Ich denke, für solche Fälle, in denen die Signale/Slots überlastet sind, bleibe ich einfach bei der alten Syntax :-). Danke!

    – dtruby

    28. Mai 2013 um 15:22 Uhr

  • Ich war so aufgeregt über die neue Syntax … jetzt ein kalter Spritzer eisiger Enttäuschung.

    – RushPL

    17. Oktober 2013 um 14:10 Uhr

  • Für diejenigen, die sich fragen (wie ich): “pmf” steht für “Zeiger auf Elementfunktion”.

    – Vicky Chijwani

    2. Juli 2015 um 10:41 Uhr

  • Ich persönlich bevorzuge die static_cast Hässlichkeit gegenüber der alten Syntax, einfach weil Die neue Syntax ermöglicht eine Prüfung zur Kompilierzeit für die Existenz des Signals/Slots, wo die alte Syntax zur Laufzeit versagen würde.

    – Vicky Chijwani

    2. Juli 2015 um 10:43 Uhr

  • Leider ist es oft keine Option, ein Signal nicht zu überladen – Qt überlastet oft seine eigenen Signale. (z.B QSerialPort)

    – PythonNut

    1. März 2016 um 15:47 Uhr

1646912411 972 Verbinden uberlasteter Signale und Slots in Qt 5
Toby Speight

Die Fehlermeldung lautet:

Fehler: keine übereinstimmende Funktion für Aufruf an QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Der wichtige Teil davon ist die Erwähnung von “nicht aufgelöster überladener Funktionstyp“. Der Compiler weiß nicht, ob Sie meinen QSpinBox::valueChanged(int) oder QSpinBox::valueChanged(QString).

Es gibt eine Handvoll Möglichkeiten, die Überlastung zu beheben:

  • Geben Sie einen geeigneten Vorlagenparameter an connect()

    QObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged,
                                             slider,  &QSlider::setValue);
    

    Dies zwingt connect() lösen &QSpinBox::valueChanged in die Überlastung, die ein dauert int.

    Wenn Sie ungelöste Überladungen für das Slot-Argument haben, müssen Sie das zweite Vorlagenargument für angeben connect(). Leider gibt es keine Syntax, um nach dem ersten zu fragen, der abgeleitet werden soll, also müssen Sie beide angeben. Dann kann der zweite Ansatz helfen:

  • Verwenden Sie eine temporäre Variable des richtigen Typs

    void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, signal,
                     slider,  &QSlider::setValue);
    

    Der Auftrag an signal wählt die gewünschte Überladung aus und kann nun erfolgreich in die Vorlage eingesetzt werden. Dies funktioniert genauso gut mit dem ‘Slot’-Argument, und ich finde es in diesem Fall weniger umständlich.

  • Verwenden Sie eine Konvertierung

    Wir können vermeiden static_cast hier, da es sich eher um einen Zwang als um die Aufhebung des Schutzes der Sprache handelt. Ich benutze so etwas wie:

    // Also useful for making the second and
    // third arguments of ?: operator agree.
    template<typename T, typename U> T&& coerce(U&& u) { return u; }
    

    Dadurch können wir schreiben

    QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                     slider, &QSlider::setValue);
    

Eigentlich können Sie Ihren Slot einfach mit Lambda umwickeln und das:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
    slider, &QSlider::setValue);

wird besser aussehen. :\

Die obigen Lösungen funktionieren, aber ich habe dies auf eine etwas andere Weise gelöst, indem ich ein Makro verwendet habe. Nur für den Fall, dass es hier ist:

#define CONNECTCAST(OBJECT,TYPE,FUNC) static_cast<void(OBJECT::*)(TYPE)>(&OBJECT::FUNC)

Fügen Sie dies in Ihrem Code hinzu.

Dann dein Beispiel:

QObject::connect(spinBox, &QSpinBox::valueChanged,
             slider, &QSlider::setValue);

Wird:

QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged),
             slider, &QSlider::setValue);

987770cookie-checkVerbinden überlasteter Signale und Slots in Qt 5

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy