Signal/Slot-Verbindung elegant herstellen



  • Ich benutze Qt, auch wenn die Frage eher konzeptioneller Art und nicht direkt darauf beschraenkt ist:

    Ich habe ein Widget bzw. Objekt (A). Dieses Objekt erstellt (quasi im Konstruktor) zwei Objekte anderer, verschiedener Klassen (B+C). C wiederum erstellt ein Objekt einer neuen Klasse, D. Und D erstellt eine Reihe von Objekten der Klasse Z. Das ganze koennte eine laengere Ahnenkette haben, daher habe ich Punkte ins Diagramm gezeichnet.
    Objekt B kann ich nicht zu einer globalen Variable machen, da es im gleichen Programm noch weitere As inklusive der restlichen Struktur gibt - und jedes hat ein eigenes B.
    Ausserdem gibt es noch weitere C-Objekte, die zu A gehoeren. Und auch deren Ds bzw. Zs muessen benachrichtigt werden.

    B
     /
    A             Z
    |\           /
    | C - ... - D - Z
    |            \
    |\            Z
    | C - ...
    |\
    | C - ...
     \
      C - ...
    

    Ich moechte nun elegant ein Signal von B mit einem Slot von Z verbinden. Zwei moegliche Loesungen sehe ich, aber beide sind unschoen:

    1. Die Konstruktoren von C und D werden so erweitert, dass ich einen Pointer zu B "durchreichen" kann um dann bei D die Verbindung herzustellen.
    2. Ich gebe C und D Slots, die das Signal von B aufnehmen und bis zu Z weiterleiten.

    Wie gesagt: Vielleicht ist das nicht zwingend ein Problem nur fuer Qt. Vielleicht gibt es auch gar keine elegante Loesung, aber ich wuerde mich freuen, wenn mir jemand eine verraten koennte.



  • Mal so aus dem Bauch heraus; was wäre mit Events?



  • Ich würde Variante 2 auf jeden Fall für sauberer halten. Wobei "auf jeden Fall" natürlich relativ ist, ohne Details zu kennen, aber da Objekt Z entsprechende Slots hat, würde es eigentlich nur Kapselung aufbrechen, wenn du da noch das Objekt B reinreichst.

    Variante 3 wäre, dass A die Signal-Slot Verbindung herstellt. Wenn es an Z rankommt. Das wäre wahrscheinlich noch besser, weil dann wäre A eben der Controller.



  • Helmut.Jakoby schrieb:

    Mal so aus dem Bauch heraus; was wäre mit Events?

    Ich habe darueber nachgedacht - aber dazu brauche ich einen Empfaenger, also den Pointer zu Z. Und wenn ich den habe, kann ich auch eine Signal/Slot-Verbindung nutzen. Oder gibt es einen Event-Typ, der nicht nach oben (child->parent), sondern nach unten (parent->child) propagiert wird?

    Mechanics schrieb:

    Ich würde Variante 2 auf jeden Fall für sauberer halten. Wobei "auf jeden Fall" natürlich relativ ist, ohne Details zu kennen, aber da Objekt Z entsprechende Slots hat, würde es eigentlich nur Kapselung aufbrechen, wenn du da noch das Objekt B reinreichst.

    Ich muesste das Objekt B nur bis zu D reichen, von dort aus kann ich ja bereits auf die Zs zugreifen und die Verbindung herstellen.
    Ist beides ziemlich haesslich, wie ich finde.

    Mechanics schrieb:

    Variante 3 wäre, dass A die Signal-Slot Verbindung herstellt. Wenn es an Z rankommt. Das wäre wahrscheinlich noch besser, weil dann wäre A eben der Controller.

    Es kaeme ran ueber diverse

    this->children();
    ...
    

    - aber das ist noch haesslicher als die beiden anderen Loesungen, oder?

    Wenn das ganze nicht huebsch zu loesen ist, dann ist wohl mein ganzes Konzept schlecht. ⚠

    Gibt es in C++ sowas wie eine halbwegs statische Variable? Die koennten sich die Zs teilen, die zu einem A gehoeren. Nur duerfte diese Variable nicht von anderen Zs, die zu anderen As gehoeren, veraendert werden.



  • Stiefel2000 schrieb:

    Oder gibt es einen Event-Typ, der nicht nach oben (child->parent), sondern nach unten (parent->child) propagiert wird?

    Du könntest z.B. einen Event Filter auf QApplication installieren und dann eine eigene QEvent Ableitung verwenden, wenn das "globale" Events sind. Vielleicht können sich dann deine B´s oder wer auch immer das Signal gebraucht hat bei so einem Singleton Event Dispatcher registrieren, der die Events verteilt. So einen Mechanismus kann man sicherlich basteln, aber ich wär vorsichtig damit. Scheint mir in deinem Fall auch nicht wirklich angebracht, weil die Objekthierarchie klar ist und du nicht einfach sagen willst, "hier ist ein Event, machts damit was ihr wollts".

    Es kann durchaus sauber sein, wenn ein Parent Objekt die Slots oder Signals von den Child Objekten delegiert. Ich weiß ja jetzt nicht, was deine ganzen Objekte darstellen, aber es könnte durchaus in Ordnung sein.

    Wenn A nur über children an die Objekte rankommt, wär das natürlich hässlich. Aber wenn es einen definierten Weg gibt, wie man über C auf Z zugreifen kann, wärs völlig in Ordnung.
    Also, ich denk z.B. an sowas:

    connect(treeView->viewport(), SIGNAL(signal()), b, SLOT(slot()));

    Wenn die Objekthierarchie bei dir auch entsprechend fest definiert ist und die Objekte C, D und Z nicht privat sind, würde eigentlich nichts dagegen sprechen, von A aus c->d()->z() aufzurufen. Wenn du das dynamisch über children machen würdest, dann find ich das allerdings auch hässlich 😉



  • Mmh, das Beispiel deines connects haelfe sogar ein Stueck weit. Damit koennte ich das Signal vermutlich von A zu D transportieren und muesste somit nur einen zusaetzlichen Slot erstellen (nutze eine ScrollArea). Und vielleicht kann ich von dort die Weiterverteilung etwas umgestalten.

    Huebsch genug fuers erste, dankeschoen. Aber weiteren Geistesblitzen will ich nicht im Weg stehen. 😉



  • Ich habe B jetzt zu einer globalen Variablen gemacht, die vor Gebrauch durch ein A etwas manipuliert werden muss. Gefaellt mir zwar nicht wirklich, aber es spart eben eine Reihe von Slots (oder Konstruktor-Parametern) ein und ist im Code genau da ansprechbar, wo ich es benoetige.



  • Globale Variablen sind nie gut... Warum brauchst du sowas kompliziertes eigentlich überhaupt? Worum geht es? Ich bin mir nicht sicher, ob Signal-Slot hier überhaupt das richtige Konzept ist. Geht es um irgendwelche Custom-Widgets oder ist es etwas ganz anderes?
    Wir haben in unserer Anwendung schon sowas wie globale Events. Da gehts aber darum, Plugins und Scripte einzubinden. Es gibt Stellen im Programm, die globale Events auslösen und Kontextinformationen mitgeben. Wer dann was ausführt ist völlig egal, da hängen sich dann Scripte ein und können bestimmte Prozesse beeinflußen. Vielleicht hast du ja was ähnliches. Aber globale Variable + Signals + Slots hört sich irgendwie zu statisch an.



  • Es geht um die Aktualisierung eines eigenen Widgets. Und das Programm ist tatsaechlich etwas komplexer - von daher wird es wohl eine bessere Loesung geben, die mir entgeht. Leider bin ich in puncto gut strukturiertem und sauberem Code in keinster Weise ausgebildet, ich gebe mir einfach groesste Muehe.

    Signale und Slots hin oder her: Irgendwie muessen da zwei Objekte kommunizieren, die relativ "weit" voneinander entfernt sind.

    Uebrigens habe ich ein QCache-Objekt, das global ist, und das klingt fuer mich nicht nach etwas Verbotenem. Und wenn ich solche Variablen als statische Klassenvariablen verstecke, ist sicher auch niemandem geholfen.


Anmelden zum Antworten