Konstantes Signal möglich?

Verschiedenes zu Qt
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Konstantes Signal möglich?

Beitrag von deedw »

Hallo,

ist es möglich, ein Signal als const zu deklarieren? Ich würde gerne so etwas machen:

Code: Alles auswählen

class Selection : public QObject
{
	Q_OBJECT
public:
	void select( const double& x, const double& y, const double& z ) const
	{
		emit sig_selectPoint( x, y, z );
	}

signals:
	void sig_selectPoint( const double& x, const double& y, const double& z ) const;

};
Wenn ich das versuche, erhalte ich beim Kompilieren immer die Fehlermeldung:

Code: Alles auswählen

moc_selection.cpp(98): error C2511: 'void Selection::sig_selectPoint(const double &,const double &,const double &)' : overloaded member function not found in 'Selection'
In der moc-Datei steht das Signal auch ohne const, obwohl ich es im Header anders deklariert habe.

Zum Hintergrund (falls mein grundsätzlicher Lösungsansatz falsch ist): Ich habe in einem Schichtenmodell oben eine Klasse A, die selektierte Punkte verwalten muss. Die Punkte selbst werden im Schichtenmodell aber ganz weit unten in einer Klasse B selektiert. Um die Basisklasse B nicht von QObject abzuleiten, erstelle ich in der oberen Klasse A eine Hilfsklasse "Selection", die ich an B gebe. B soll also auf die Selection zugreifen können, damit das Signal ausgesandt wird, was dann bei A landet. Ein direkter Zugriff von B auf A verbietet sich natürlich aufgrund des Schichtenmodells. Ich würde nun gern den Zugriff der Klasse B auf die Selection beschränken, schließlich soll die Klasse nicht irgendwelche Daten in der Selection verändern.

Viele Grüße
Dee
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Warum soll B nicht von QObject abgeleitet werden? Perfomance kann es nicht sein, denn du führst 1) eine zusätzliche Ebene ein, die 2) dann doch von QObject erbt, also irgendwo eine unnötige weitere Indirektion.
Warum muss denn select() const-qualifiziert sein? Ohne kannst du nämlich das signal auch non-const deklarieren, und alles läuft ohne Probleme.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Prinzipiell zum design, wie franzf schon sagt:

Ne klasse mit dem Namen "Selection" laesst vermuten, das sie eine "Auswahl" verwaltet. ein Select aendert diese auswahl, oder nicht ?
das const an der select methode iss IMHO logisch dann ned richtig.
Deshalb die Frage, warum iss das Ding const ?

Ich hab const signale nie gebraucht ... ich glaub auch ned das das gehen wuerde. der moccer besteht halt auf bestimmte randbedingungen.
Rueckgabewerte mag er z.B. auch ned

Ciao ...
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Beitrag von deedw »

Warum soll B nicht von QObject abgeleitet werden?
Die unteren Schichten sind relativ (!) Qt-unabhängig. Die Objekte darin benutzen mal einen QString oder ein QFile, aber sind selbst nicht von QObject abgeleitet. Ist eine reine Designentscheidung.
Warum muss denn select() const-qualifiziert sein?
Zum einen weil die Methode nichts am Objekt ändert, zum anderen weil ich die Hilfsklasse Selection dann nicht also const an Klasse B geben kann. Und wenn Selection nicht const ist, kann B mit ihr machen, was es will.

Ggf. gibt es ja auch eine Möglichkeit, dass Klasse B die nicht-const-Methoden von Selection gar nicht rufen kann. Mir fällt da spontan eigentlich nur ein "friend A;" ein, oder?
ein Select aendert diese auswahl, oder nicht ?
Hm, nein, eigentlich nicht. Die Selection im obigen Fall verwaltet den Typ der Selection und gibt selektiere Punkte wie im Code zu sehen nach außen. Wer auch immer dann damit was anfangen will (Punkte nach cout oder in Datei oder Ähnliches), kann dies tun. Der Selection ist es egal, was damit passiert, sie reagiert auch nicht darauf.

Nochmal zum Design. Klasse A hat auf Klasse B keinen direkten Zugriff, denn Klasse A kennt die Struktur unter sich nur zu einem bestimmten Grad und nicht im Detail. Das bedeutet auch (denke ich zuumindest), dass man Klasse A nicht per Slot an ein Signal von B binden kann, weil man die echten Objekte von B in A gar nicht kennt. A selbst nach unten geben, ist wie gesagt kein gutes Design.

Die einzige Möglichkeit (die mir grad einfällt), wenn B wirklich von QObject abhängt und das Signal selbst ausgibt, wäre, dass derjenige über B das Signal auffängt und weiterreicht. Und der da drüber genauso, solange, bis das Signal bei A angekommen ist. Im Endeffekt hängen dann alle Klassen über B von QObject ab, nur weil sie ein Signal weiterreichen (nicht mal selbst ausgeben) sollen.

Ich gebe zu, dass ich mit beiden Lösungen (nicht const Selection bzw. alles von QObject abhängig) nicht so glücklich wäre, mit friend könnte ich leben.

Gruß Dee
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

deedw hat geschrieben:
Warum soll B nicht von QObject abgeleitet werden?
Die unteren Schichten sind relativ (!) Qt-unabhängig. Die Objekte darin benutzen mal einen QString oder ein QFile, aber sind selbst nicht von QObject abgeleitet. Ist eine reine Designentscheidung.
Entweder du hältst dein komplettes Konstrukt Qt-frei, verzichtest also auf QFile, QString, usw und verwendest stdlib-Klassen (std::string, std::fstream,...), oder du ziehst die Qt-Abhängigkeit in dein Schichtenmodell, aber dann richtig.
Denn sobald du die Abhängigkeit in dein Projekt ziehst, macht ein teilweiser Verzicht keinen Sinn mehr. Wenn du einzelne Komonenten, die alleinstehend Sinn ergeben, von Qt freihalten kannst, ist das was anderes. Aber bei dir scheinen die Klassen ja voneinander abzuhängen. Jetzt übergibst du der Klasse B ein Selection-Object und machst es damit indirekt abhängig von QObject. Dann mach doch Klasse B gleich zum QObject.
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Beitrag von deedw »

Ja, diese Teilabhängigkeit (sprich, nur Zugriff, aber keine Ableitung) ist etwas fragil. Dennoch besteht ja noch das "Problem", dass ich dann alles von QObject abhängig machen muss, was darüber liegt. Es wäre nicht das ganze Projekt, aber schon ein Großteil.

Aber ich lasse es mir noch einmal durch den Kopf gehen, derzeit unterbinde ich den Zugriff auf die non-const-Methoden aus B heraus durch private setzen und ein "friend A;".

Sollte ich noch an einer anderen Stelle auf das Problem stoßen, werde ich wohl wirklich ein QObject aus allem machen.

Vielen Dank für die Antworten
Dee

PS: Solltest es natürlich doch noch eine Möglichkeit geben, dass ein Signal const wird, dann nur raus damit. :)
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Aber ich lasse es mir noch einmal durch den Kopf gehen, derzeit unterbinde ich den Zugriff auf die non-const-Methoden aus B heraus durch private setzen und ein "friend A;".
Das setzt aber voraus, dass das B-Objekt selber als nicht-const bekannt ist, sonst bringt dich auch das friend class A nicht weiter - die privaten non-const-Methoden können nicht verwendet werden!

Hat in deinem Modell denn eine Schicht auf die darüber und die darunter liegende Zugriff? Dann braucht es nämlich keine Weitergabe über SIGNAL/SLOT, da tun es gewöhnliche Methodenaufrufe.

Ansonsten solltest du überlegen, ob eine managende Klasse nicht gut wäre, die auf die einzelnen Schichten zugreifen kann. Ich kann mir eh nicht vorstellen, dass deine Schichten lose im Raum hängen, irgend wem müssen die ja bekannt sein ;)
Du kannst dir nun einen eigenen Kommunikations-Mechanismus ausdenken, z.B. über IDs/Indizes o.Ä. Jede Schicht hat Zugriff auf das Managende Objekt, dieses bietet Funktionen, um eine bestimmte Schicht zu holen - per Index, Id, Name, ... was du halt brauchst. So kann sich die unterste Schicht ("Klasse B") direkt an die oberste Schicht ("Klasse A") wenden, wenn sich die Auswhl ändert. Schau dir auch mal boost.signal an (oder boost.signal2, ist threadsafe + Header Only), ist vllt. ganz interessant zu kennen.

Aber eigentlich wissen wir immer noch zu wenig über Sinn und genauen Aufbau bzw. das eigentlich zu erreichende Ziel, um andere Voschläge geben zu können.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ja, diese Teilabhängigkeit (sprich, nur Zugriff, aber keine Ableitung) ist etwas fragil. Dennoch besteht ja noch das "Problem", dass ich dann alles von QObject abhängig machen muss, was darüber liegt. Es wäre nicht das ganze Projekt, aber schon ein Großteil.
Naja, Ich find die Schlussfolgerung fragil :twisted:
Instinktiv wuerd ich zeurst genau die andere Richtung abchecken, nämlich Deine GUI und die Logic sauber trennen, und nur die GUI QT abhaengig lassen. Also QString und QFile raus aus den Daten.
Aber wahrscheinlich, also zumindest klingt es so, wuerdest du scho arge probleme bei dem Trennen bekommen -> Design ???
Dann wuerde sich schon mal die Frage klären, warum eine "Selection" Signale braucht.... entweder iss der Name sehr irritierend gewahelt, oder was anderes stimmt ned.

Und nochmal zu der const Signal Geschichte.
Fuer die Qt entwickler waer es sicher vertrebarer aufwand, auch das const in der signatur zu beachten.
Es entsteht nur ne weitere Frage. Duerfen const signale dann nur const slots an den anderen Objecten aufrufen ? oder nicht ?
wenn ja bekommst du dann ein quasi globales const was mehrere objecte ueberspannt :-) klingt lustig.
Sicher koennte man das loesen, aber warum ??? wenn es die Leute brauchen, dann gaebe es das sicher scho.

Wie gesagt, QT Trennung GUI und Logic. GUI elemente aendern sich bei Useraktionen, also auch bei selektionen ... Datenelemente muessen nicht ....
In der GUI iss bei mir fast nix konstant. In der Datenschicht dagegen so viel wie möglich/nötig (const correctness).
Selectionen gehoeren eigentlich in die GUI ....

Nur mal als Denkanstoss ...

Ciao ....
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Beitrag von deedw »

Hat in deinem Modell denn eine Schicht auf die darüber und die darunter liegende Zugriff?
Nein, wobei ich nicht mal sicher bin, ob man dann noch von Schichten spricht. Ist ja eher ein Wollknäuel. ;) Bei mir haben nur die oberen Schichten auf die darunter Zugriff.
Ansonsten solltest du überlegen, ob eine managende Klasse nicht gut wäre, die auf die einzelnen Schichten zugreifen kann.
Das war ursprünglich mit der Selektion angedacht. Diese sollte einfach jeden selektierten Punkt von B erhalten und selbst halten. Die Klasse A hätte dann einfach reingegriffen und rausgeholt, was sie interessiert. Das wäre aber eine Datenverdoppelung gewesen, ich hätte ja zumindest die Referenzen auf die Punkte halten müssen. Die Weitergabe per Parameter über Signale kam mir da sinnvoller vor.

Eine weitere Lösung meines Problems wäre ja auch, wenn ich die Selektion splitte. Einmal in einen Selector, der nur die Punkte weiterreicht. Der darf dann natürlich non-const sein. Und eine Selektion, die const ist, die nur den Typ und notwendigen Daten für die aktuelle Selektion hält.

Aber ich habe gestern eh nochmal drüber nachgedacht und bin inzwischen auch zu dem Schluss gekommen, dass sie Abhängigkeit von QObject mich nicht umbringen wird. Ich bin danach zwar hochgradig abhängig von Qt (Derzeit können alle unteren Klassen leicht auf STL-Funktionen/Klassen umgestellt werden.), ich hoffe aber einfach, dass niemand den unteren Teil für sich herauslösen will, um ihn irgendwo, was Nicht-Qt ist, zu verwenden.
Instinktiv wuerd ich zeurst genau die andere Richtung abchecken, nämlich Deine GUI und die Logic sauber trennen, und nur die GUI QT abhaengig lassen.
Das Argument habe ich schonmal gehört und nicht ganz verstanden. Qt heißt doch nicht gleich QWidget. Nur weil ich QString benutze, hat das noch nichts mit der GUI zu tun. Deswegen gibt es doch extra QtCore. Am Ende sollen meine Basisklassen natürlich nur von QtCore abhängen und meine GUI von QtGUI.
Dann wuerde sich schon mal die Frage klären, warum eine "Selection" Signale braucht.... entweder iss der Name sehr irritierend gewahelt,
Der Name ist nur irritierend gewählt. ;) Ich glaube, mit SelectionHandler wärst Du glücklicher gewesen.
GUI elemente aendern sich bei Useraktionen, also auch bei selektionen ... Datenelemente muessen nicht ....
Sehe ich anders bzw. ist mir das ein zu enges Denken. Wenn oben im Fenster ein Selektion getätigt wird (z.B. durch Ziehen eines Rahmens), müssen die Datenobjekte in der Regel schon wissen, dass sie ein paar Punkte selektieren müssen und zwar aufgrund welcher Bedingungen (z.B. weil weitere Berechnungen davon abhängen, ob und wieviele selektiert wurde).

Ganz im Gegenteil sollte gerade die GUI nicht wissen, wie die Datenstruktur darunter aussieht, damit sie variabel nutzbar ist. Sprich bei mir weiß Objekt A nichts von Object B. Es könnte es zwar nutzen, es soll aber keinen Zugriff darauf geben bzw. ermögliche ich diesen nicht.
Selectionen gehoeren eigentlich in die GUI ....
Sehe ich wie gesagt genau anders. Bei mir ist eine Selektion auf der untersten Schicht und die unteren Objekte benutzten diese Selektion. (Um es verwirrender zu machen: Nicht die Selektion oben, sondern nur ein BoolArray mit Flags, welche Sachen selektiert sind und welche nicht. Ich sollte den Namen oben echt in SelectionHandler ändern.)

Viele Grüße
Dee
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Das Argument habe ich schonmal gehört und nicht ganz verstanden. Qt heißt doch nicht gleich QWidget.
Grob gesehen hasst du sicher recht. Die Stolperstricke verstecken sich da eher in Details. Richtig, Die QT Packete wurden getrennt, QString, etc aus der Objecthirarchie rausgenommen/ bzw waren nicht drinn.
Ergo isses ned "ganz so schlimm", QString und consorten da zu verwenden. Sie sind halt auch schoen Bequem.
viel fieser isses, in der Datenschicht Signale und Slots zu verwenden, und Objecte die von QObject abgeleitet sind. QFile z.b.
Wobei ne serialisierung ned unbedingt in ne datenschicht gehoeren muss ...

Abraten von der QT aus low level schichten tu ich aus anderen Gruenden.
QT ist kein 100% sauberes C++. Qt macht nen ziemliches Spagat zwischen user / einsteigerfreundlichkeit, implizierter Optimierung (um trotzdem schnell zu sein, trotz naiver ansatzpunkte der User) und standard C++. Dabei bleiben einige Dinge auf der Strecke. Die Qt bibs beruecksichtigen nicht alle aspekte guten C++ designs.
Iss aber auch kein Problem, jedes GUI Framework hat das Problem, und im Vergleich iss die QT da noch mit am meisten am Standard.

Aber grad Bibliotheken, low level strukturen mit hohen anforderungen an wiederverwertbarkeit haben da etwas andere Ansprueche als GUI's
Da zaehlen eher definiertes verhalten, laufzeitzusicherungen, schlankheit.
und da hat nen QString gegen z.b. nen std::basic_string keine chance !
QString verwendet implizites sharing. Das macht einige Dinge unvorhersehbar, und kann arge probleme beim miltithreading machen, wenn man ned aufpasst. die STL gibt zusicherungen an das verhalten, an laufzeiten und an nebeneffekte. Die QT kann das auf grund der verwendeten technicken einfach nicht. bei der stl und boost z.b. muss ich die Performance selber implementieren, ich muss mir also immer nen kopf um die kosten machen ... aber auf der anderen seite hab ich auch die möglichkeiten zu optimieren. Bei der QT muss ich die dinger immer so verwenden wie es von TT gedacht ist ... weil ich ned weiss was im hintergrund passiert.
Nen anderes Thema sind die Abhaengigkeiten und versionen. Wenn schon mal in nem Project mit mehreren modulen (aka dlls) und QT gearbeitet hasst, und nicht alle entwickler in "Schlagreichweite" sind, dann lernst die eleganz von qt freien modulen einfach schaetzen ^^ Sie laufen und laufen .... auch wenn mal ne neue QT version kommt ^^
müssen die Datenobjekte in der Regel schon wissen, dass sie ein paar Punkte selektieren müssen und zwar aufgrund welcher Bedingungen
Neee, eigentlich typisches Model-View-Controller Szenario. Nen Datenmodel(Model) sollte nie wissen, was grad mit ihm angestellt wird. Die GUI (View) sollte nur so viel wissen wie sie braucht. Brauchst du weiter logik, Ergo zustaende selectionen etc ... dann ist dafuer eigentlich ein controller zustaendig. Dir fehlt also ne Schicht dazwischen.
zumindest in Umfangreicheren Projecten garantiert dir nur sowas eine gewisse flexibilitaet.
Dein Selection(Handler) koennte also schon ne art controller sein. Aber der sollt zustaende speichern ohne das die Datenstruktur was mitbekommt. Also duerfte der Selectionhandler nie const sein, wenn sich seine zustaende aendern, aber er muesst natuerlich mit ner constanten datenstruktur arbeiten koennen.
anz im Gegenteil sollte gerade die GUI nicht wissen, wie die Datenstruktur darunter aussieht
Richtig, aber IMHO isses schlimmer wenn die Datenstruktur Details/Zustaende ueber die GUI wissen muss, damit die App funktioniert.

Ciao ...
Zuletzt geändert von RHBaum am 23. April 2010 10:17, insgesamt 4-mal geändert.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Aber ich habe gestern eh nochmal drüber nachgedacht und bin inzwischen auch zu dem Schluss gekommen, dass sie Abhängigkeit von QObject mich nicht umbringen wird. Ich bin danach zwar hochgradig abhängig von Qt (Derzeit können alle unteren Klassen leicht auf STL-Funktionen/Klassen umgestellt werden.), ich hoffe aber einfach, dass niemand den unteren Teil für sich herauslösen will, um ihn irgendwo, was Nicht-Qt ist, zu verwenden.
Hast du dir auch schon überlegt, wo, wie und wann du die connects zwischen den Ebenen machen willst?
Und vllt. auch gleich überlegen, ob diese indirekte Verwebung so viel Unterschied macht zu einer direkten. Deine Ebenen liegen ja im gleichen Thread. Dann bekommst du mit einer (default) Qt::AutoConnection beim connect ein Qt::DirectConnection. Das bedeutet, dass beim emit der SLOT direkt aufgerufen wird. Und das bedeutet eben, dass der connect auf einen direkten Funktionsaufruf hinausläuft, nur mit dem Umweg über den Handler.
Deswegen gibt es doch extra QtCore. Am Ende sollen meine Basisklassen natürlich nur von QtCore abhängen und meine GUI von QtGUI.
Genau! Und deshalb liegt QAbstractItemModel auch in QtCore, QAbstractItemView hingegen in QtGui.
Wie wäre es wenn du dich da mal einliest?
GUI elemente aendern sich bei Useraktionen, also auch bei selektionen ... Datenelemente muessen nicht ....
Sehe ich anders bzw. ist mir das ein zu enges Denken. Wenn oben im Fenster ein Selektion getätigt wird (z.B. durch Ziehen eines Rahmens), müssen die Datenobjekte in der Regel schon wissen, dass sie ein paar Punkte selektieren müssen und zwar aufgrund welcher Bedingungen (z.B. weil weitere Berechnungen davon abhängen, ob und wieviele selektiert wurde).
Userinteraktion mit GUI == Rahmen ziehen, Button hovern, clicken, ...
Und dabei ändert sich nunmal das GUI-Element.
Ganz im Gegenteil sollte gerade die GUI nicht wissen, wie die Datenstruktur darunter aussieht, damit sie variabel nutzbar ist.
Das ist aber jetzt komisch. Eigentlich muss doch die GUI wissen was sie visualisieren soll, oder? Du hast ein spezielles Problem (Punkte visualisieren), da macht es doch wenig Sinn, wenn mit deiner GUI auch ein Brotbackautomat gesteuert werden kann, oder?

Überhaupt wäre es nicht schlecht, mal genauer zu erfahren, was denn deine Schichten bei der Selection für Aufgaben erfüllen.
Mit welcher Schicht arbeitet am Ende der Benutzer deines Modells? Mit der obersten, oder?
Das heißt Anfragen über selektierte Punkte oder setzen von Selektionskriterien geschehen in dieser Schnittstelle.
Was spricht denn gegen ein direktes returnen durch die Ebenen hindurch?

Code: Alles auswählen

ResultSet Layer::select( const SelectionRule& rule ) {
    if( canHandle(rule) ) return createResult( rule );
    return nextLayer()->select(rule);
}
So wird die Anfrage von einer Schicht in die andere geleitet, bis eine Schicht die Rule bearbeiten kann und ein Ergebnis erstellt, welches direkt nach oben weitergeleitet wird.
Soll nur ein Beispiel sein. Du kannst natürlich setzen von Regeln und Auslesen der Ergebnisse in separate Funktionen packen.
Auch weiß ich nicht, wie deine Ebenen genau die Punkte verwalten, ob es Geschwisterebenen gibt, usw.
Problem ist halt dass wir bisher nur theoretisieren, und da kann man schlecht konkrete Tips geben :P
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Beitrag von deedw »

Danke, RHBaum, für die Erläuterungen zu Qt. Ja, mir wäre es auch ganz lieb, wenn die Datenschicht nur STL-Funktionen nutzen würde. Aber die Zeit drängt und ich muss irgendwann fertig werden. ;)

Mal zum groben Hintergrund: Es gibt ein fertiges Projekt, dass geometrische Daten auf dem Bildschirm darstellt. Derzeit gibt es da ein angedachtes Schichtenmodell, was aber sehr oft verletzt wird. Daneben gibt es keinerlei Trennung zwischen Daten, Verarbeitung und Anzeige. So werfen die geometrischen Objekte lustig MessageBoxen.

Ziel ist es nun (innerhalb einer endlichen Zeit), das ganze zumindest so umzustellen, dass man ein besseres Schichtenmodell hat, und die Datenhaltung von der Anzeige trennt (was mich einiges an Nerven kostet). Durch die zeitliche Begrenzung kann ich also nicht alles von Grund auf neu programmieren, sondern muss mich einigermaßen an vorhandene Klassen und Strukturen halten.

Obiges Thema entstand eben aufgrund dessen, dass die Objekte, die selektiert (also einen Punkt als ausgewählt markieren, egal wie dieser ausgewählt wurde), die Daten an den Viewer geben müssen, weil der damit auch noch was machen will. Und die bisherige Lösung, einen Pointer auf den Viewer nach unten zu geben, auf den dann jeder zugreifen kann, war nicht so toll.

Da auch in den unteren Schichten bereits (fast) alles von Qt abhängt, habe ich nicht die Zeit, das in STL zu wandeln, auch wenn es möglich wäre. Und weil Signal/Slot so schön ist, nutze ich ja jetzt sogar noch etwas "mehr" Qt als vorher. Dass das Konzept von der Idee schon nicht toll ist, weiß ich. :(
aber IMHO isses schlimmer wenn die Datenstruktur Details/Zustaende ueber die GUI wissen muss, damit die App funktioniert.
Ja, das ist sicherlich korrekt, wobei man hier natürlich unterscheiden muss, finde ich. Nehmen wir an, ich klicke in einer GUI auf den Knopf "Berechne mir XYZ", dann muss dieses XYZ irgendwie nach unten gegeben werden, damit etwas aus den Daten oder in den Daten berechnet wird. Natürlich hängt nun in dieser Aufforderung der indirekte Hinweis, dass in der GUI der Knopf für XYZ gedrückt ist, aber das hat ja nichts mit der GUI an sich zu tun.

Ich versuche solche Informationen mit dem SelectionHandler weiterzureichen, wobei Du natürlich recht hast, dass ich da nicht konsequent genug bin. Hier habe ich z.B. Zustände wie "Wähle alles in diesem 2D-Rechteck (auf dem Bildschirm) aus", was natürlich von der GUI abhängt. Eine schnell umzusetzende, sauberere Lösung ist mir (und vielen meiner Kollegen) leider auch nicht eingefallen.

So, nun zu franzf ...
Hast du dir auch schon überlegt, wo, wie und wann du die connects zwischen den Ebenen machen willst?
In dem Absatz hast Du mich irgendwo verloren, so tief stecke ich wohl nicht in Qt drin. Auf alle Fälle gibt nun Objekt B ein Signal aus, das landet bei B', das leitet es weiter an B'', das leitet es weiter an B''' und dann ist es glaube ich schon bei A, wenn ich mich nicht verzählt habe.
Wie wäre es wenn du dich da mal einliest?
Zu viel für einmal, aber ich habe mir heute das Buch "Pattern-orientierte Softwarearchitektur" von Buschmann, Meunier, Rohner, Sommerlad und Stal vorgenommen. Darin gibt es ein Kapitel zum Thema MVC. Und ich weiß, dass die Anwendung, die ich überarbeite, dem nicht standhält - und auch nach meiner Überarbeitung noch nicht standhalten wird. (Wobei ich die Kritik aus dem Buch an MVC verstehen kann. Die Frage ist auch, ob eine Document-View-Architektur nicht besser wäre für mein Vorhaben. Natürlich nur aus theoretischer Sicht, ich werde es nicht umsetzen können.)
Das ist aber jetzt komisch. Eigentlich muss doch die GUI wissen was sie visualisieren soll, oder?
Finde ich eigentlich nicht. Der Viewer (nicht die GUI, die weiß eh von nix ;)) soll zeichnen, nicht mehr und nicht weniger. Ihm soll sogar egal sein, was er zeichnet, er soll es nur darstellen. Dank QGLWidget geht das ja mit "makeCurrent" und dann zeichnet man einfach mal drauf los. Das muss nicht der Viewer selbst machen (wobei er natürlich das Zeichnen über paintGL() anstößt.)

Ich fände es auch aber seltsam, wenn der Viewer z.B. wissen muss, dass es in der darunterliegenden Schicht z.B. drei Objekte gibt, wovon Objekt 1 immer benutzt wird und Objekt 2 nur manchmal und auch dann nur, wenn nicht Objekt 3 eine Rolle spielt. Das sollte dem Viewer egal sein. Er sollte (in meinen Augen) nur seinem direkten Verbündeten/Untergebenem/Nachbarn sagen, dass er doch nun endlich mal was auf den Bildschirm (bzw. die OpenGL-Befehle in ein gerade aktives Fenster) zeichen soll. Dieser Befehl "zeichne jetzt" wird dann von jedem Objekt in der Struktur solange nach unten gegeben, bis es jemand kriegt, der damit was anfangen kann.
Mit welcher Schicht arbeitet am Ende der Benutzer deines Modells? Mit der obersten, oder?
Es gibt verschiedene Viewer/GUIs, also verschiedene Frontends. Der Benutzer arbeitet natürlich nur mit der GUI, mehr sieht er nicht. Und das sollte natürlich auch die oberste Schicht sein.
Das heißt Anfragen über selektierte Punkte oder setzen von Selektionskriterien geschehen in dieser Schnittstelle.
Korrekt.
Was spricht denn gegen ein direktes returnen durch die Ebenen hindurch?
Der Speicheraufwand. Ich muss dann jeden Punkt zwischenspeichern, der selektiert wurde. Außer der SelectionHandler bekommt noch mehr Logik und filtert diese gleich nach bestimmten Kriterien. Aber das würde den Sinn des Handlers dann ja wieder verfremden, finde ich.

Ich gebe (bei meiner heutigen Lösung) den SelectionHandler nun zwar als const-Objekt nach unten, das Nachobengeben der selektieren Punkte geschieht aber direkt per Signal in den jeweiligen Objekten.

Ich werde mir bei Gelegenheit (wenn morgen nicht so tolles Wetter wäre) auch mal das MVC von Qt durchlesen, ob da noch mehr drinsteht als in dem Kapitel, was ich heute durchgelesen habe. Danke auf alle Fälle für den Link.

Viele Grüße
Dee
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

deedw hat geschrieben:Hier habe ich z.B. Zustände wie "Wähle alles in diesem 2D-Rechteck (auf dem Bildschirm) aus", was natürlich von der GUI abhängt. Eine schnell umzusetzende, sauberere Lösung ist mir (und vielen meiner Kollegen) leider auch nicht eingefallen.
Keine Chance, einen Mapper bei solchen Sachen zwischenzuschalten, der über Zoomstufe/Lage des visualisierten Auschnitts und Position im Model bscheid weiß, und dann mit einem map() Koordinaten umrechnen kann?
Hast du dir auch schon überlegt, wo, wie und wann du die connects zwischen den Ebenen machen willst?
In dem Absatz hast Du mich irgendwo verloren, so tief stecke ich wohl nicht in Qt drin. Auf alle Fälle gibt nun Objekt B ein Signal aus, das landet bei B', das leitet es weiter an B'', das leitet es weiter an B''' und dann ist es glaube ich schon bei A, wenn ich mich nicht verzählt habe.
Das ist ja schön :) aber dir bringt ein emit (fast) nix, wenn du nicht vorher eine QObject::connect gemacht hast. Irgendwo musst du ja deine Schichten erstellen und dann jenen connect durchführen. Das macht hoffentlich nicht der Benutzer deiner Schnittstelle... (Nicht der (End)Benutzer des Programms!)
Warum kann diese Klasse nicht gleich auch als Schnittstelle für callbacks bei erfolgter Selektion fungieren?

Code: Alles auswählen

class SchichtenModell { // Deine Schnittstelle zum Model, mit dem der Programmierer später auf die Daten zugreift
    void selectionCallback( const Result& res );
    void createLayer(const ID& id ) {
        Layer* l = new Layer(id, this);
        // connect(l, SIGNAL(sig_selected(const Result&)), lastLayer(), SLOT(on_selected(const Result&))); // dein jetziger Code (ungefähr)
        insertNewLayerAfter(l, lastLayer());
    }
};

class Layer {
    Layer( SchichtenModell* model) : m_model(model) {}
    void select( const SelectionRule& rule ) {
        Result r = createResult(rule);
        // statt
        // emit sig_selected(r);
        model->selectionCallback(r);
    }
};
Mal als Möglichkeit...

Das ist aber jetzt komisch. Eigentlich muss doch die GUI wissen was sie visualisieren soll, oder?
Finde ich eigentlich nicht.
Sry, war zu schnell :) Die Datenstruktur ist sicher egal. Aber deine Daten braucht irgend eine rendernde Komponente, und die wird wohl das class SchichtenModell von oben sein.
Das muss nicht der Viewer selbst machen (wobei er natürlich das Zeichnen über paintGL() anstößt.)
Und ist paintGL() const? Nö :P
Un darum ging es eigentlich. Eine Benutzereingabe/interaktion VERÄNDERT das Aussehen, und deshalb geht sowas nicht über const, und darum kannst du auf const im SIGNAL getrost verzichten (ich hoffe ich hab den Bogen jetzt korrekt zurück verfolgt :D)
Ich fände es auch aber seltsam, wenn der Viewer z.B. wissen muss, dass es in der darunterliegenden Schicht z.B. drei Objekte gibt, wovon Objekt 1 immer benutzt wird und Objekt 2 nur manchmal und auch dann nur, wenn nicht Objekt 3 eine Rolle spielt. Das sollte dem Viewer egal sein.
richtig! Und darum gibt es EINE Schnittstelle (class SchichtenModell) mit der alle Programmierer, die was mit deinen Daten machen wollen, agieren :)
Mit welcher Schicht arbeitet am Ende der Benutzer deines Modells? Mit der obersten, oder?
Es gibt verschiedene Viewer/GUIs, also verschiedene Frontends. Der Benutzer arbeitet natürlich nur mit der GUI, mehr sieht er nicht. Und das sollte natürlich auch die oberste Schicht sein.
Nicht (End)Benutzer des Programms, sondern Benutzer des Models == Programmierer ;)
Was spricht denn gegen ein direktes returnen durch die Ebenen hindurch?
Der Speicheraufwand. Ich muss dann jeden Punkt zwischenspeichern, der selektiert wurde.
Dir ist klar, dass der Weg über SIGNAL/SLOT einen deutlich höheren Speicher- und Rechenaufwand bedeutet?
Ein QObject::connect bekommt die Namen der Methoden (signal und slot). Der Qt::ConnectionType wird erst zur Laufzeit ausgewertet (Es ist ja bei dem connect noch nicht zwingend bekannt, ob das Objekt jetzt in nem anderen Thread liegt, außerdem kann man den ConnectionType-Parameter erst zur Laufzeit z.B. vom User in ner ComboBox auswählen lassen).
In jedem Fall werden die Argumente zwischengespeichert (mit ner Kopie), dann der SLOT per name gesucht, gefunden, aufgerufen. Bis der String-vergleich abgeschlossen ist, hast du wahrscheinlich schon längst manuell das SelectionResult an den Aufrufer returned. Und warum du da jetzt die Ergebnisse alle speichern (wo?) müsstest, und warum mit dem SelectionHandler eben nicht, das begreif ich leider nicht...
Kannst du da mal aktuellen Code posten?
RHBaum hat geschrieben:QString verwendet implizites sharing. Das macht einige Dinge unvorhersehbar, und kann arge probleme beim miltithreading machen, wenn man ned aufpasst. die STL gibt zusicherungen an das verhalten, an laufzeiten und an nebeneffekte. Die QT kann das auf grund der verwendeten technicken einfach nicht.
Ich hab gelesen, dass der aktuelle GCC den std::basic_string über copy-on-write string-Kopien optimiert. Da threads im aktuellen Standard nicht existieren, wird sich auch keine Implementierung die Mühe machen, Threadsicherheit zu integrieren. Ergo macht ein std::string auch Probleme mit multithreading, wenn man nicht selber Zugriffe locked.
Weiß nicht, ob mit C++0x auf Threadsicherheit bei string-Zugriffen verlangt wird - es wird ja dann threads und das ganze Drumherum geben.
deedw
Beiträge: 15
Registriert: 19. April 2010 13:09

Beitrag von deedw »

Keine Chance, einen Mapper bei solchen Sachen zwischenzuschalten,
Na immerhin das habe ich richtig gemacht. ;) Ja, es gibt bereits einen ProjectionHandler, aber der hängt natürlich auch vom Viewer ab. In ihm stecken alle Informationen, um einen 3-D-Punkt in das 2-D-Fenster zu projizieren, ohne die restlichen Informationen des Viewers zu kennen.
aber dir bringt ein emit (fast) nix, wenn du nicht vorher eine QObject::connect gemacht hast.
Okay, das setzte ich als selbstverständlich voraus. Da B'' ja B' kennt, verbindet sich B'' bei der Erstellung oder bei der Übergabe von B' mit dem Signal.
Das macht hoffentlich nicht der Benutzer deiner Schnittstelle
Es gibt eigentlich keinen Benutzer meiner Schnittstelle. Bisher war das ein Ein-Mann-Projekt. Und die nun genutzte Schnittstelle ist im Vergleich zum gesamten Rest ziemlich klein. Der spätere Benutzer der Schnittstelle hat nichts mit einer Selektion zu tun bzw. wenn er bei sich irgendwas selektiert haben will, muss er sich selbst drum kümmern, diese Selektion zu halten. (Wobei das auch wieder nicht stimmt. Es gibt eine Methode, die sagt "selektiere mir diesen Punkt", aber in dem Fall habe ich ja gar keinen Viewer, der die Punkte abfangen müsste.)
Dir ist klar, dass der Weg über SIGNAL/SLOT einen deutlich höheren Speicher- und Rechenaufwand bedeutet?
Nein, ist es/war es nicht. Wenn SIGNAL/SLOT so ... suboptimal ist, wieso nutzt man es dann überhaupt? Nur, weil es praktisch ist? Ob es bei mir einen Unterschied macht, müsste ich natürlich testen. Ich denke, bei mir sind die Flaschenhälse eh an anderer Stelle.
Und warum du da jetzt die Ergebnisse alle speichern (wo?) müsstest, und warum mit dem SelectionHandler eben nicht, das begreif ich leider nicht...
Nein, nicht alle speichern, das ist ja der Grund, wieso ich sie an den übergeben wollte, der entscheiden kann, wie viele selektierte Punkte er noch braucht. Der Viewer oder Szenenhandler brauchen effektiv nur die ersten vier selektierten Punkte. Eine Möglichkeit ist es natürlich, in den SelectionHandler ein Array der Größe 4 abzulegen und dieses befüllen zu lassen bzw. bei der Selection ein Array der Größe 4 mitzugeben, um dieses zu befüllen. Ist wohl dann doch die schnellere Lösung als mit Signal/Slot, werde ich nachher mal testen. (Wobei ich die Geschwindigkeit nicht testen kann. Ich kann nur testen, ob es funktioniert.)
Kannst du da mal aktuellen Code posten?
Originalcode nicht. Ich kann versuchen es zu abstrahieren, aber da hängen verdammt viele Klassen mit drin, das würde eine Weile dauern. Von was genau willst Du denn etwas sehen? Selektion?

Viele Grüße
Dee
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Interessant ist eigentlich nur
*) Die Schnittstelle, mit der der Programmierer dein Modell bedient.
*) Die Layer-Schnittstelle
*) Wie/Wo die Schichten zusammengesetzt werden
*) Evtl. kurze Erklärung was modelliert wird
Zur Schnittstelle natürlich kurze dokumentierende Erklärungen, wenn die Bezeichner nicht selbstredend sind.

Wegen SIGNAL/SLOT:
Signal/Slot sollen nicht normale Funktionsaufrufe ersetzen. Es bietet die Möglichkeit, zwei Objekte die nichts voneinander wissen, zu verbinden, so dass bei einer versandten Nachricht des einen Objektes das andere reagieren kann. Es wäre ein extremer Overhead, das alles über eigene Interfaces usw. diese Möglichkeit nachzubauen. Im Prinzip wäre das ein eigener spezialisierter Event-Mechanismus.

Die andere Möglichkeit wären templates. Schau dich mal schlau in boost.signals / boost.signals2.
Hier die Erklärung, warum man bei Qt keine templates verwendet hat.
Antworten