Re: QT 3.3.4 Kommunikation zwischen Dialogen

Stefan Hundhammer <[email protected]>
Newsgroups gmane.linux.suse.programming
Organization SuSE Linux AG
Message-ID <[email protected]>
On Saturday 18 June 2005 13:35, Norbert Pilsl wrote:
> >>  Mit Qt könnte man auch den einen ein Signal versenden lassen, das man
> >> auf höherer Ebene mit einem Slot auf der anderen Seite verbindet - dann
> >> wären die Komponenten maximal entkoppelt.
>
> Diese Idee gefällt mir auch am besten. Einen Slot zu schreiben ist kein
> Problem. Nur mit dem Signal habe ich Probleme. Wenn ich das richtig sehe
> kann man nur die vordefinierten Signale versenden. (Mausclick oder anderes)

Nein, natürlich nicht - dann wäre das Konzept ja reichlich nutzlos.
Du kannst selber Signale mit beliebigen Parametern definieren und zu 
beliebigen Zeitpunkten versenden. Es empfiehlt sich aber, sich auf 
einigermaßen gängige Datentypen zu beschränken - dann erhöht sich der 
Wiederverwendbarkeitsfaktor der Komponenten, die man son entwickelt hat.

Um selber ein Signal zu definieren, muß man es nur im Header-File des Objekts 
(das von QObject abgeleitet sein muß, also auch alles, was von QWidget 
abgeleitet ist) definieren und in einer der Methoden dieses Objekts dann mit 
"emit" versenden. Also etwa:

class MySuperCoolThingy: public QWidget
{
    ...

    signals:
        void doSomething( int howMany );
        void doMore( void );

    ...
};


MySuperCoolThingy::somewhere()
{
    if ( ... )
    {
        emit doSomething( 42 );
    }
    
    if ( ... )
        emit doMore();
}


Die Parameter, die man bei "emit" mitschickt, werden jedem Slot mitgegeben, 
der mit "connect()" mit diesem Signal verbunden wurde. Eben deshalb empfiehlt 
es sich, nicht unbedingt exotische Pointer auf komplexe Strukturen zu 
verwenden (oder gar die komplexen Strukturen selber), sondern nach 
Möglichkeit einfache Datentypen wie int, boolean, QString u.s.w.; dann kann 
man ganz leicht ein solches Signal auch mit einem Qt-Standard-Slot verbinden 
und fertig.

Man kann insbesondere auch ein Signal mit einem anderen Signal verbinden, ohne 
daß man dazu extra noch einen Slot schreiben müßte, der das auch nur 
weiterreicht. Das ist in der Praxis sehr viel nützlicher, als sich das jetzt 
vielleicht anhört.

> "Mein" Signal müsste auch "mittendrin" versendet werden. (z.B. Settings
> geladen)

Ja, natürlich.


> Als "Ersatz" für globale Variablen wäre wohl eine eigene Klasse am Besten.
> Diese speichert die Daten und stellt Methoden bereit um diese Daten zu
> bearbeiten. Diese Klasse müßte im MainWindow erstellt werden.

Das macht das Ganze zwar komplexer, ändert aber am eigentlichen Problem kein 
bißchen: Du mußt eine Verbindung zwischen dem Dialog und dem Hauptfenster 
herstellen.


> Für die anderen Dialoge müßte eine Funktion erstellt werden die den
> Zugriff auf das MainWindow zurückgibt. (So wie unten)
> mainwindow.h muß dann nicht mehr in den Header der anderen Dialoge.
> Leider habe ich bei dem Beispiel noch ein Verständnisproblem:
>
>
> mainwindow.cpp:
>
>   // Die Variable für den eingenen Zugriff wird
>   // erstmal in einen definierten
>  // Zustand versetzt.
> MainWindow::_mainWindow = 0;
>
> // Der Sinn dieser nachfolgender Zeilen ist mir nicht klar
>
> MainWindow::MainWindow( ... )
>
>     : QMainWindow( ... )
>
>  {
>       ...
> // Setzen des eigenen Objektes
>       _mainWindow = this;
>   }
>
> // Rückgabe des eigenen Objekts
>   MainWindow::mainWindow()
>   {
>       return _mainWindow;
>   }

Damit implementiert man den Zugriff auf ein Singleton-Objekt - d.h. du weißt 
von außen gar nichts über irgendwelche Instanzen dieser Klasse, insbesondere 
hat der Aufrufer keinen Zeiger oder keine Referenz auf eine Instanz. Aber man 
weiß, daß es davon genau eine gibt, und den Zeiger darauf kann man sich so 
holen.


> // Rückgabe des Textes
>   QString MainWindow::sonstwas() const
>   {
>       return _sonstwas->text();
>   }
>
>
> Gehe ich recht in der Annahme das man mit diesen System so ziemlich alle
> Aufgaben des Datenaustausches lösen kann (soll) ?

Das sind nur so die ganz gängigen Sachen - man will mit den Zugriffsmethoden 
Objektattribute auslesen können ( sonstwas() ) genauso wie setzen 
(setSonstwas() ).

Von der Namensgebung her gibt es da zwei Lager: Die einen ziehen es vor, eine 
Methode getSonstwas() und eine setSonstwas() zu nennen, die anderen benutzen 
sonstwas() und setSonstwas() mit dem Hintergrund, daß man sehr viel Öfter 
solche Werte holt, als daß man sie setzt.

Qt verwendet sonstwas() und setSonstwas(), deshalb paßt diese Methode besser 
ins Schema, wenn man Qt benutzt - deshalb findet man das auch in 
Qt-Programmen sehr viel öfter als getSonstwas().


> In der Hilfe Funktion des QT - Designer gibt es ein extra Kapitel über
> die Erstellung von eigenen Klassen. Dieses habe ich noch nicht gelesen.
> Konnte mir bis jetzt nicht vorstellen das man schon bei solchen
> "Kleinkram" eigene Klassen einsetzten muß (soll).

Qt erleichtert es mit seinen Signals und Slots enorm, vorhandene Klassen 
einfach zu verwenden, anstatt für jede Kleinigkeit eine eigene abzuleiten. 
Aber ganz ohne eigene Klassen geht es natürlich auch dort nicht.

Bei Qt muß man z.B. nicht eine eigene Klasse MachWasButton() erfinden, die 
z.B. von QPushButton() abgeleitet ist, wenn man nur auf einen Mausklick 
reagieren will - da erzeugt (instanziiert) man einfach einen vorhandenen 
QPushButton() und verbindet sein clicked() -Signal mit dem Slot, der 
tatsächlich etwas tut.


> Bis jetzt habe ich nur mit prozeduralen Sprachen gearbeitet und mein Hirn
> denkt auch immer nur prozedural. Erst mit der Zeit werde ich ein Gespür
> dafür entwickeln wann und wo man eigene Klassen einsetzt.
>
> Kleiner Nachtrag für Stefan Hundhammer:
> Wirklich herzlichen Dank für Dein Beispiel. Das wird mein
> "Grundgerüst" für meine ersten eigenen Klassen.
> Nur eine kleine Kritik:
> Es ist etwas schwierig für das menschliche Auge zwischen
> MainWindow ; QMainWindow ; mainWindow ; _mainWindow
> zu unterscheiden.

Es empfiehlt sich dringend, sich an eine Konvention zur Namensgebung zu 
halten. Es gibt auch hier natürlich verschiedene Ansätze, die alle ihre 
Berechtigung haben - und letztendlich natürlich hauptsächlich eine 
Geschmacksfrage sind.

Was man aber definitiv braucht, ist eine offensichtliche Verbindung zwischen

- Membervariable
- Methode zum Holen
- Methode zum Setzen

Ich verwende hier die Konvention, daß Membervariablen mit einem Unterstrich 
beginnen (andere verwenden m_). Das vermeidet Namenskonflikte innerhalb von 
Klassen und macht außerdem deutlich, daß man mit einer Membervariable 
arbeitet, auch wenn man ein Codefragment nur flüchtig liest. Es gehören also 
immer zusammen:

	sonstwas()	// lesen  - public oder protected
	setSonstwas() 	// setzen - public oder protected
	_sonstwas	// die Variable selber - private (!)

Man kann die private Membervariable natürlich auch irgendwie benennen, aber 
dann wird es wirklich kompliziert - und fehleranfällig. Man hat dann nach 
einiger Zeit ganz schnell vergessen, daß man ja auf "larifari" mit 
"sonstwas()" und "setSonstwas()" zugreift, und wird lange rätseln, was der 
Code denn eigentlich tut. Wenn jemand etwas ändern muß, wird er evtl. nur 
fluchen und (vor allem unter Zeitdruck) "larifari" einfach "public" oder 
"protected" machen und direkt darauf zugreifen - und dann geht nicht nur die 
Abstraktion verloren, sondern auch noch jede Art Fehlerbehandlung, die in 
"setSontwas()" oder "sonstwas()" so stecken mag. Und damit verschenkt man 
einen der allergrößter Vorteile von objektorientierter Programmierung.

Für abgeleitete Klassen gilt das analog - es gibt nicht so wirklich viele 
sinnreiche Arten, wie man Widgets und ähnliche Klassen nennen kann. Speziell 
ein Hauptfenster wird man deshalb auch irgendwas mit "mainWindow" nennen - 
und immer schön aufpassen, daß man nicht künstlich Konfusion einbringt, indem 
man etwa Namen verwendet, die schon mit anderer Semantik vorbelegt sind - 
z.B. "xyApplication" für irgendwas, was von QMainWindow oder QDialog oder 
auch nur QWidget abgeleitet ist - es gibt ja auch QApplication, und das ist 
etwas ganz anderes.

Bevor man hier also glaubt, alles selber komplett anders neu erfinden zu 
wollen, sollte man sich einfach mal ansehen, wie das andere gelöst haben. 
Manche solchen Konventionen haben sich einfach bewährt.


CU
-- 
Stefan Hundhammer <[email protected]>                Penguin by conviction.
YaST2 Development
SUSE Linux Products GmbH
Nuernberg, Germany

-- 
Um die Liste abzubestellen, schicken Sie eine Mail an:
    [email protected]
Um eine Liste aller verfügbaren Kommandos zu bekommen, schicken
Sie eine Mail an: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.