QT und Templates
-
klar kannst du auch Qt models in konsolen programmen verwenden...
Und nur weil es auf stackoverflow was steht muss es nicht heißen dass es korrekt ist...
Und Qt != Gui. Qt ist ein "komplettes" Application Framework wo die Gui nur ein kleiner Teil davon ist.
Ich glaube so langsam trifften wir vom eigentlichen problem komplett ab...
Und ich denke das wir aneinander vorbei reden, da auser dir vermutlich keiner weis was du eigentlich vor hast. Zu mindestens geht es mir so.
Also beschreib bitte genau was du eigentlich vor hast. Und mir geht es dabei um das Prinzip/Idee.
-
Eisflamme schrieb:
Der Witz an MVC ist, dass das Model unabhängig vom UI(-Framework) ist.
Im Grunde ja. Aber das model muss eine wohl definierte Schnittstelle bieten, damit ein View oder Controller auch unabhängig einer konkreten model instanz/Implementierung funktionieren kann.
Und wenn du verschiedene "Frameworks" mischt so bleibt es nicht aus, dass du Adapter Objekte erstellen musst.
Und da Qt auch entsprechende Views bereitstellt, definiert es auch eine entsprechende Schnittstelle (in form von QAbstractItemModel).
Eisflamme schrieb:
Wenn QT mir aber das Model stellt, ist das eben nicht der Fall. Oder wie soll ich mein eigentliches Model jetzt noch in einer Konsolenanwendung oder als Basis für eine Handy-App wiederverwenden?
Qt an sich stellt dir in erster Hinsicht nur eine definierte Schnittstelle bereit. Und die im Qtgui(qt4)/qtwidget(qt5) bereitgestellten Views verwenden diese Schnittstelle.
Eisflamme schrieb:
Daher taugen die QT-Models nur dazu zwischen eigentlichem Model zu vermitteln. Wer aber zwischen Model und View vermittelt, wird üblicherweise Controller genannt. So ist die Terminologie logisch.
Sehe ich nicht so. Laut der Definition die ich kenne hat ein View lesenden zugriff auf das Model. Nur über den Controller können Änderungen am Model vorgenommen werden (z.b. um Input Validierung zu ermöglichen).
Quellen:
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
http://msdn.microsoft.com/en-us/library/ff649643.aspxDie Qt-Models (Abgeleitete Klassen von QAbstractItemModel) an sich sind Models. QAbstractItemModel definiert nur die Schnittstelle.
In Qt sind View und Controller eine Entität. Und dadurch ist das Model-View Framework in Qt eher eine MVC Ausprägung namens ModelDelegate
Eisflamme schrieb:
Edit:
Wobei, eigentlich ist es so auch nicht logisch. VCs sind schon irgendwie verschmolzen, je nachdem, wie man ein View halt definiert. Aber das, was als Model bezeichnet wird, ist dann eben ein Controller auf anderer Ebene. Oder man definiert Model wirklich so wie von QT, aber dann muss man zwangsläufig die Applikationslogik nochmal auf einen Bereich vor dem Model schieben, somit hätten wir also 4 "Arten von Komponenten". Denn zusätzlich zum M(V)C-Konzept von QT sollte man sein "eigentliches" Model/Backend imo als von QT unabhängigen Teil modellieren. Ich finde es unsauber (und man ärgert sich wie ich auch einfach), wenn man nicht einen auslagerbaren Teil QT-unabhängig entwickelt.Aber wie gesagt, verlinkter Beitrag argumentiert das ebenfalls. Ist ne Frage der Ebene, aber vermutlich gab es hier wegen meiner QT-unüblichen Beschreibung einige Missverständnisse.
Ich denke auch das hier Missverständnisse auf beiden Seiten entstanden sind.
Das Model-View Framework in Qt stellt ein paar Views bereit und definiert eine Grundlegende Schnittstelle über welche die Views mit einem Model kommunizieren können. Zusätzlich werden halt schon für ein paar oft genutzte Fälle konkrete Model Implementationen zusätzlich von Qt bereitgestellt.
Wenn du bestehende Views von Qt (und damit sind nicht irgendwelche widgets gemeint) verwenden möchtest/musst, so musst du dich an die definierte Schnittstelle halten.
Aber es spricht nichts dagegen im Backend komplett ohne Qt zu arbeiten. Wobei mit Qt 5.2 (mit 5.1 nur Android) auch komplette Smartphone Apps entwickelt werden können.
-
Okay, was Du so schreibst, ist alles in sich schlüssig und ich stimme einem großen Teil auch zu. Der einzige Unterschied ist: Wenn man bei QT den Modelteil wirklich als Model akzeptiert, impliziert das einige Dinge, denen ich widersprochen haben, weil das nach meiner Modeldefinition nicht korrekt ist.
Aber meine Definition ist auch nur eine von vielen, ich bin da jetzt etwas offener und sehe schon, dass man das unterschiedlich definieren kann. Macht die Diskussion leider mühselig, aber die Literatur zum Thema ist ja nicht besser.
Jedenfalls habe ich mein eigentliches Problem ja bereits gelöst. Wenn man so möchte, habe ich bei mir sogar MVC verschmolzen (was aber bei QT-Definition ohnehin für jedes Widget, welches nicht View/Model unterstützt der Fall ist), wobei mein eigentliches Backend (also QT-unabhängig, somit noch vor Model) eben Template ist.
Möchte ich im QT-UI entsprechend die Templateklassen darstellen, dann sollte das Widget meiner Meinung nach auch template sein. Alternativvorschläge hier im Thread basieren ja nur auf std::function, was aber nur dann ginge, wenn meine Backend-Klassen so einfältig wären, dass dort nur Text gelesen und gesetzt würde.
Tatsächlich werden über 5 verschiedene, je schreibende und lesende Methoden aufgerufen, was einfach eine Abhängigkeit vom Widget zu meinen Templateklassen als deutlich intuitiver erscheinen lässt. Kenntnis von Templateklassen erfordert aber eben, dass mein Widget selbst template wird. Und das ist auch völlig logisch und sauber in meinem Design.
Kritik dazu nehme ich gerne mit sachlichen (und nicht für mich leider unnachvollziehbaren Erfahrungsargumenten) entgegen, aber anscheinend haben sich die meisten hier ja wie so oft ohnehin zurückgezogen, als ich die Schwachpunkte deren Lösungen oder Nachfragen Mal ins Feld geführt hab.
Dennoch vielen Dank und beste Grüße!
-
Eisflamme schrieb:
Tatsächlich werden über 5 verschiedene, je schreibende und lesende Methoden aufgerufen, was einfach eine Abhängigkeit vom Widget zu meinen Templateklassen als deutlich intuitiver erscheinen lässt. Kenntnis von Templateklassen erfordert aber eben, dass mein Widget selbst template wird.
Das ein Widget auch ein template sein muss, da es ein Klassentemplate als member referenziert ist falsch. Es mag vielleicht deiner Meinung so sein.
Da du meine Frage nicht beantwortet hast... hier noch einmal etwas abgewandelt:
Wie genau sieht dein Model Konzeptionell aus?(keine konkrete Implementation)
Und wie steht das UI-Element dazu in Beziehung?
-
@firefly: das seh ich anders. Die QAbstractItemModels von Qt sind für mich auch eher Controller. Das sind keine "Modelle". Ich werde meine Logik sicher nicht in Form von QAbstractItemModel definieren, wo ich über Indizes und QVariants auf die Daten zugreife. Das eigentliche Modell besteht aus stark typisierten Klassen, wie Person, AddressBook und was weiß ich was. Das wäre das Modell.
Eine QAbstractItemModel Ableitung, die darauf zugreift, ist nur eine Zwischenschicht. Man kann darüber streiten, ob man sie als Modell oder Controller bezeichnet. Es wäre für mich aber sicher nicht das Modell in dem Sinne von MCV wenn man die gesamte Anwendung betrachtet. Vielleicht sowas wie das ViewModel beim MVVM von Microsoft. Ich seh aber auch kein Problem damit, das QAbstractItemModel als Controller zu bezeichnen.
-
@Eisflamme: ja, ich finds immer noch etwas schwierig, mir konkret vorzustellen, was du willst. Vielleicht kannst du mal ein einfaches Beispiel mit zwei konkreten Modell Klassen und der entsprechenden UI machen, wie du dir das vorstellt und was du da gemeinsam verwenden willst? Also, wirklich ein Beispiel wo man sieht, was die Klassen für Daten beinhalten und wie du die View wiederverwenden willst.
Deinen Vergleich mit Policy Based Design kann ich nicht wirklich nachvollziehen. Beim Policy Based Design geht es meist um grundlegende Sachen, z.B. SmartPointer und Speicherverwaltung. Das ist etwas, was bei so gut wie jeder Klasse Sinn macht. Du hingegen willst die GUI über Templates für verschiedene Klassen wiederverwenden, und das ist etwas, was schon wieder sehr sehr selten zusammenpasst. Es würde mir wahrscheinlich echt schwerfallen, in unserer Software zwei Klassen zu finden, wo man das anwenden könnte.
-
Mechanics schrieb:
@firefly: das seh ich anders. Die QAbstractItemModels von Qt sind für mich auch eher Controller. Das sind keine "Modelle". Ich werde meine Logik sicher nicht in Form von QAbstractItemModel definieren, wo ich über Indizes und QVariants auf die Daten zugreife. Das eigentliche Modell besteht aus stark typisierten Klassen, wie Person, AddressBook und was weiß ich was. Das wäre das Modell.
Eine QAbstractItemModel Ableitung, die darauf zugreift, ist nur eine Zwischenschicht. Man kann darüber streiten, ob man sie als Modell oder Controller bezeichnet. Es wäre für mich aber sicher nicht das Modell in dem Sinne von MCV wenn man die gesamte Anwendung betrachtet. Vielleicht sowas wie das ViewModel beim MVVM von Microsoft. Ich seh aber auch kein Problem damit, das QAbstractItemModel als Controller zu bezeichnen.WO siehst du in QAbstractItemModel COntroller funktionalität?
Was du beschreibst ist eher eine Adaption eines bestehenden "Models" an die Model-View Infrastruktur von Qt (Um bestehende Views von Qt verwenden zu können).
Und das ist nicht der einzige zweck von QAbstractItemModel...
Was nützt dir stark typisierte "Models", wenn du dann für jedes Model einen eigenen View erstellen musst? (da die Anzahl der "Properties" unterschiedlich sein können)
Oder wie meinst du da ein allgemeine Schnittstelle definiert zu bekommen?
-
Ich will keine allgemeine Schnittstelle. Wenn ich mein gesamtes Programm betrachte, ist das Modell die "Geschäftslogik" des Programms. Und das hat nichts mit irgendwelchen Schnittstellen und erstmal auch nichts mit der GUI zu tun. Das ist davon völlig unabhängig. Und das implementiere ich in eigenen Klassen. Denk z.B. an das QFileSystemModel. Das ist NICHT das eigentlich Modell. Das Modell wären QFile, QFileInfo usw. QFileSystemModel ist der Adapter für die GUI, ja.
Warum sollte ich für jedes "Modell" eine eigene View definieren müssen und warum brauchen die Modelle irgendwelchen allgemeinen Schnittstellen? Wir verstehen das Wort Modell einfach unterschiedlich.
QAbstractItemModel entspricht für mich eigentlich perfekt der Definition des Controllers. Das ist die Zwischenschicht zwischen der View und dem Model. Das benachrichtigt z.B. die View über Änderungen des Modells, über allgemeine Signale wie dataChanged. Und das nimmt Commands in allgemeiner Form (setData) von der GUI entgegen und leitet sie an das drunterliegende Modell weiter, indem es in der setData Implementierung die entsprechenden Methoden des Modells (z.B. QFle::rename) aufruft. Aber es implementiert selber keine Geschäftslogik, daher ist es kein Modell.
-
Mechanics schrieb:
Ich will keine allgemeine Schnittstelle. Wenn ich mein gesamtes Programm betrachte, ist das Modell die "Geschäftslogik" des Programms. Und das hat nichts mit irgendwelchen Schnittstellen und erstmal auch nichts mit der GUI zu tun. Das ist davon völlig unabhängig. Und das implementiere ich in eigenen Klassen. Denk z.B. an das QFileSystemModel. Das ist NICHT das eigentlich Modell. Das Modell wären QFile, QFileInfo usw. QFileSystemModel ist der Adapter für die GUI, ja.
Warum sollte ich für jedes "Modell" eine eigene View definieren müssen und warum brauchen die Modelle irgendwelchen allgemeinen Schnittstellen? Wir verstehen das Wort Modell einfach unterschiedlich.
QAbstractItemModel entspricht für mich eigentlich perfekt der Definition des Controllers. Das ist die Zwischenschicht zwischen der View und dem Model. Das benachrichtigt z.B. die View über Änderungen des Modells, über allgemeine Signale wie dataChanged. Und das nimmt Commands in allgemeiner Form (setData) von der GUI entgegen und leitet sie an das drunterliegende Modell weiter, indem es in der setData Implementierung die entsprechenden Methoden des Modells (z.B. QFle::rename) aufruft. Aber es implementiert selber keine Geschäftslogik, daher ist es kein Modell.Ja da haben wir unterschiedliche Definitionen was ein Model ist :).
Bei MVC kann der View auch direkt zugriff auf das Model haben. So interpretiere ich die einzelnen graphischen Darstellungen von MVC die ich im internet gefunden habe.
Und laut Wikipedia gibt es da auch keine genaue Definition was ein Model ist. Je nach Betrachtungsweise kann ein Model Geschäftslogik enthalten oder auch nicht.
https://de.wikipedia.org/wiki/Model_View_Controller schrieb:
Modell
Das Modell enthält die darzustellenden Daten und gegebenenfalls (abhängig von der Implementierung des MVC-Patterns) auch die Geschäftslogik.In der reinen Theorie dürfte ein Model keine Geschäftslogik enthalten, weil sonst das Model nur für einen bestimmten Aufgabenbereich (definiert über die Geschäftslogik) verwendet werden kann.
Aber grundsätzlich ist QAbstractItemModel eine Basis für ein Model und es bietet soviel logik an um daten im model zu verändern.
Z.b. um dein Beispiel Klasse Person = Model aufzugreifen:
- Person definiert bestimmt keine Geschäftslogik es dient eher als Datasource für eine Geschäftslogik (z.b. um Personen in einem Addressbuch zu verwalten).
Aber das gleiche "Model" kann auch dazu genutzt werden um z.b. Profil information über eine Forenmitglied darzustellen. Und hierfür ist eine andere Geschäftslogik notwendig. - Sind die getData/setData Methoden von QAbstractItemModel vergleichbar mit den einzelnen "getter/setter" der Klasse Person um auf die einzelnen Attribute der Klasse zuzugreifen. Im falle von QAbstractItemModel wurde das ganze über ein generisches Interface gelöst damit die Views nur ein Interface kennen müssen.
Und ich sehe da keinerlei Geschäftslogik nur "Methoden" um Werte im "Model" zu lesen/verändern.
Wenn du jetzt eine Instanz von Person z.b. in eine QListView darstellen möchtest (eine Zeile pro Attribut der Klasse). So verwendest du QAbstractListModel (erbt von QAbstractItemModel) eher in der Form Model–view–adapter (MVA)
Mechanics schrieb:
Warum sollte ich für jedes "Modell" eine eigene View definieren müssen und warum brauchen die Modelle irgendwelchen allgemeinen Schnittstellen? Wir verstehen das Wort Modell einfach unterschiedlich.
Müssen nicht, aber da jede Klasse für dich ein Model sein kann und jede Klasse nicht die selben Attribute hat (sonst wären es nicht unterschiedliche Klassen :)) musst du Anpassungen am View vornehmen damit dieser mit dem "Model" umgehen kann.
Und somit hast du ein View fest an ein bestimmen Type von Model gekoppelt. Das ist ja an sich nicht schlecht, wenn es so die Anforderung ist.
Nur dadurch ist der View als solches (z.b. ein ListView/TreeView) nicht mehr verwendbar für andere Typen von Models. Z.b. du kannst ein ListView, welche eine "Person" darstellen kann, nicht verwenden um z.b. "Tier" anzuzeigen. Da beide Klassen keine gemeinsame Schnittstelle definieren, wie auf die einzelnen Attribute zugegriffen werden kann.Mechanics schrieb:
QAbstractItemModel entspricht für mich eigentlich perfekt der Definition des Controllers. Das ist die Zwischenschicht zwischen der View und dem Model. Das benachrichtigt z.B. die View über Änderungen des Modells, über allgemeine Signale wie dataChanged.
Je nach definition ist QAbstractItemModel ein Model oder nur ein Adapter.
Aber an sich bestimmt kein Controller.Denn das Model kann dem Controller eine Möglichkeit bieten über Änderungen im Model selbst zu signalisieren. Und nur durch diese Funktionalität soll dann aus einem Model ein Controller werden?
In Qt existiert der Controller nicht als eigenständige Instanz sondern ist in den Views enthalten.
- Person definiert bestimmt keine Geschäftslogik es dient eher als Datasource für eine Geschäftslogik (z.b. um Personen in einem Addressbuch zu verwalten).
-
Das ein Widget auch ein template sein muss, da es ein Klassentemplate als member referenziert ist falsch. Es mag vielleicht deiner Meinung so sein.
Wenn diese Seitenhiebe nicht aufhören, dann lese ich Deine Beiträge einfach nicht mehr. Hältst Du mich für so blöd, dass ich nicht weiß, dass Folgendes funktioniert?
class SomeClass : public QWidget { private: SomeTemplate<int> elem; };
Selbstverständlich ging es - wie auch schon in mindestens 3 Codes in diesem Thread dargestellt - darum, dass der Templateparameter nicht fixiert ist. Verzichtet man zusätzlich auf gemeinsame Basisklasse - was dann aber einfach Laufzeitpolymorphie wäre - geht es eben nicht ohne. Edit: Halt stimmt nicht, CRTP geht natürlich auch noch, aber ich hoffe, das wird hier nicht ernsthaft erwogen (falls doch hier, warum das Käse ist: ich müsste neue Klassen nebst Vererbungshierarchie basteln, nur damit meine UI-Klasse meine Backend-Klasse nutzen könnte? Erscheint ziemlich viel Boilerplate für nichts; wieso nicht Backend-Klassen mit CRTP? Steht unten)
Da du meine Frage nicht beantwortet hast... hier noch einmal etwas abgewandelt:
Wie genau sieht dein Model Konzeptionell aus?(keine konkrete Implementation)
Und wie steht das UI-Element dazu in Beziehung?Ich kann es nicht genauer spezifizieren, ohne Code zu bringen oder hier einen seitenweisen Roman nur dafür zu schreiben und dann müsstest Du Dich noch über Backgrounds von der Thematik informieren - daher führt der Code auf Mechanics Frage zu mehr, der Dir vielleicht auch hilft (s.u.).
Mechanics:
In eurer Sidediscussion bin ich komplett Deiner Meinung. Ich finde aber immer noch die Antworten auf den Stackoverflow-Artikel interessant und schlüssig. Dort wird häufig zwischen lokalem (QT) Verständnis von MVC und vom globalen (applikationsweit) unterschieden. In ersterem sind die Models durchaus "Models", denn sie hier als Controller zu bezeichnen wäre insofern falsch, als dass die Controller (in der QT-Welt) ja mit den Views ohnehin verschmolzen sind. Somit kann es nicht sein, dass es beide sind. Applikationsweit betrachtet - was imo auch interessanter ist, wenn man QT nur als UI-Framework sieht - sind die Models für mich auch eher Controller.Mechanics:
Okay, ich skizziere nochmal kurz was auf die Schnelle. Aber damit wir hier überhaupt zu was kommen, bitte ich Mal nicht zu hinterfragen, dass ich meinen Proxy so gestaltet habe, wie ich es habe.// Nach Mechanics und meiner Definition eine Model-Klasse, sonst Data oder so class SomeClass { public: typedef std::vector<int> NumberType; // trivial const NumberType& GetNumbers() const {return numbers;} public: NumberType numbers; }; // filtert; macht in echt mehr, sodass das Proxydesign imo sehr vernünftig ist; // CRTP will ich nicht, weil ich damit const-correctness (auf Ebene von Proxy als auch verwiesenes Objekt) nicht erzeugen kann wie es z.B. auch bei einem iterator geht; // so oder so, nicht Gegenstand der Diskussion, dass ich hier nen Proxy verwende; // auch das gehört noch zur Model-Schicht template<typename SomeClassPtr> class SomeClassFilterProxy { public: // nutzt someClass->GetNumbers() aber filtert Primzahlen raus const NumberType& GetNumbers() const; // ist in echt natürlich besser gelöst als hier so plump; // es geht darum, dass SomeClassFilterProxy<...> x(&someObj); mit // x->GetNumbers() funktioniert, ein Proxy sich also bei mir wie ein Zeiger verhält SomeClassFilterProxy* operator->() {return this;} SomeClassFilterProxy(SomeClassPtr someClass) : someClass(someClass) {} private: SomeClassPtr someClass; }; // Jetzt UI (M)VC-Klasse (M für firefly's Definition, nach Mechanics und mir eher C, aber ihr wisst, was ich meine, hoffe ich) template<typename SomeClassPtr> class SpecialDisplayTable : public QLabel { // das Ding stellt die Zahlen in einer Matrix dar, also erste Zeile 1-10, // zweite 11-20 oder so; // die Filtervariante ist aber öffentlich, es graut Zahlen aus, die durch Filter // ausgefiltert werden (dafür habe ich jetzt oben keine Methode skizziert, // es geht nur darum, dass es mehr als nur die GetNumbers()-Methode gibt) // und ein QLabel ist es, weil Zeichnen und Mausklicks über Felder hinweg // gehandhabt werden uvm. public: // parent Gedöns ausgespart SpecialDisplayTable(SomeClassPtr someClass) : someClass(someClass) {} void paintEvent(QPaintEvent* event) { // das nutzt hier jetzt alle möglichen Methoden von someClass aus, // es gibt auch weitere Proxys, das ist hier jetzt nur schematisiert // irgendwo jedenfalls: auto filteredOrUnfilteredNumbers/*depending on SomeClassPtr*/ = someClass->GetNumbers(); // läuft für SomeClassPtr == SomeClass* or == SomeClassFilterProxy/* ohne * */ } private: SomeClassPtr someClass; }; // irgendwo in einem übergeordneten Widget aka die "Anwendung des Tables" { SomeClass someNiceNumbers; // wir nehmen an, da stecken jetzt auf wundersame Weise auch Zahlen drin auto someNiceNumbersFiltered = makeSomeClassFilterProxy(&someNiceNumbers); // makeSCFP nicht definiert, aber Funktionsweise ist ja von shared_ptr u.ä. bekannt // jetzt geht beides und das ist für viele Fälle bei mir äußerst praktisch SpecialDisplayTable* table1 = new SpecialDisplayTable(&someNiceNumbers); SpecialDisplayTable* table2 = new SpecialDisplayTable(someNiceNumbersFiltered); // hier kommt noch mehr }
Das ist wieder nur ein Ausschnitt, aber ich hoffe, der repräsentiert jetzt genug. Abweichungen zum eigentlichen Fall sind:
- Es gibt wesentlich mehr Methoden, die vom SpecialDisplayTable auch genutzt werden (noch mehr Sonderfarben, Verbindung über einen anderen Proxy zu einem Tree, was Spezialisierung/andere Traits nach sich zieht, diverse Manipulationsmethoden je nach Maustaste und Strg/Shift/Alt-Dazudruck usw. usf.)
- Der Proxy ist nur einer der möglichen Proxys und der hier auch aufs filtern reduziert, daher installiere ich in SomeClass auch nicht einfach einen Filter übersetFilter(Filter* someFilter)
, was sonst natürlich eleganter wäre
- Es gibt mehr als nur den SpecialDisplayTable, es gibt auch ein QLineEdit, das ein paar Dinge mehr anzeigt (sagen wir z.B. ein kleines Rechteck im rechten Bereich des Textfeldes, wenn es ein Primzahlfilter ist, mit dem Text "Primzahl"), das nutzt als Zugriff jedoch einen "StringAccessor", der wiederum über SomeClassPtr zugreift; aber das ist hier vermutlich irrelevant
- ... (wichtig?)Ich hoffe, diese Untermenge meines Problems findet einen guten Kompromiss zwischen "nicht erschlagend" und "konkret genug". Falls nicht, füge ich sonst noch was hinzu (d.h. im Falle von meiner falschen Annahme bzgl. "nicht erschlagend" wohl besser nicht
).
-
Eisflamme schrieb:
Das ist wieder nur ein Ausschnitt, aber ich hoffe, der repräsentiert jetzt genug.
Hmm, du hast mich noch nicht überzeugt, warum du in der GUI Templates brauchst. Es geht doch eigentlich um SpecialDisplayTable? Die einzige Stelle, wo du da deinen Templateparameter verwendest, ist die Stelle, um an "NumberType" zu kommen. Das ist aber schon wieder für alle Templateargumente gleich. Warum übergibst du dann nicht einfach dein NumberType an das Widget? Mag sein, dass du den Templateparameter noch an anderen Stellen brauchst, solltest du dann hier auch mal zeigen, sonst ist mir das noch nicht klar. Ist jetzt auch nicht komplett durchdacht, ich überlege beim Schreiben.
Was du jetzt als Beispiel gebracht hast, würde aber wieder gut zu dem Model/View Konzept von Qt passen. Vielleicht ist dein eigentlicher Anwendungsfall anders, aber zumindest bei dem Beispiel seh ich jetzt nicht, warum man nicht die Qt Methoden nutzen sollte. Du hast also eine SpecialDisplayTable, die eine Matrix mit Zahlen darstellen soll. Spricht was dagegen, ein AbstractItemView draus zu machen? Sorry, wenn das schon kam, ich hab die ersten Posts nicht sehr aufmerksam verfolgt.
Dann könntest du ein Qt Model übergeben und daraus die Daten ziehen. Und da könntest du statt deinem eigentlichen Model problemlos ein FilterModel übergeben. Das Filtermodel könnte z.B. data überschreiben und für ForegroundRole z.B. grau zurückgeben. Damit wäre auch die Logik von der Darstellung entkoppelt und deine View müsste nicht selber entscheiden, wann ein Element "grau" ist, sondern würde diese Information transparent vom Model bekommen und das implementiert wiederum die Geschäftslogik hier (im FilterModel) und sagt, das ist grau darzustellen. Warum kann dem View egal sein.
Dein QLineEdit müsstest du jetzt nicht unbedingt von AbstractItemView ableiten, hört sich auf den ersten Blick übertrieben an. Aber da brauchst du auch nicht viele Extrainformationen? Vielleicht kann man ja ein std::function Objekt übergeben, das die eine Zusatzinformation liefert, die du brauchst. Und dann übergibst du da eben eine Methode von deiner Templateklasse, die du jetzt ja auch nicht mehr brauchen würdest.
Also, vielleicht machen Templates hier schon Sinn, aber ich hoffe du siehst, warum du mich noch nicht überzeugt hast.
-
@firefly: ja, was man als Modell betrachtet und wo man die Geschäftslogik implementiert kann man natürlich unterschiedlich sehen und auch unterschiedlich implementieren. Für mich ist (wenn ich meine gesamte Anwendung betrachte) das Modell eben das "Datenmodell" von meinem Programm, also das, was ich im Code nutzen würde.
Eine Person kann durchaus ein trivialer DTO sein. Und die Geschäftslogik ist vielleicht gar nicht in der Klasse Person implementiert sondern in völlig anderen Klassen, z.B: IContactExporter::exportContact(const Person& person) mit einer Implementierung OutlookContactExporter. Aber dann wäre es auf jeden Fall immer noch angenehmer und logischer person.name, person.age usw. zu schreiben, statt person->data(PersonModel::NameColumn).toString(), person->data(PersonModel::AgeColumn).toInt() usw. Damit wäre die Klasse Person das Modell und nicht PersonModel. Und selbst wenn du keine "öffentliche" Klasse Person verwendest, sondern tatsächlich nur ein PersonModel (abgeleitet von QAbstractItemModel) anbietest, wie würdest du da intern die Daten speichern? Doch wohl kaum in einer QMap<int, QVariant>? Eher intern ein struct Person. Und dann kann man es auch gleich öffentlich anbieten und das PersonModel als Wrapper drum rum sehen.
Je komplexer die Daten werden, desto weniger eignet sich QAbstractItemModel dafür. Wir entwickeln z.B. Software im CAD Bereich und als "Modell" könnte man bei uns Bauteile und deren Aufbau betrachten. Es gibt Assemblies, Einzelteile, Konfigurationen, Features usw. Und das ganze ist ein Baum. In dem Modell ist jetzt nicht viel Logik an sich implementiert. Aber es sind grundlegende Datenstrukturen, die überall benutzt werden. Und das seh ich eben als Model. Ein von QAbstractItemModel abgeleitetes AssemblyItemModel für die Darstellung in einer Baumansicht gibts natürlich auch, aber das ist nur ein Adapter, das ist kein Model und wird nur für die GUI verwendet. Und überall, wo man in der GUI was mit den Teilen machen will (also im eigentlichen Controller), holt man dann doch die richtigen "Modellklassen" aus dem ItemModel.
Der Controller ist in MVC am schwammigsten definiert. Aus meiner Sicht gibt es dann eigentlich mindestens zwei Controller, das QAbstractItemModel und den GUI Code, der das ganze zusammenhält, also z.B. EventHandler von irgendwelchen Buttons, die dann irgendwelche Aktionen ausführen. Natürlich ist ein QAbstractItemModel kein "richtiger" Controller, aber ich hab echt kein Problem damit, das als Controller zu bezeichnen. Das ist für mich halt eher Controller als Modell.
-
Grundsätzlich kannst du natürlich auch in Qt Widgets als templates verwenden.
Nur hast du dabei folgende Einschränkungen (zumindestens für Qt4):Signal/Slots funktionieren nicht, da (nach meinem Wissen) in Qt4 ein connect zwischen einem Signal und einem Slot übers Metaobject System realisiert wird.
(Dadurch ist ein fehlerhafter connect kein compiletime fehler sondern ein runtime fehler.)Wenn du auf Signale oder Slots nicht verzichten kannst, so musst du es so machen, wie du es in deinem eingangspost beschrieben hast.
Anders wird es nicht gehen. Das ist eine Einschränkung in Qt4.In Qt5 kannst du als Slot auch z.b. lambdas oder beliebiege Funktionen verwenden
http://qt-project.org/wiki/New_Signal_Slot_Syntax
-
Mechanics schrieb:
Eisflamme schrieb:
Das ist wieder nur ein Ausschnitt, aber ich hoffe, der repräsentiert jetzt genug.
Hmm, du hast mich noch nicht überzeugt, warum du in der GUI Templates brauchst. Es geht doch eigentlich um SpecialDisplayTable? Die einzige Stelle, wo du da deinen Templateparameter verwendest, ist die Stelle, um an "NumberType" zu kommen.
Ich habe nun wirklich mehrfach beschrieben, dass ich es darauf reduziert habe und es eigentlich 5-6 Methoden sind. Dass es sonst mit einem std::function erledigt wäre, habe ich auch verstanden und in diesem Thread mindestens drei Mal angemerkt... Hm, andere Informationen beziehen sich wirklich aufs Schreiben, Lesen und auf Informationen, die zur Darstellung notwendig sind.
Spricht was dagegen, ein AbstractItemView draus zu machen? Sorry, wenn das schon kam, ich hab die ersten Posts nicht sehr aufmerksam verfolgt.
Ich bin damit nicht so besonders firm, aber wieso überhaupt ItemView (und nicht TableView)? Und wenn ich die Maus gedrückt halte und über mehrere Felder gehe, kann ich dann Mehrfachmarkierungen machen? Auch für die Grafik: Ich glaube, ich müsste mehr mit Stylesheets und speziellen Event-Überladungen arbeiten, als es Aufwand ist, einfach gleich selbst ein View basierend auf einem Label zu basteln.
Dann könntest du ein Qt Model übergeben und daraus die Daten ziehen. Und da könntest du statt deinem eigentlichen Model problemlos ein FilterModel übergeben.
Gut, dann gibt es noch zwei Arten des Ausgegrautseins, aber das könnte man da reinstecken. ForegroundRole mag ich nicht, ich habe für die Tabelle ein komplett eigenes Farbset, mit Farbverläufen uvm. Man könnte es dennoch entkoppeln, da hast Du Recht. Da das QT-Model aber ohnehin niemals meine Geschäftslogik ersetzt, hätte ich die Form "Geschäftslogik" <-> Model <-> View+Controller, und Model würde hier nur als Adapter (oder laut Dir Controller) fundieren. Ich gewinne dadurch aber dann nichts, wenn M : V+C dem Verhältnis 1:1 entspricht, was auf Grund des Spezialisierungsgrades meiner Anzeige der Fall ist (und bleiben wird).
Entkopplung ist für diesen Fall kein Selbstzweck (oh, da ist die Menge empört :)), wo habe ich hier wirklich Vorteile?
Vielleicht kann man ja ein std::function Objekt übergeben, das die eine Zusatzinformation liefert, die du brauchst.
Habe ich schon mehrfach oben diskutiert, ich bräuchte viel mehr als eins.
Also, vielleicht machen Templates hier schon Sinn, aber ich hoffe du siehst, warum du mich noch nicht überzeugt hast.
Ja, sehe ich.
Resultiert aber imo daraus, dass ich hier nicht mein Gesamtprojekt offenlege, sondern nur einen Teil, was aber auch bei der Komplexität imo kaum anders geht. Aber es ist gut zu checken, ob andere wirklich anders denken oder ob mein Fall tatsächlich ein Spezialfall ist. Ich komme immer mehr zur Schlussfolgerung, dass letzteres der Fall ist. Trotzdem lerne ich hier einiges, ist also in jedem Fall wertvolles Informationsmaterial.
Dazu auch Deine passende Aussage:
Ein von QAbstractItemModel abgeleitetes AssemblyItemModel für die Darstellung in einer Baumansicht gibts natürlich auch, aber das ist nur ein Adapter, das ist kein Model und wird nur für die GUI verwendet.
Meine eigentlichen Daten sind in gewisser Weise schon vielfältig. Tabellenform ist Anzeigeform, Textform ist Anzeigeform oder anders: Es sind Repräsentationen der Daten (es gibt noch eine Dritte). Die Daten selbst sind auch in einem gewissen Datenschema abgelegt, doch das möchte ich ggü. dem QT-Model natürlich blackboxen. Und QT-Models als Adapter für die Anzeige zu verwenden, ist genau richtig. Individuelle und komplexe Typen mit einer stereotypischen Klasse für Listen/Tabellendaten zu erschlagen bzw. da hineinzupressen empfinde ich als schlechten Stil. Wenn es perfekt passt, könnte man es tun, aber selbst dann finde ich es besser Geschäftslogik nochmal für sich abzubilden und erst dann ein QT-Model dazwischenzulegen, auch wenn die Abbildung hier mehr oder minder 1:1 wäre. Grund: Alle Geschäftslogikdaten sind QT-unabhängig und v.a. stehen die auf einer Ebene für sich. Aber das ist für mich nur ein stilistischer Bonus, den man nicht teilen muss (aber sollte :p). Ich schweife ab.
Grundsätzlich kannst du natürlich auch in Qt Widgets als templates verwenden.
Nur hast du dabei folgende Einschränkungen (zumindestens für Qt4).Der OP dieses Threads trägt der Tatsache Rechnung, dass es die Einschränkungen gibt, sprich: Genau diese Dinge waren mir schon vor Beginn des Threads bewusst. Die Folgediskussion hat sich ja erst dadurch ergeben, dass mein Design basierend auf unvollständigen Informationen angezweifelt wurde (aber das öffnet Horizont und führt zu Lernerkenntnissen, daher ist das gar nicht so negativ gemeint, wie es vielleicht klingt :)).