QSettings Wert des Schlüssels leer nach Zuweisung

Alles rund um die Programmierung mit Qt
Antworten
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

QSettings Wert des Schlüssels leer nach Zuweisung

Beitrag von Marcus Breuer »

Hallo Qt'ler,

ich bitte Euch um eine kurze Hilfe. Und zwar arbeite ich gerade an einem kleinem Programm.
Ich möchte in der Registry einen Schlüssel anlegen und diesem einen Wert zuweisen. Es soll bspw. der Wert, unten im Code die 20 abgelegt werden, wobei das keine Rolle spielt, um wenn der Wert entsprechend gesetzt ist, zu bestimmen, ob das Programm das erste mal gestartet wurde oder nicht. Daraufhin sollte dann nämlich ein Passwort durch den Benutzer abgelegt werden. Der Wert danach bspw. auf 30 gesetzt werden. Ich denke, dass das hinhauen sollte.
Mein Problem ist jedoch, dass wenn ich mir den Wert des Schlüssels als String ausgeben lasse, dieser leer ist. In die Registry habe ich schon geschaut, ist alles eingetragen. Wieso erhalte ich also einen leeren String?

Eine Frage, die ich dann später wohl noch haben werde, ist:

Mit QCryptograhpicHash könnte ich doch das Passwort, das bei erstmaligem Start gewählt wurde, bspw. mit MD5 verschlüsseln und in der Registry ablegen, liege ich da richtig?

Wenn nun in dem Abfragedialog für das Passwort in das QLineEdit die Zeichenkette eingegeben wurde, könnte ich diese doch dann ebenso mittels obiger Klasse verschlüsseln und per

Code: Alles auswählen

 QByteArray QCryptographicHash::result () const
quasi, mit ein bisschen Umformungen die Werte aus Eingabe und Registry vergleichen?

Ich habe leider auch schon im Forum einiges dazu gelesen, aber so richtig schlau draus bin ich nicht geworden und probiere mir meinen eigenen Dreh daraus zu basteln. Ich wäre für jede Hilfe und Anregungen zu Sicherheit und Co, sofern meine Vorgehensweise übrhaupt halbwegs sicher ist, sehr dankbar. Es muss im Grunde genommen auch nicht das Non-Plus-Ultra sein, aber Schrott ist auch Käse.

Hier noch der Code des Dialogs zur Passwortabfrage, der natürlich noch weit davon entfernt ist, fertig zu sein:)

Code: Alles auswählen

 #include "passworteingabe.h"

#include <QDebug>

passworteingabe::passworteingabe()
{
   setWindowTitle("Anmelden");
   setFixedSize(300, 60);

   eingabe  = new QLineEdit(this);
   eingabe->setGeometry(15, 15, 200, 30);
   anmelden = new QPushButton("Ok", this);
   anmelden->setGeometry(230, 15, 45, 30);


   QSettings *anmeldung = new QSettings("XY", "ZP");
   anmeldung->beginGroup("Anmeldung");
   anmeldung->setValue("CZ", 20);
   anmeldung->setValue("PW", eingabe->text());
   anmeldung->endGroup();

   qDebug() << anmeldung->value("CZ").toString();

}
Wie gesagt, qDebug() << .. zeigt mit einen leeren String an.

Vielen Dank für Eure Mühen. Grüße,
Marcus
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Also ich habe mal etwas weiter gemacht und geschaut, so dass es eventuell etwas sinnvoller wird. Also ich bin zwar nicht mit NSIS vertraut, aber ich habe zumindest schonmal in Erfahrung gebracht, dass man damit Einträge in die Registry schreiben kann. Sonst würde es im Programm, wie ich es vor hatte kaum Sinn ergeben, dass ich einen ersten Start des Programm tatsächlich auslesen kann oder nicht. Denn der Wert muss ja vorher gesetzt werden, da ich in ja sonst im Programm immer wieder auf einen Wert setzen müsste, der mir einen Erststart des Programm anzeigt. So habe ich mir das jetzt gedacht.

Ich habe also vor einen Installer zu generieren, der mir die entsprechenden Einträge schreibt und das diese dann im Programm ausgelesen werden. Dazu habe ich nun im Programm den Code in der main wie folgt geändert:

Code: Alles auswählen

int main(int argc, char* argv[]) {


    QSqlDatabase db_00 = QSqlDatabase::addDatabase("QSQLITE");
    db_00.setDatabaseName("/Users/marcusbreuer/Desktop/Datenbank/Kontakte");
    db_00.open();

    QCoreApplication::setOrganizationName("FireAndIce");
    QCoreApplication::setApplicationName("Datenbank");

    QApplication app(argc, argv);

    Hauptfenster *haupt = new Hauptfenster;
    haupt->show();

    return app.exec();
}
und möchte nun diese Schlüssel auslesen (sie sind aus meinem ersten Versuch noch in der Registry enthalten):

Code: Alles auswählen

#include "passworteingabe.h"

#include <QDebug>

passworteingabe::passworteingabe()
{
    setWindowTitle("Anmeldung");
    setFixedSize(300,60);

    anmeldung = new QSettings();
    QString f;

    if(anmeldung->value("FirstStart").toInt() == 20) {
         f = "Es klappt!:)";
    }
    else
        f = "Leider nein";

    qDebug() << f;

}
Naja, der Wert in der Registry ist auf 20 gesetzt, allerdings bekomme ich für den String f immer "Leider nein" zu sehen. Also irgendwas will nicht, aber ich werde da absolut nicht schlau draus.

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

Beitrag von franzf »

Mit beginGroup() betrittst du eine Config-Gruppe, mit endGroup() verlässt du sie. Nach dem endGroup() musst du die Gruppe explizit mit angeben.
In "/CZ" liegt einfach kein Wert, der liegt unter "/Anmeldung/CZ".
Desweiteren legst du das QSettings-Objekt auf dem HEAP an. Zerstören musst du das selber! Das machst du nicht, genauso machst du kein explizites sync() zum Schreiben der Settings, weshalb die auch nie in der Datei landen! Leg dein QSettings-Objekt einfach nicht mit new an, das soll die Funktion eh nicht überleben, und bei der Zerstörung wird automatisch die Config-Datei (ini, registry, ...) geschrieben.
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Hallo franzf,

vielen lieben Dank erstmal für deine Hilfe. Deine Antwort hat mir Klarheit verschafft, jedoch ist meine Lösung dennoch etwas anders und Gott sei Dank funktioniert sie sogar einwandfrei und in meinem Sinne. ich poste sie mal:

Code: Alles auswählen

    anmeldung = new QSettings("FireAndIce", "Datenbank");
    anmeldung->beginGroup("Anmeldung");
    if(anmeldung->value("FirstStart").toInt() == 20) {
        passworteingabe *start = new passworteingabe;
        int status = start->exec();
    }
    else{
        passwortfestlegen *festlegen = new passwortfestlegen;
        int status = festlegen->exec();
    }
    anmeldung->endGroup();
Ich habe mich etwas an einem Buch entlang gehangelt. Das hatte ich vor meiner Frage zwar auch schon, da habe ich es aber halt falsch gemacht, als ich probiert hatte nach

Code: Alles auswählen

anmeldung->endGroup();
noch auf die Werte in den Schlüsseln zuzugreifen.

Grüße und ein sonniges Wochenende,
Marcus
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Bist du dir denn wirklich sicher, dass anmeldung mit nwe erstellt werden muss? Speziell bei QSettings, wo eine Erstellung verdammt schnell geht, finde ich es unnötig!

Ansonsten: Schön wenn es jetzt geht.
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Es tut mir Leid, aber Du musst mir gerade auf die Sprünge helfen, was Du mit nwe meinst, vielleicht bin ich gerade aber auch scho zu müde, um überhaupt noch irgendwas zu verstehen:(
Also ich wollte das hauptsächlich auch deswegen machen, um weiter in Qt zu kommen. Die Anwendung schreibe ich für einen Bekannten, er hat mich aber auch gefragt, ob ich ihm da eine Passwortanmeldung reinsbasteln kann.

Ich sitze gerade noch ein bisschen dran. Kannst Du mir vielleicht nochmal auf die Sprünge helfen. Ich poste weiter unten die Codefragmente. Die sind vielleicht nicht so schön, ich wollte den Konstruktoren der beiden Dialoge passworteingabe und passwortfestlegen noch einen int und einen string mitgeben, damit das da etwas ordentlicher aussieht und nicht zu so einem Codedschungel wird.

Ich würde jetzt (Annahme der Schlüssel "FistBoot" hat shon den Wert 1) das Programm gerne so gestalten, dass sofern der Vergleich des eingegebenen Passworts mit dem in der Registry negativ ausällt, das Hauptfenster garnicht erst geöffnet wird, sollte der Benutzer einfach den Eingabedialog per x wegklicken. Bisher ist es so, dass danach dennoch das Hauptfenster sichtbar wird.

Am besten mal die Codefragmente, die wichtig sind:

Code: Alles auswählen

#include "passworteingabe.h"

passworteingabe::passworteingabe()
{
        setWindowTitle("Anmeldung");
        setFixedSize(300,60);

        eingabe     = new QLineEdit(this);
        eingabe->setGeometry(15,15,215,30);
        anmelden    = new QPushButton("Ok", this);
        anmelden->setGeometry(245,15,40,30);

        access = false;

        connect(anmelden, SIGNAL(clicked()),
                this, SLOT(benutzerVerifikation()));
}

void passworteingabe::benutzerVerifikation() {
    if(eingabe->text().isEmpty()) {
        QMessageBox fehlerMeldung(QMessageBox::Warning, "Kein Passwort angegeben",
                                  "Bitte richtiges Passwort angeben!",
                                  QMessageBox::Ok);
        fehlerMeldung.exec();
    }
    else if(!eingabe->text().isEmpty()){
            QSettings *passwortVerifikation = new QSettings("FireAndIce", "Datenbank");
            passwortVerifikation->beginGroup("Anmeldung");

            if(passwortVerifikation->value("PW").toString().compare(eingabe->text(), Qt::CaseSensitive) == 0) {
                this->close();
            }
            else {
                QMessageBox falschesPasswort(QMessageBox::Warning, "Falsches Passwort",
                                          "Bitte richtiges Passwort angeben!",
                                          QMessageBox::Ok);
                falschesPasswort.exec();
            }
            passwortVerifikation->endGroup();
    }
}

Code: Alles auswählen

#include "passwortfestlegen.hpp"

passwortfestlegen::passwortfestlegen()
{
    setWindowTitle("Passwort festlegen");
    setFixedSize(360,150);

    passwortFestlegen   = new QLabel("Passwort eingeben:", this);
    passwortFestlegen->setGeometry(15,15,100,30);
    passwort            = new QLineEdit(this);
    passwort->setGeometry(130,15,215,30);
    passwortWiederholen = new QLabel("Wiederholen:", this);
    passwortWiederholen->setGeometry(15,60,100,30);
    bestaetigung  = new QLineEdit(this);
    bestaetigung->setGeometry(130,60,215,30);
    anmelden    = new QPushButton("Ok", this);
    anmelden->setGeometry(305,105,40,30);

    connect(anmelden, SIGNAL(clicked()),
            this, SLOT(speichern()));

}

void passwortfestlegen::speichern()
{
    if(passwort->text().isEmpty()|| bestaetigung->text().isEmpty()) {
        QMessageBox fehlerMeldung(QMessageBox::Critical, "Kein Passwort angegeben",
                                  "Bitte Passwort angeben!",
                                  QMessageBox::Ok);
        fehlerMeldung.exec();
    }
    else if(!passwort->text().isEmpty() && !bestaetigung->text().isEmpty()) {
        int comp = passwort->text().compare(bestaetigung->text(), Qt::CaseSensitive);
        if(comp == 0) {
            QSettings *passwortInRegistry = new QSettings("FireAndIce", "Datenbank");
            passwortInRegistry->beginGroup("Anmeldung");
            passwortInRegistry->setValue("PW", passwort->text());
            passwortInRegistry->endGroup();
            this->close();

        }
    }
}
Und der Anfang des Konstruktors der Klasse hauptfenster:

Code: Alles auswählen

Hauptfenster::Hauptfenster()
{
    anmeldung = new QSettings("FireAndIce", "Datenbank");
    anmeldung->beginGroup("Anmeldung");
    if(anmeldung->value("FirstStart").toInt() == 1) {
        start = new passworteingabe;
        int status = start->exec();
    }
    else{
        festlegen = new passwortfestlegen;
        int status = festlegen->exec();
        anmeldung->setValue("FirstStart", 1);
    }
    anmeldung->endGroup();
Was ich schon erfolglos probiert hatte, war in der Klasse passworteingabe in Abhängikeit der Auswertung eine boolsche Variable mit true oder false zu belegen, diese dann im Hauptfenster nach

Code: Alles auswählen

int status = start->exec();
durch

Code: Alles auswählen

 if(start->hierDieBoolVariable == false) this->close();
abzufangen und dann dann entweder das Programm ganz zu schließen, so wie ich dachte, es oben erreichen zu können oder einfach garnichts zu machen und das Programm dann runterlaufen zu lassen.

Ich hoffe, ich habe mich bei dem ganzen Text noch klar ausgedrückt. Sonst schiebe ich es definitiv auf die Uhrzeit und den Wochentag:)

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

Beitrag von franzf »

Dir sagt nwe nix? :D Kein Wunder, ist ein Schreibfehler, soll natürlich "new" heißen...

Code: Alles auswählen

QSettings *passwortInRegistry = new QSettings("FireAndIce", "Datenbank"); 
Es fehlt aber in dem Scope das dazugehörige delete - das QSettings-Objekt wird nie zerstört!
Besser:

Code: Alles auswählen

QSettings settings("FireAndIce", "Datenbank");
Da musst du dich um gar nix mehr kümmern, Objekt wird automatisch zerstört.
Selbiges gilt auch noch für passworteingabe::benutzerVerifikation()

Code: Alles auswählen

Hauptfenster::Hauptfenster()
{
    anmeldung = new QSettings("FireAndIce", "Datenbank"); 
anmeldung ist doch sicher als "QSettings *anmeldung;" in Hauptfenster deklariert, oder?
Das ginge wieder schöner, wenn das kein Zeiger wäre:

Code: Alles auswählen

class Hauptfenster {
    QSettings anmeldung;
};
Initialisierung dann über die Initialisierungsliste:

Code: Alles auswählen

Hauptfenster::Hauptfenster()
 : anmeldung("FireAndIce", "Datenbank")
{ // usw
Aber noch besser wäre, du ziehst die Instanz wieder direkt an die Stelle an der sie gebraucht wird - also Member raus, und in Hauptfenster() einfach eine Instanz anlegen, so wie oebn in passwortfestlegen::speichern().

Du solltest dir am besten noch mal anschauen, für was "new" (also dynamische Speicherverwaltung) gut ist, und welche Nachteile es mit sich bringt.

Desweiteren wäre ein Blick in Richtung "Layouts" nicht schlecht. Du gibst deinen Widgets momentan feste Größen und Positionen innerhalb ihres parenst. Das ist schlecht, da das Hinzufügen eines neuen Elements viel Codeanpassung (Verschieben der Elemente) bedeutet! Außerdem richtet dich deine jetzige Anordnung nur nach deinen persönlichen Einstellungen: Schriftart, Schriftgröße, verwendeter Style. Das alles sorgt für unterschiedliche Größen der Widgets. Es ist recht wahrscheinlich, dass dein Prgramm auf dem Rechner deines Freundes recht chaotisch daherkommt :D
Fang am besten in der Doku mit QLayout an, und hangel dich dann weiter an den ganzen Spezialisierugen.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Sieht mir irgendwie nach Code eines Java-Umsteigers aus :wink:

Auch die Dialoge würde ich auf dem Stack anlegen. Und evt. auch bessere Namen vergeben (damit Klassentypen und Variablen besser auseinander gehalten werden können):

Code: Alles auswählen

  else{
        PasswortDialog dialog;
        int status = dialog.exec();
    } 
Weiters gehört die QApplication-Instanz immer an den Anfang:

Code: Alles auswählen

int main(int argc, char* argv[]) {
    QApplication app(argc, argv);
   
   ...
}
Die sucht die Qt-Plugins zusammen..Daher stehen gewisse Funktionen in Qt erst nach dem Erstellen der QApp..-Instanz zur Verfügung.
Ich hätte schwören können, dass eine Datenbank-Verbindung vorher nichtmal funktioniert....

hth!
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Guten Morgen franzf,

oh man, ja nwe und new, das hätte mir eigentlich aus dem Kontext mehr als klar sein sollen:) Das Brett vorm Kopf. In dem Fall waren es gleich mehrere.

Ich werde das definitiv so einmal ausprobieren, wie Du mir gesagt hast. Ich denke soweit auch, dass es mit Sicherheit sinnvoller wäre.

Zu den Layouts, ich hatte darauf verzichten wollen, da die Oberfläche an sich eigentlich recht simpel ist. Keine größeren Dialoge, die darin vorkommen etc.
So hatte ich mir dann einfach gedacht, dass ich, wenn ich eine Linie finde, wie groß bspw. ein PushButton sein soll, die Abstände zu anderen Elementen klar sind etc., ich dann eine saubere Formatierung erhalte.
Aber auch das ein guter Hinweis. Ich kann aber auch gerne mal ein paar Screenshots hochladen. Das Blöde ist aber tatsächlich, dass bspw. die MessageBoxen auf Windows ganz gut aussehen, unter Mac jedoch grauenvoll.

Viele Grüße und ein riesen Danke,
Marcus
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Hallo solarix,

Dankeschön auch für deine Antwort. Ich habe jetzt die Änderungen von franzf und von Dir übernommen. Sprich, alles sollte jetzt auf dem Stack liegen.
Die QSettings, wie auch die Dialoge.

Es wäre lächerlich, wenn ich von mir behaupten würde, dass ich ein Java-Umsteiger wäre, denn ich habe es nur mal kurz im ersten Semester gelernt und naja meine Kenntnisse darin sind dann doch sehr bescheiden. Also sie gehen sehr schnell gegen 0.

Allerdings blieb mir noch die Frage:

Wenn ich jetzt den Dialog zur Passworteingabe einfach schließe, dann wird mein Hauptfenster immer ausgeführt. Das soll natürlich überhaupt nicht sein. Wird das falsche oder garkein Passwort angegeben, soll der Dialog zwar geöffnet bleiben, klickt der Benutzer aber auf schließen, dann soll sich das Hauptfenster auf keinen Fall öffnen. Ich habe weiter oben da auch beschrieben, was ich probiert hatte. Aber das scheint nicht wirklich zu funktionieren.
Könnt Ihr mir da vllt. einen Fingerzeig geben, wie ich das in den Griff bekomme?

Grüße,
Marcus
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

So, eben kam mir die Idee, die Settings in der main abzufragen, dort dann nur die beiden Dialoge für eine Passworteingabe einzubinden und je nachdem, was in den Schlüsseln steht, dann den einen oder den anderen zu starten. In den Dialogen wird dann erst die Instanz des Hauptfensters erzeugt, in Abhängigkeit des Vergleichs des eingegebenen Passworts mit dem in der Registry hinterlegtem. Das klappt jetzt soweit schonmal wunderbar. Ohne Passwort kommt man jetzt definitiv nicht mehr ins Programm rein.
Jetzt bleibt nur ncoh die Sicheheit des Passwortes, da dieses ja komplett unverschlüsselt in der Registry drin steht. Ich werde wohl die Tage etwas tüfteln und auch einfach mal Fortschritte posten. Irgendwem wird es ähnlich gehen, wie mir, der findet es dann vllt.
Schönen Samstag. Grüße, Marcus
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Wenn Dialoge (die nur kurzfristig existieren) das Hauptfenster (welches über die ganze Arbeitszeit existiert) erzeugen, geht einiges an Verantwortung flöten (Wer kümmert sich um das Hauptfenster? Wer löscht was? etc.).

Viel besser und einfacher:
Hauptfenster wird angezeigt, über einen QTimer::singleshot wird der Dialog zur Passworteingabe getriggert. Beim Hauptfenster sind am Anfang Menus und Toolbars disabled, ein centralWidget ist NICHT gesetzt. Erst die korrekte Passworteingabe lässt den User mit dem Fenster interagieren.
Wenn du nun am Anfang als CentralWidget deine Maske zur Passworteingabe anzeigst, sparst du dir sogar den Dialog, und (in Meinen Augen) wird das ganze nutzerfreundlicher, da man besser erkennt, was los ist (starte Programm -> kein Hauptfenster sondern ein doofer Passwort-Dialog erscheint ?!?) und es den Fokus des Benutzers auf das Wichtige lenkt (Hauptfenster zeigt nur eine Passworteingabemaske an).
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Cool, Danke. Ich glaube, ich konnte folgen. Ich kann leider gerade nichts mehr am Programm machen, erst morgen früh wieder, dann würde ich es aber gerne mal so ausprobieren.
Also sollte ich es so machen, dass ich:

Im Kontsruktor vom Hauptfenster zunächst alles, was Du mir gesagt hattest auf disabled setze, dann die Schlüssel der registry abfrage und dann bspw.

Code: Alles auswählen

QTimer::singleshot(100, &passworteingabe, SLOT(exec());
aufrufe. Danach folgen dann die Abfragen auf Korrektheit des Passwortes und sollte es stimmen, dann enable ich die Menüs, Toolbars etc... wieder?

Ist das so, wie Du es meintest? Danke für deine mühen.

Viele Grüße,
Marcus
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Ok, ich werde jetzt zwar zu spät zu meinem Termin kommen, konnte es gerade aber noch zu Teilen ausprobieren. Das ist eine super Idee. Sieht einfach viel viel besser aus.
Marcus Breuer
Beiträge: 113
Registriert: 31. März 2011 17:19
Wohnort: Aachen
Kontaktdaten:

Beitrag von Marcus Breuer »

Es hat sich so eben erledigt. Ich Holzkopf hatte ein paar Header falsch eingebunden und deswegen funktionierten meine Auswertungen der Dialoge nicht. Habe den Quellcode jetzt relativ sauber:)
Antworten