Seite 1 von 1

QDialog schließt nicht richtig bei done()

Verfasst: 10. Dezember 2009 14:03
von suamikim
Hallo zusammen,

ich habe hier eine von QDialog abgeleitete Klasse, die lediglich 2 Progressbars, 2 Labels und eine ButtonBox beinhaltet.

Der Dialog ist modal, wird also folgendermaßen aufgerufen:

Code: Alles auswählen

FtpUpdateFileDialog updateDlg(this);
int result = updateDlg.exec();
Das Cancel-Signal der ButtonBox ist mit dem reject-Slot des QDialogs verbunden:

Code: Alles auswählen

connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
Wird der Dialog mittels Cancel-Button beendet funktioniert auch alles wie es soll (result = 0).

Nun aber zum Problem:

Im Kontruktor wird eine Funktion aufgerufen, die ein paar Dinge erledigt (Ftp upload oder download, ...).
Diese Funktion kann relativ schnell fehlschlagen (wenn zB. eine Datei nicht vorhanden ist) und soll dann sofort mit einem dem Fehler entsprechenden Return-Code zurückkehren:

Code: Alles auswählen

FtpUpdateFileDialog::FtpUpdateFileDialog(QWidget *parent) : QDialog(parent, Qt::CustomizeWindowHint), m_ui(new Ui::FtpUpdateFileDialog)
{
  // Parameter initialisieren, ...

  m_ui->setupUi(this);

  connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));

  doSomething();
}

void FtpUpdateFileDialog::doSomething()
{
  // Prüfe irgendwas, mache irgendwas
  if (bError) // Zuvor ist irgendwo ein Fehler aufgetreten
  {
    done(iErrorNo); // Dialog beenden und irgendeine Fehlernummer zurückgeben
  }
}
Dies war mein 1. Versuch. Hierbei ist der Dialog obwohl er ins if (bError) reingelaufen ist einfach stehengeblieben, als wäre nichts gewesen und wurde erst korrekt beendet, wenn ich auf den Cancel-Button gedrückt habe.

Mein 2. Versuch war nun, die doSomething()-Funktion erst aufzurufen, wenn der Dialog bereits angezeigt wird (kann ja sein, dass das done() erst zurückkehrt, wenn der Dialog wirklich sichtbar ist).
Also hab ich das showEvent überschrieben und die Funktion von hier aus aufgerufen:

Code: Alles auswählen

void FtpUpdateFileDialog::showEvent(QShowEvent *e)
{
  QDialog::showEvent(e);

  doSomething(); // Ist natürlich im Konstruktor nun nicht mehr vorhanden
}
Das Ergebnis ist auch nicht so fein. Wenn nun ein Fehler auftritt und done(iError) in doSomething aufgerufen wird erscheint mein Fenster zwar, allerdings nur "halb" gezeichnet (nur der Rahmen ist schemenhaft erkennbar), außerdem schließt es sich nicht mehr und ich kann es auch nicht mehr selbst schließen, da der Cancel-Button ja noch nicht gezeichnet wurde und der Dialog eigentlich schon geschlossen wurde...
Die Hauptanwendung ist auch nicht mehr bedienbar, weil sie ja aufs tatsächliche Ende des Dialogs wartet...

Schätze mal, dass kommt daher, weil das showEvent nicht Ordnungsgemäß zurückkehrt, sondern mit done(iError) quasi abgestochen wird?

Die Frage wäre jetzt natürlich wie ich es hinbekomme, dass sich der Dialog im Falle eines Fehlers sofort Ordnungsgemäß wieder schließt und der Hauptanwendung den entsprechenden Fehlercode zurückliefert?

danke, mfg

Verfasst: 10. Dezember 2009 14:45
von archer
Man könnte es auch anders machen...

Code: Alles auswählen

pDialog = new myDialog;
if (pDialog->doSomthing() == noErroor)
   pDialog->exec();
delete pDialog ;
Quasi, das Prüfen nicht im Konstrukor zu machen.

Verfasst: 10. Dezember 2009 14:52
von suamikim
Führt mich auch nicht wirklich zum Ziel. Wenn kein Fehler in doSomething auftritt wird ein eigener Thread gestartet, dessen Abarbeitung ich abwarten muss, bevor ich den Dialog schließen kann.

Das fertigwerden des Threads kriegt aber nur mein Dialog über Singnals des Threads mit, dh. nur weil doSomething fertig ist, heißt das nicht automatisch, dass der Dialog fertig ist...

Verfasst: 10. Dezember 2009 15:20
von archer
Das zeigt doch mein Code: Wenn kein Fehler, dann exec().
Dann wird über den cancel-Button beendet.

Wenn Fehler, brauch ich den Thread nicht starten, brauche den Dialog nicht anzeigen, also kann ich ihn löschen!

Schätze mal dein Problem liegt in der Threads!

Verfasst: 10. Dezember 2009 15:32
von suamikim
Ah, sorry, da hab ich über dein Post zu schnell drübergelesen...

So richtig gefallen will mir die Lösung aber trotzdem nicht. Ich würde gerne wissen, warum mein Dialog beim done() nicht richtig schließt...

Mit den Threads hats jedenfalls definitiv nichts zu tun, da die bei meinen 1. Versuchen noch gar nicht vorhanden waren.

Die hab ich erst in der Zwischenzeit aufgenommen und bin dabei zu folgender zusätzlicher Information gekommen:

Wenn mein Thread fertig wird, soll sich der Dialog auch wieder automatisch schließen:
-> Signal von Thread kommt in meinem Dialog-Slot an
-> done(iResult)

Hier funktioniert das done nun genauso, wie es soll.

Kann es sein, dass ich das done() bei meinen 1. versuchen (also wenn in doSomething() bereits ein Fehler auftritt) einfach zu früh nach dem erzeugen des Dialogs aufrufe und es deshalb nicht das macht, was es eigentlich machen sollte?

danke, mfg

Verfasst: 10. Dezember 2009 15:48
von suamikim
@archer:
Ich hab deine Version jetzt doch mal versucht und damit funktioniert auch alles so, wie es soll. Danke dafür mal!

Mich würde trotzdem noch interessieren, wie man das sonst noch machen könnte und warum meine 1. Versuche nun genau fehlschlugen.

Wenn also jemand Ideen dazu hat: Nur her damit!

danke, mfg

Verfasst: 10. Dezember 2009 15:53
von archer
Das kann ich leider nicht beantworten, aber würde mal sagen jain.
Ich finde es allerdings unschön ein Objekt zu löschen, bevor es volständig angelegt ist.

Verfasst: 11. Dezember 2009 09:33
von RHBaum
Mich würde trotzdem noch interessieren, wie man das sonst noch machen könnte und warum meine 1. Versuche nun genau fehlschlugen.
das aufrufen des slots aus dem Kontext des Konstruktors halt ich eh fuer gefaehrlich.
Hochstwahrscheinlich benutzt done() die eventloop, um alle anderen vom schliessen des dialogs zu unterrichten. Wahrscheinlich existiert im konstruktor die eventloop zwar scho, nur laufen tut sie nicht ....

Besser: wie du es scho machst, mit threads .... oder mit timer.
Das doofe an der qt: es gibt keine OnInit,oder OnIdle ..... also keine funktion / ein Event , was aufgerufen wird nachdem der dialog komplett gezeichnet wurde und die eventloop laeuft.

Das polish event funktioniert irgendwie auch ned immer.
Also macht man oft kruecken mit QTimer::singleshot etc um ein OnInit zu simulieren, oder man faengt wirklich das windows event (WM_INITDIALOG) ab ... dann wars das natuerlich mit der Plattformunanbhaengigkeit.

Ciao ...

Verfasst: 11. Dezember 2009 10:08
von suamikim
Ok, so ähnlich hab ich mir das schon gedacht.

Ich werd das ganze wohl ändern, so dass mein doSomething erst nach Triggerung eines Single-Shot-Timers gestartet wird.

Wo startet man den Timer idealerweise? Im Konstruktor oder im Show-Event?
Und welche Zeit wählt man hier für die Verzögerung? Einfach testen und drauf verlassen, dass das eh immer funktioniert (ist wohl Blödsinn, weil das "starten" ja nicht immer gleich lange dauern kann. Rechnerkonfiguration, laufende Prozesse, ...)?

Nichts gegen die oben genannte Lösung, aber mir gefällt dabei nicht, dass ich beim Aufruf des Dialogs schon aufpassen muss. Ich werde diesen Dialog wohl in einigen anderen Anwendungen auch noch übernehmen und hätte gerne, dass der Aufruf "ganz normal" funktioniert und sich die Dialog-Klasse selbst um das "Problem" kümmert.

danke, mfg