QMap im globalen Bereich
-
Hallo,
ich versuche gerade mein Projekt umzustrukturieren. Ich generiere aktuell mehrere Clients die jeweils ein Object einer "Client-Klasse" sind. Diese ganzen Clients sind in einer QMap abgelegt mit Ihrer IP als Schlüssel.
Da ich das ganze Programmübergreifend benutzen muss hatte ich mir überlegt das es sinnvoll wäre in der Main direkt die QMap zu erstellen was ich per:
QMap<QString, MovieClient*> movieClients;
erstellt habe. Nun hänge ich aber beim Versuch die QMap im Context bekannt zu machen.
Normale Objekte funktionieren ja nach dem Schema:
UdpReceiver uReceiver; engine.rootContext()->setContextProperty("UdpReceiver", &uReceiver);
was den Fehler "no matching member function for call to "setContextProperty" liefert, auch der Versuch mit "setContextObject" hat keinen Erfolg gebracht.
Hat jemand einen Tipp für mich wie ich die QMap an der Stelle Programmweit erreichbar machen kann?
Ich wollte die QMap mit den Werten die z.B. die uReceiver erhält füllen und im QML auf den Inhalt dann zugreifen. Aktuell habe ich die QMap im Receiver direkt liegen so das sie gefüllt wird mit den Daten die der Receiver erhält. Da ich aber noch Werte später im Programm nachtragen wollte dachte ich mir wäre eine zentrale Stelle wo alle darauf zugreifen können zum ablegen der QMap sinnvoller oder? -
Beim Ausdruck "Gloabaler Speicher" bekommen einige schon Schnappatmung. Vorteil ist natürlich, dass jeder direkt darauf zugreifen kann. Der Nachteil ist aber auch dass jeder direkt unkontrolliert darauf zugreifen. Dies ist nicht unbedingt reentrant und kann zu seltsamen Phänomenen führen.
Ich würde die Liste zumindest nur als statisches Member in eine Klasse packen. Ueber statische Routinen kannst du dann auch von überall her zugreifen, aber du hast wenigstens eine Kontrolle wie zugegriffen wird.
-
Schnappatmung
Der war gut :)
Aber ehrlich, globale Variablen in C++ sind nicht ganz ungefährlich. Zum Beispiel wird
int globalInt = 0
keine großen Problem verursachen, da er beim Programmstart initialisiert wird. Dagegen muss fürQMap<QString, MovieClient*> movieClients;
ein Klassenkonstruktur aufgerufen werden; wann das passiert ist in C++ undefiniert. Es kann also passieren, dass Du auf die Map zugreifst bevor sie initialisiert ist und das gibt einen schönen Crash :)Ehrlicherweise muss ich sagen, dass es auch für dieses Problem in Qt eine Lösung gibt, und zwar
Q_GLOBAL_STATIC
. Besser ist aber wie von @koahnig vorgeschlagen, statische Membervariablen zu verwenden.Grüße
-
Hallo @koahnig ,
danke für deine Hilfe und static ist eigentlich genau das was Sinn ergibt für meine Clientliste.
Ich habe gerade mal etwas gesucht, mit "static" bleibt ja eine Variable erhalten auch wenn die Funktion die sie erstellt beendet ist, nur waren die Beispiele dich ich im Netz gefunden haben bisher so aufgebaut das über eine static int zum Beispiel ein Zähler für die Anzahl der Aufrufe einer Funktion gebaut wurde.
Meine erste Idee einfach bei der Erstellung der QMap ein "static" davor zu setzen um es als statisch zu kennzeichnen war nicht erfolgreich.
static QMap<QString, MovieClient*> movieClients; lieferte: udpreceiver.obj:-1: Fehler: LNK2001: unresolved external symbol "public: static class QMap<class QString,class MovieClient *> UdpReceiver::movieClients" (?movieClients@UdpReceiver@@2V?$QMap@VQString@@PEAVMovieClient@@@@A)
womit ich leider nichts anfangen konnte.
Da du "statischen Routinen" erwähnt hast hatte ich gehofft das mir der Ansatz plus Google mir weiter hilft was leider nicht der Fall war. Kannst du mir vielleicht sagen was du mit den statischen Routinen meintest?
-
Hallo @aha_1980
ich habe versucht mich in die Q_GLOBAL_STATIC und Q_GLOBAL_STATIC_WITH_ARGS einzulesen, und bin jetzt gerade etwas verwirrt. Nach dem was ich gelesen habe erzeugen die beiden ein Object des übergebenen Typs (mit bzw ohne Argumente).
Für eine QMap wie ich sie verwenden wollte (da ich mehrere gleiche Objekte erstellen will) scheine ich die nicht nutzen zu können, oder habe ich das falsch verstanden?
-
@Throndar said in QMap im globalen Bereich:
Hallo @koahnig ,
danke für deine Hilfe und static ist eigentlich genau das was Sinn ergibt für meine Clientliste.
Ich habe gerade mal etwas gesucht, mit "static" bleibt ja eine Variable erhalten auch wenn die Funktion die sie erstellt beendet ist, nur waren die Beispiele dich ich im Netz gefunden haben bisher so aufgebaut das über eine static int zum Beispiel ein Zähler für die Anzahl der Aufrufe einer Funktion gebaut wurde.
Meine erste Idee einfach bei der Erstellung der QMap ein "static" davor zu setzen um es als statisch zu kennzeichnen war nicht erfolgreich.
static QMap<QString, MovieClient*> movieClients; lieferte: udpreceiver.obj:-1: Fehler: LNK2001: unresolved external symbol "public: static class QMap<class QString,class MovieClient *> UdpReceiver::movieClients" (?movieClients@UdpReceiver@@2V?$QMap@VQString@@PEAVMovieClient@@@@A)
womit ich leider nichts anfangen konnte.
Da du "statischen Routinen" erwähnt hast hatte ich gehofft das mir der Ansatz plus Google mir weiter hilft was leider nicht der Fall war. Kannst du mir vielleicht sagen was du mit den statischen Routinen meintest?
So sollte es gehen:
#ifndef MYCLASS_H #define MYCLASS_H #include <QMap> #include <QString> class MyClass { static int MyStaticInt; static QMap < QString, QString *> MyStaticMap; public: MyClass(); }; #endif // MYCLASS_H
#include "MyClass.h" int MyClass::MyStaticInt = 0; QMap<QString, QString*> MyClass::MyStaticMap; MyClass::MyClass() { }
-
Oder mit Q_GLOBAL_STATIC:
#include "MyClass.h" #include <QSet> int MyClass::MyStaticInt = 0; QMap<QString, QString*> MyClass::MyStaticMap; Q_GLOBAL_STATIC (int, MyTotalGlobalInt); Q_GLOBAL_STATIC (QSet<QString>, MyTotalGlobalQSet); typedef QMap<QString,QString> MyMapT; Q_GLOBAL_STATIC(MyMapT,MyTotalGlobalQMap); MyClass::MyClass() { }
So lässt es sich übersetzen.Musst aber einmal schauen, ob es da Nebeneffekte gibt.
Wie geschrieben, die static als Member ist besser.
-
Hallo @koahnig ,
wow vielen vielen Dank!
Mit den Beispielen konnte ich jetzt endlich den Aufbau nachvollziehen wie man static verwenden muss.
Jetzt ist nur noch ein Problem von mir offen an dem ich fest hänge. Ich kann jetzt meine Werte programmweit erreichen, nur komme ich aus dem QML Context nicht ran.
Ich habe ein Objekt der Klasse "UdpReceiver" in der Main erstellt und per
engine.rootContext()->setContextProperty("UdpReceiver", &uReceiver);
im QML verfügbar gemacht. Jetzt kann ich aus dem QML auf die per Q_INVOKEABLE gekennzeichneten Funktionen zugreifen.
Die Klasse selbst habe ich (nachdem ich Konstruktor, Destruktor und CopyKontruktor erstellt habe) per
Q_DECLARE_METATYPE(UdpReceiver)
im MOC bekannt gemacht. Das gleiche habe ich mit der Klasse "MovieClients" gemacht.
Bei dem Versuch jetzt per:
Q_PROPERTY(QMap<QString, MovieClient*> movieClientList MEMBER movieClients)
die QMap für QML nutzbar zu machen bekomme ich als Meldung
Lexical or Preprocessor Issue 17:30: error: too many arguments provided to function-like macro invocation qobjectdefs.h:62:9: note: macro 'Q_PROPERTY' defined here
Ich kann das Programm zwar starten und bekomme auch im QML Context die "movieClientList" zur auswahl, aber scheinbar komplett ohne Ziel (per Console log liefert ein typeof "undefined".
Mache ich da einen Fehler beim Versuch die QMap aus dem QML erreichbar zu machen oder ist mein Ansatz komplett falsch an der Stelle?
-
Nachtrag:
Das Problem mit den "to many arguments" scheint vom Komma in QMap zu kommen.
Ich habe darauf hin versucht per
typedef QMap<QString, MovieClient*> movies;
das Komma zu umgehen. Das klappt soweit, nur meckert er jetzt
QMetaProperty::read: Unable to handle unregistered datatype 'movies' for property 'UdpReceiver::movieClientList'
-
Die Makros haben ein Problem mit dem Komma, das hast du richtig erkannt.
Da musst du wohl noch qRegisterMetaType durchführen. Das zweite Beispiel sieht doch recht ähnlich mit deinem Fall:
typedef QString CustomString; qRegisterMetaType<CustomString>("CustomString");
Da muss ich auch gerade durchhangeln. ;)
-
Hallo @koahnig
super, das mit dem qRegisterMetaType hat mich schonmal einen riesigen Schritt weiter gebracht.
Ich habe jetzt sowohl die Klasse die ich erstellt habe als auch die QMap die Key und die Objekte (MovieClients) der eigenen Klassen enthält damit registriert und komme schonmal soweit das ich per
console.log(UdpReceiver.movieClientList)
ein
qml: QVariant(QMap<QString,MovieClient*>)
zurück erhalte.
(unter UdpReceiver ist der Name des Objekt unter dem ich es im QML Context verfügbar gemacht, movieClientList ist die Property unter der ich die QMap<QString, MovieClient*> bekannt gemacht habe).
Ich scheine also schon bis zum Objekt zu kommen in dem die QMap liegt, nur funktioniert der "normale" Zugriff auf die Werte scheinbar nicht. Im C++ Context würde ich jetzt ja beispielsweise mit
movieClients["192.168.100.94"]->name();
auf die Daten die im MovieClient Objekt mit dem Key "192.168.100.94" an Eigenschat "name" liegen kommen, im QML kommt aber schon bei
console.log(UdpReceiver.movieClientList["192.168.100.94"]) oder console.log(UdpReceiver.movieClientList.value("192.168.100.94"))
Als Antwort ein:
undefined. (erste Anfrage)
bzw
TypeError: Property 'value' of object QVariant(QMap<QString,MovieClient*>) is not a functionzurück.
Ich gehe jetzt davon aus das der Fehler darin liegt das die QMap in einer QVariant liegt, aber wie kann ich auf den Inhalt einer QVariant zugreifen an der Stelle?
-
trotzdem Danke für deine Mühe, immerhin bin ich jetzt schon soweit das ich das was ich habe im QML habe... wenn auch eingepackt in einer QVariant aber schon mal überhaupt da.
Vielleicht hat ja noch jemand anders einen Tipp für mich :)