Programmende erzwingen

Alles rund um die Programmierung mit Qt
Antworten
schoettner
Beiträge: 15
Registriert: 13. August 2012 17:12

Programmende erzwingen

Beitrag von schoettner »

Hallo zusammen,

ich habe mich vor einigen Tagen nach C++ Guis umgesehen und mich für Qt entschieden. Ich habe mehrere kleine Testprojekte erstellt und schon einige Fehler produziert und behoben ^^ Jetzt habe ich aber ein Problem, das ich nicht nachvollziehen kann, wie es zustande kommt.

Zuerst zum Projekt:
Ich möchte eine Datenbankanwendung programmieren, mit der ich Objekte anzeigen, manipulieren, hinzufügen und ändern kann. Ich habe mich für MySQL als RDBMS entschieden (hat nur knapp einen Tag gebraucht bis das Plugin endlich erstellt wurde...). Ich habe einen Dialog geschrieben, der die Verbindung aufruft und ein Hauptfenster das dann die ersten Daten lädt. Das ganze funktioniert auch wunderbar :)

Jetzt zum Problem:
Ich möchte den Dialog aufrufen, bevor das Hauptfenster angezeigt wird. Nur bei erfolgreichem Login soll das Hauptfenster angezeigt werden, sonst soll nur eine Fehlermeldung ausgegeben werden. Sollte der Benutzer den Dialog aber schließen, soll die ganze Anwendung beendet werden. Und hier ist das Problem. Ich lasse auf meinem Dialog mit emit ein Signal ausführen das dem Hauptfenster sagen soll, das die Anwendung beendet werden soll.

Code: Alles auswählen

connect (loginDialog, SIGNAL(close_app()), this, SLOT(close_application()));
...
void MainWindow::close_application()
{
    QMessageBox::critical(0, QObject::tr("Application"),"No connection was made");
    QApplication::exit();
}
Mit der MessageBox prüfe ich, ob der Slot auch ordnungsgemäß aufgerufen wird. Auch das geschieht einwandfrei. Das einzige was nicht passiert ist, dass der Prozess beendet wird! Wenn der Benutzer den Dialog schließt, bleibt das Hauptfenster zwar versteckt aber wird nicht geschlossen. Nur über den Taskmanager kann das Programm beendet werden.

Ich habe schon mehrere Möglichkeiten anstatt QApplication::exit() probiert. Alle ohne erfolg :( Kann mir bitte jemand weiter helfen?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Programmende erzwingen

Beitrag von Christian81 »

Wenn die QApplication-Eventloop noch nicht läuft dann geht auch kein QApplication::exit(). Das können wir aber nicht sehen da zu wenig Code. Deshalb: Minimales kompilierbares Beispiel bitte!
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
schoettner
Beiträge: 15
Registriert: 13. August 2012 17:12

Re: Programmende erzwingen

Beitrag von schoettner »

Ok sry :) Hab das mit den Slots auch nochmal geändert. kompilierbares beispiel wird wegen dem mysql wohl schwierig ;)

hier die main

Code: Alles auswählen

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    //w.show();
    return a.exec();
}
mainwindow.cpp

Code: Alles auswählen

//constr
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    login(); //logindialog beim erstellen der form aufrufen. die form ist dabei noch unsichtbar
}

bool MainWindow::login()
{
    loginDialog = new LoginDialog(this);  //dialog erzeugen
    connect (loginDialog, //nur wenn die verbindung funktioniert hat wird der dialog ordnungsgemäß beendet, username und pwd werden als parameter von signal zu slot übergeben
             SIGNAL(connection_success(const QString&,const QString&)),
             this,
             SLOT(connect_db(const QString&,const QString&)));

    connect (loginDialog, SIGNAL(close_app()), this, SLOT(close_application()));

    if(!loginDialog->exec()) //dialog ausführen und danach aus dem speicher löschen
    {
        QMessageBox::critical(0, QObject::tr("Application"),"anwendung hier beenden");
        QApplication::exit();
        return false;
    }

    delete loginDialog;
    return true;
login wird also über den konstruktor aufgerufen, weil ich nicht erst auf irgendeinen button klicken will. er soll ja zuerst den dialog anzeigen und nicht das Fenster. Denke da wird das Problem liegen, oder?
ObiWanKenobe
Beiträge: 4
Registriert: 13. August 2012 12:36

Re: Programmende erzwingen

Beitrag von ObiWanKenobe »

Wie wär's mit

Code: Alles auswählen

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    loginDialog = new LoginDialog();
    if (loginDialog->isLoginOk())
    {
        MainWindow w;
        w.show();
        delete loginDialog;
        return a.exec();
    }
    else
    {
        // Fehlermeldung, o.ä.
    }
}
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Programmende erzwingen

Beitrag von franzf »

@schoettner:
Geh deinen Code Schritt für Schritt durch, starte in main.
Die EventLoop gibt es erst bei a.exec().
Und dann sag uns, ob an der fraglichen Stelle tatsächlich schon die Main-EventLoop läuft!
schoettner
Beiträge: 15
Registriert: 13. August 2012 17:12

Re: Programmende erzwingen

Beitrag von schoettner »

Ich bin nicht sicher ob die Eventloop läuft. Der Dialog wird ja im Konstruktor der mainwindow ausgeführt. Dieses wird ja eigentlich vor der Eventloop erstellt. Ganz soviel Code ist es ja noch nicht. Das meiste ist ja nur Layout

main.cpp

Code: Alles auswählen

#include <QtGui/QApplication>
//#include <QtGui>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    //w.show();
    return a.exec();
}
mainwindow.h

Code: Alles auswählen

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

//declare classes so pointer on this classes are allowed
class LoginDialog;
class QSqlQueryModel;

namespace Ui
{
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    
private:
    Ui::MainWindow *ui;  //mainwindow surface
    LoginDialog *loginDialog;   //pointervar
    QSqlQueryModel * qmodel;    //pointervar
    bool update_table(const QString&, const QString&);  //function which loads the data from the database

private slots:
    bool login();   //function which calls connect_db (startet in constructor)
    void connect_db(const QString& user, const QString& pwd);


};

#endif // MAINWINDOW_H
mainwindow.cpp

Code: Alles auswählen

#include <QtGui/QMessageBox>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logindialog.h"
#include <QtSql>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    login(); //logindialog beim erstellen der form aufrufen. die form ist dabei noch unsichtbar
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::login()
{
    loginDialog = new LoginDialog(this);  //dialog erzeugen
    connect (loginDialog, //nur wenn die verbindung funktioniert hat wird der dialog ordnungsgemäß beendet, username und pwd werden als parameter von signal zu slot übergeben
             SIGNAL(connection_success(const QString&,const QString&)),
             this,
             SLOT(connect_db(const QString&,const QString&)));

    connect (loginDialog, SIGNAL(close_app()), this, SLOT(close_application()));

    if(!loginDialog->exec()) //dialog ausführen und danach aus dem speicher löschen
    {
        QMessageBox::critical(0, QObject::tr("Application"),"anw beenden");
        QApplication::exit();
        return false;
    }

    delete loginDialog;
    return true;
}

void MainWindow::connect_db(const QString& user, const QString& pwd)
{
    loginDialog->hide();
    update_table(user,pwd);
    this->show();
}

bool MainWindow::update_table(const QString& user, const QString& pwd)
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("Vanya");
    db.setUserName(user);
    db.setPassword(pwd);
    if (db.open())
    {
        qmodel = new QSqlQueryModel(this);
        qmodel->setQuery("SELECT m.id, m.titel, m.genre, p.firstname, p.secondname, m.lended FROM movie AS m join person AS p ON m.owner=p.id;",db);
        ui->tableView->setModel(qmodel);
        ui->tableView->setColumnHidden(0,true);
        ui->tableView->resizeColumnsToContents();
        return true;
    }
    else
    {
        QMessageBox::critical(0, QObject::tr("Connection Error"),"no connection was made");
        return false;
    }

}
logindialog.h

Code: Alles auswählen

#ifndef LOGINIALOG_H
#define LOGINDIALOG_H

#include <QDialog>

//declare classes so pointers are possible
class QLabel;
class QLineEdit;
class QPushButton;

//Erstellen Loginklasse
class LoginDialog : public QDialog
{
    Q_OBJECT //macro das für dialoge mit eigenen objecten nötig ist

public:
    LoginDialog(QWidget *parent = 0);  //constructor which tells the parent. default is no parent

signals:
    void connection_fail();
    void connection_success(const QString& user, const QString& pwd);

private slots:
    void try_to_connect();
    void on_LineEdit_textchanged();

private:
//elements that are on the form
    QLabel *userlabel;
    QLabel *pwdlabel;
    QLabel *infolabel;
    QLineEdit *userlineEdit;
    QLineEdit *pwdlineEdit;
    QPushButton *connectButton;
    QPushButton *closeButton;
};

#endif
logindialog.cpp

Code: Alles auswählen

#include <QtGui>
#include <QtSql>
#include "logindialog.h"

LoginDialog::LoginDialog(QWidget *parent)
    : QDialog(parent)
{
    //initialize components
    userlabel = new QLabel(tr("&Username"));
    userlineEdit = new QLineEdit;
    userlabel->setBuddy(userlineEdit);
    pwdlabel = new QLabel(tr("&Password"));
    pwdlineEdit = new QLineEdit;
    pwdlineEdit->setEchoMode(QLineEdit::Password);
    pwdlabel->setBuddy(pwdlineEdit);
    infolabel = new QLabel(tr("Please enter your username and password"));
    connectButton = new QPushButton(tr("&Login"));
    connectButton->setEnabled(false);
    connectButton->setDefault(true);
    closeButton = new QPushButton(tr("&Cancel"));

    //connect components
    connect(userlineEdit,SIGNAL(textChanged(QString)),this,SLOT(on_LineEdit_textchanged()));
    connect(connectButton, SIGNAL(clicked()), this, SLOT(try_to_connect()));
    connect(closeButton, SIGNAL(clicked()),this,SLOT(reject()));

    //create Layouts
    QVBoxLayout* topMidLayout = new QVBoxLayout;
    topMidLayout->addWidget(infolabel);
    topMidLayout->addStretch();

    QVBoxLayout* botLeftLayout = new QVBoxLayout;
    botLeftLayout->addWidget(userlineEdit);
    botLeftLayout->addWidget(userlabel);

    QVBoxLayout* midLeftLayout = new QVBoxLayout;
    midLeftLayout->addLayout(botLeftLayout);
    midLeftLayout->addStretch();
    midLeftLayout->addWidget(connectButton);

    QVBoxLayout* botRightLayout = new QVBoxLayout;
    botRightLayout->addWidget(pwdlineEdit);
    botRightLayout->addWidget(pwdlabel);

    QVBoxLayout* midRightLayout = new QVBoxLayout;
    midRightLayout->addLayout(botRightLayout);
    midRightLayout->addStretch();
    midRightLayout->addWidget(closeButton);

    QHBoxLayout* midLayout = new QHBoxLayout;
    midLayout->addLayout(midLeftLayout);
    midLayout->addLayout(midRightLayout);

    QVBoxLayout* mainLayout = new QVBoxLayout;
    mainLayout->addLayout(topMidLayout);
    mainLayout->addLayout(midLayout);

    setLayout(mainLayout);
    setWindowTitle(tr("Login"));
    setFixedHeight(sizeHint().height());
    setFixedWidth(sizeHint().width());
    setTabOrder(userlineEdit, pwdlineEdit);
    setTabOrder(connectButton, closeButton);
}

////////////////////signal code////////////////////////////

void LoginDialog::try_to_connect()
{   //build up d databaseconnection by
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName("localhost");
    db.setDatabaseName("Vanya");
    db.setUserName(userlineEdit->text());
    db.setPassword(pwdlineEdit->text());
    if (!db.open())
    {
        infolabel->setText("<font color='red'>Error connecting to db</font>");
        QMessageBox::critical(0, QObject::tr("Database Error"),db.lastError().text());
        emit connection_fail();
    }
    else
    {
        db.close();
        infolabel->setText("<font color='green'>Database connected successfully</font>");
        QMessageBox::information(this, tr("Database connected"),tr("Database connected successfully"));
        emit connection_success(userlineEdit->text(), pwdlineEdit->text());
        accept();
    }
}

void LoginDialog::on_LineEdit_textchanged()
{
    if (userlineEdit->text() != "")
        connectButton->setEnabled(true);
    else
        connectButton->setEnabled(false);
}
Den Dialog in der main aufrufen habe ich mir auch überlegt. Habe mich aber gefragt ob das überhaupt einen unterschied macht
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Programmende erzwingen

Beitrag von Christian81 »

1. ist das kein minimales Beispiel und 2. hast Du die Frage selbst beantwortet. im ctor von MainWindow läuft noch keine Eventloop - die wird ja erst mit app.exec() gestartet. Des weiteren hat franzf und ObiWanKenobe schon alles zweimal erklärt.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Programmende erzwingen

Beitrag von franzf »

Die einfachste Lösung ist mMn. das ganze eben nicht durch einen Dialog zu lösen, sondern direkt das MainWindow anzuzeigen, allerdings komplett disabled bis auf das centralWidget, das anfangs die Login-Maske enthält. War der Login erfolgreich, wird das eigentliche centralWidget angezeigt und die restliche Gui auf enabled gesetzt. So hast du auch die Möglichkeit, mit dem User zu interagieren, falls der falsche Login-Daten angegeben hat.
schoettner
Beiträge: 15
Registriert: 13. August 2012 17:12

Re: Programmende erzwingen

Beitrag von schoettner »

Ok vielen Dank für die Hilfe. Da ich den Login nochmal an anderen stellen brauche, würde ich das gerne weiterhin über den dialog lösen. Ich hätte aber nochmal eine Frage zum Bespiel von ObiWan

Code: Alles auswählen

#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    loginDialog = new LoginDialog();
    if (loginDialog->isLoginOk())
    {
        MainWindow w;
        w.show();
        delete loginDialog;
        return a.exec();
    }
    else
    {
        // Fehlermeldung, o.ä.
    }
}
Wie löse ich hier die Parameterübergabe vom user und pwd am besten? Das Mainwindow exestiert ja in dem Fall nicht, ich kann also via Signal/Slot kein Parameter an es übergeben
brax
Beiträge: 208
Registriert: 11. Mai 2010 11:22

Re: Programmende erzwingen

Beitrag von brax »

Ich sehe nicht, warum der Wunsch das Login später noch zu benutzen gegen die Lösung von franzf spricht, aber das ist was anderes...

Du könntest Dir die Logindaten im LoginWindow speichern und sie dann dem Konstruktor von MainWindow mitgeben. Nebenbei: ich sehe keinen Grund, warum das LoginWindow per new auf dem Heap erzeugt werden muss.

Code: Alles auswählen

LoginDialog login;
login.exec();
if (login.isLoginOk()) {
  MainWindow w(login.getUserName(), login.getPassword());
  w.show();
  return a.exec();
} else {
 ...
}
schoettner
Beiträge: 15
Registriert: 13. August 2012 17:12

Re: Programmende erzwingen

Beitrag von schoettner »

Vielen dank, ich habe das Problem lösen können :)
Habe die main.cpp wie vorgeschlagen angepasst und jetzt funktioniert es.

Code: Alles auswählen

#include <QtGui/QApplication>
#include "mainwindow.h"
#include "logindialog.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    LoginDialog loginDialog;
    if(!loginDialog.exec())
    {
        return -1;
    }
    MainWindow window(0,loginDialog.getusername(),loginDialog.getpassword());
    window.show();
    return a.exec();
}
Antworten