Seite 1 von 1
Programmende erzwingen
Verfasst: 13. August 2012 17:48
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?
Re: Programmende erzwingen
Verfasst: 13. August 2012 18:56
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!
Re: Programmende erzwingen
Verfasst: 13. August 2012 19:16
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?
Re: Programmende erzwingen
Verfasst: 13. August 2012 19:55
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.ä.
}
}
Re: Programmende erzwingen
Verfasst: 13. August 2012 21:34
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!
Re: Programmende erzwingen
Verfasst: 13. August 2012 22:06
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
Re: Programmende erzwingen
Verfasst: 14. August 2012 06:25
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.
Re: Programmende erzwingen
Verfasst: 14. August 2012 12:20
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.
Re: Programmende erzwingen
Verfasst: 14. August 2012 14:11
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
Re: Programmende erzwingen
Verfasst: 14. August 2012 15:49
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 {
...
}
Re: Programmende erzwingen
Verfasst: 15. August 2012 22:28
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();
}