Slot erfolgreich connected, wird aber nicht ausgeführt

Du bist neu in der Welt von C++? Dann schau hier herein!
Antworten
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Slot erfolgreich connected, wird aber nicht ausgeführt

Beitrag von dd0815 »

Hallo zusammen,

eigentlich will ich fürs Erste nur ein Log-Fenster haben, dass von mehreren Klassen Meldungen empfangen kann. Das QTextEdit ist in der Klasse "mainWindow". Alle anderen Klassen werden in dieser Klasse instanziiert:

(Konstruktor der Klasse mainWindow)

Code: Alles auswählen

    komm k; // Klasse für die UDP-Kommunikation
    connect(&k, SIGNAL(log(const QString&)), this, SLOT(writeLog(const QString&)));
    k.emitLog();
Das funktioniert, die letzte Zeile ruft die "emitLog" Methode von "k" aus , die das Signal "log" abschickt, welches auch durch den Slot "writeLog" der Klasse "MainWindow" korrekt angezeigt wird.

Jetzt möchte ich aber auf Tastendruck (die Taste "lebt" in der Klasse "mainWindow") den Slot "broadcastDatagram" in der Klasse "k" aufrufen:

(immer noch im Konstruktor der Klasse mainWindow)

Code: Alles auswählen

    bool check = connect(startButton, SIGNAL(clicked()), &k, SLOT(broadcastDatagram()));
    if (!check) {textEdit->append("connect nicht erfolgreich");}
    else {textEdit->append("connect erfolgreich");}

    k.broadcastDatagram();
Das connect war erfolgreich, aber beim Tastendruck passiert nichts. Wenn ich jedoch wie in der letzten Codezeile zu sehen ist, den Slot bzw. die Methode direkt aufrufe wird diese korrekt durchlaufen. Und wenn ich die Taste an den Slot "close()" hänge, wird die Applikation auch geschlossen...

Das ich (noch) Verständnisprobleme und (noch) wenig Ahnung vom Software-Strukturierung habe ist klar, aber das sollte doch eigentlich funktionieren? Oder ist doch in der Reihenfolge etwas falsch?

Ist das nicht egal wo der connect-Befehl steht, ob nun beim Objekt 1 oder Objekt 2 oder darüber? (Irgendwie beziehen sich alle gesichteten Beispiele immer auf dieselbe Klasse)

bis dann,
dd0815
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Dein "komm k;" überlebt den Konstruktor nicht. Das muss in den Freispeicher (mit "new").
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Beitrag von dd0815 »

Danke für die schnelle Antwort , jetzt gehts...
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Speicherfreigabe

Beitrag von dd0815 »

Hallo nochmal,

bin grade über das Thema Speicherfreigabe gestolptert. Meine eigene Klasse "k" müßte ich ja nun mittels delete wieder freigeben. Dafür habe ich ein Destruktor der Klasse "mainWindow" angelegt und da "delete k" reingeschrieben. Beim Ausführen bzw. beim Beenden bekomme ich nun die wunderschöne Fehlermeldung "... hat ein Problem festgestellt und muss beendet werden".

Ich vermute, dass ich den bereits vorhandenen Destruktor überschrieben habe und somit die ganzen Qt Widgets nicht mehr richtig gelöscht werden?

Was mich nun wieder zu der Softwarestruktur-Frage bringt. Sinnvoll ist die Hierarchie

mainWindow (alle Widgets)
- Klasse k
- Klasse m..n

nicht wirklich, oder? (ich würde mich gern über das Thema belesen...)

->dd0815
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Kannst du mal Klassendefinition und Konstruktordefinition zeigen? Ich nehme an, du hast nen Member "komm *k;", deklarierst aber im Konstruktor ein neues Objekt.
Eine bessere Herangehensweise wäre, komm einfach dein mainWindow (also "this") als parent mitzugeben. Ein QObject zerstört automatisch alle children. Wenn jetzt "komm *k" dein mainWindow als parent bekommt, wird k automatisch zerstört, sobald mainWindow zerstört wird.

Das ist eine Eigenart, die QObject mitbringt, und kein C++-Automatismus, sobald du irgend welche parent<->child-Beziehungen hast - nur mal so am Rande ;)
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Beitrag von dd0815 »

Hier die Klassendefinition von mainWindow

Code: Alles auswählen

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QDialog>
#include "komm.h"
#include "datei.h"

QT_BEGIN_NAMESPACE
    class QDialogButtonBox;
    class QLabel;
    class QPushButton;
    class QTextEdit;
QT_END_NAMESPACE

class mainWindow : public QDialog
{
    Q_OBJECT

    public:
        mainWindow(QWidget *parent = 0);
        // ~mainWindow();

    private:
        QLabel *statusLabel;
        QPushButton *startButton;
        QPushButton *quitButton;
        QDialogButtonBox *buttonBox;
        QTextEdit *textEdit;
        komm *k;
        datei *d;

    public slots:
        void writeLog(const QString&);

};

#endif // MAINWINDOW_H

(Konstruktordefinition)

Code: Alles auswählen

mainWindow::mainWindow(QWidget *parent)
    : QDialog(parent)
{...}
Dass Qt dieses Autodelete mitbringt ist klar, unklar war nur das Verfahren bzw. das Vermischen von Klassen die vom QObject abgeleitet waren mit eigenen Klassen.

Ein Member komm *k habe ich. Wie ich Deiner Frage entnehmen kann brauche ich dann kein neues Objekt? (Du merkst, da fehlen wirklich die Basics - komme ja auch aus der Automatisierungstechnik :-)). Aber ohne das neue Objekt ist zwar Kompilieren möglich, aber beim Start stürzt das Programm gleich ab.


dd0815
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Bei deinem Konstruktor hätte mich eben genau das zwischen den Klammern interessiert :P (das gehört ja eben zur Definition)

Code: Alles auswählen

class mainWin : public QDialog
{
    Q_OBJECT
    komm *k;
/// usw.
};

mainWin::mainWin(QWidget* parent) 
 : QObject(parent) 
{
    komm *k = new komm(this);  // 1)
    k = new komm(this);        // 2)
}
1) k ist ein NEUES Objekt, hat nix mit deinem Member gleichen Namens zu tun. Im Scope des Konstruuktors überdeckt das dort deklarierte k den Member.
Dadurch bleibt natürlich dein Member uninitialisiert! Das führt dazu, dass bei einem "delete k" im Destruktor dein Programm crasht.
2) Das willst du ;) Das deklariert kein neues Objekt sondern initialisiert direkt den Member deines mainWin.

Ich denke du hast das 1) in deinem Konstruktor stehen. Wenn nicht liegt der Fehler wahrscheinlich wo anders.
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Beitrag von dd0815 »

Hier nochmal der Konstruktor komplett :-)

Code: Alles auswählen

mainWindow::mainWindow(QWidget *parent)
    : QDialog(parent)
{

    // ##### UI definieren #####
    setWindowTitle(tr("Test Leitsystem"));
    startButton = new QPushButton(tr("&Start"));
    quitButton = new QPushButton(tr("&Quit"));
    textEdit = new QTextEdit;

    // ##### Klassen definieren & UI-Signale #####
    komm *k = new komm;
    connect(k, SIGNAL(log(const QString&)), this, SLOT(writeLog(const QString&)));
    k->emitLog();

    QVBoxLayout *mainLayout = new QVBoxLayout;
    // mainLayout->addWidget(buttonBox);
    mainLayout->addWidget(startButton);
    mainLayout->addWidget(quitButton);
    mainLayout->addWidget(textEdit);
    setLayout(mainLayout);

    //bool check = connect(startButton, SIGNAL(clicked()),
    //                        this, SLOT(writeLog(const QString&)));
    bool check = connect(startButton, SIGNAL(clicked()), k, SLOT(broadcastDatagram()));
    if (!check) {textEdit->append("connect nicht erfolgreich");}
    else {textEdit->append("connect erfolgreich");}

    // k->broadcastDatagram();

    // connect(startButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));

    // ##### UI Layout #####
    /*buttonBox = new QDialogButtonBox;
    buttonBox->addButton(startButton, QDialogButtonBox::ApplyRole);
    buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
*/

    datei *d = new datei;
    d->schreiben();

}
Hast richtig vermutet das ich Variante 1 drin hatte...

Ich werde jetzt mal pausieren bis morgen und Variante 2 umsetzen. Besten Dank schonmal für Deine Hilfe, ohne die ich noch endlos weiter rumstochern würde ohne weiterzukommen (Ist schon frustrierend nicht zu wissen wie man anfangen soll zu lernen, denn auf solche Anfänger-Fragen / -Fehler kann kein Handbuch eingehen).

Einen schönen Abend noch,
::dd0815::
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Doch noch schnell ...

Beitrag von dd0815 »

Code: Alles auswählen

k = new komm(this);
Funktioniert leider nicht, da die Konstruktoren meiner eigenen Klassen nichts erwarten ("no matching funktion for call komm::komm(mainWindow * const)

Hier mal die "eigene" Klasse Kommunikation:

Code: Alles auswählen

#ifndef KOMM_H
#define KOMM_H

#include <QDialog>
#include <QtNetwork>
// #include <QTimer>

QT_BEGIN_NAMESPACE
class QTimer;
class QUdpSocket;
QT_END_NAMESPACE

class komm : public QDialog
{

    Q_OBJECT

public:
    komm();
    QTimer *timer;
    void emitLog();

public slots:
    void broadcastDatagram();

signals:
    void log( const QString& );

private slots:

    void processPendingDatagrams();

private:
    QUdpSocket *udpSocket;

    int messageNo;

};

#endif // KOMM_H

Und hier dessen Konstruktor:

Code: Alles auswählen

komm::komm()
{
    udpSocket = new QUdpSocket(this);
    udpSocket->bind(44444, QUdpSocket::ShareAddress);
    timer = new QTimer(this);
    messageNo = 1;

    connect(udpSocket, SIGNAL(readyRead()),
            this, SLOT(processPendingDatagrams()));

}
Ich weiß jetzt auch gar nicht, wie ich diese erweitern kann...

::dd0815::
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Ziel

Beitrag von dd0815 »

Ich erzähl einfach mal kurz, was ich überhaupt so insgesamt Schlimmes vorhabe (Ich hoffe Du / Ihr nehmt es nicht übel, wenn ich nicht nur den kleinen Finger, sondern mal die ganze Hand nehme). :?

Ich möchte eine modulare Software erstellen, die auf unterster Ebene die Kommunikation (UDP) abwickelt und die die Messages irgendwie für eine Logik- Ebene bereitstellt (Stichworte Middleware, IPC - mir grausts schon). Diese wiederum ist in Kontakt mit einer Visualisierungsebene bzw. tauscht mit anderen Kommunikationsebenen (z.B. Dateien im XML-Format) oder Datenhaltungsebenen (Dateien, SQL) Daten aus. Die "Ebenen" sollen unabhängig voneinander laufen können und somit einzeln erweiterbar sein. Wie die Ebenen miteinander reden können in welchem Format verschwimmt im meinem geistigen Nebel, weil das schon ganz weit von meinem jetztigen Kenntnisstand ist.

Also, falls Ihr Wissensträger mal einige Schlagworte fallen lassen könntet, wäre ich dankbar, ansonsten vergeßt mal diese Posting...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Doch noch schnell ...

Beitrag von franzf »

dd0815 hat geschrieben:

Code: Alles auswählen

k = new komm(this);
Funktioniert leider nicht, da die Konstruktoren meiner eigenen Klassen nichts erwarten ("no matching funktion for call komm::komm(mainWindow * const)
Jut :P
Dann musst du dir den so schreiben (funktioniert genauso wie bei mainWindow), oder du lässt den Parameter weg und kümmerst dich selber ums Zerstören. Heißt ein "delete k" im Destruktor von mainWindow.
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Beitrag von dd0815 »

Habe den Konstruktor der Klasse "komm" nun so erweitert:

(Deklaration)

Code: Alles auswählen

komm(QWidget *parent = 0);
(in .cpp)

Code: Alles auswählen

komm::komm(QWidget *parent)
Ich benutze Qt Creator und der färbt in der .cpp das parent in "komm::komm(QWidget *parent)" grau, was bedeutet das? Laut Einstellungen vom Qt Creator TextEditor "nicht verwendet". Er kompilierts und es geht, aber kann ich nun sicher sein, dass die mit
"k = new komm(this);" initialisierte Instanz automatisch deleted wird? (In der mainWindow Klasse ist er nicht grau)

Noch einmal die Frage: Wenn ich nun in der mainWindow-Klasse den Destruktor selbst baue, wird dann nicht der von Qt "automatisch" generierte Destruktor überschrieben und somit bleiben alle Widgets im Freispeicher hängen?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

wenn parent grau ist wird die Variable nicht verwendet. Mann muss sie schon dem QObject/QWidget/Basisklassen ctor weitergeben...
MfG Christian

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

Beitrag von franzf »

dd0815 hat geschrieben:Ich benutze Qt Creator und der färbt in der .cpp das parent in "komm::komm(QWidget *parent)" grau, was bedeutet das? Laut Einstellungen vom Qt Creator TextEditor "nicht verwendet". Er kompilierts und es geht, aber kann ich nun sicher sein, dass die mit
"k = new komm(this);" initialisierte Instanz automatisch deleted wird? (In der mainWindow Klasse ist er nicht grau)
Wenn du wie im mainWindow den Basisinitialisierer verwendest, sollte parent verwendet sein. Ansonsten hängst du dich auch nicht an das parent und wirst nicht automatisch zerstört.

Code: Alles auswählen

komm::komm(QWidget* parent=0)
 : QDialog(parent)   // Basisinitialisierer
{
    ///
}
Noch einmal die Frage: Wenn ich nun in der mainWindow-Klasse den Destruktor selbst baue, wird dann nicht der von Qt "automatisch" generierte Destruktor überschrieben und somit bleiben alle Widgets im Freispeicher hängen?
Der "automatisch generierte" Destruktor macht einfach gar nix ;) Deshalb ist es auch kein Problem, den selber zu implementieren. Aber nur wenn du auch was eigenes machen willst/musst, wenn das Objekt zerstört wird, ansonsten bringts nix.
Destruktoren werden in der Vererbungshierarchie beim Zerstören eines Objektes übrigens automatisch rückwärts aufgerufen:

Code: Alles auswählen

komm *k = new komm;
delete k;
delete komm ruft zuerst komm::~komm auf. Dann automatisch QDialog::~QDialog, QWidget::~QWidget, QObject::~QObject. Zum Schluss wird der Speicher, den das Objekt "k" belegt hat, freigegeben. Wäre ja ein Witz, wenn ein eigener Destruktor das Speichermanagement durcheinanderhauen würde :P
dd0815
Beiträge: 37
Registriert: 8. Juni 2010 16:28

Beitrag von dd0815 »

@franzf: Ich danke vielmals für Deine Ausdauer, jetzt gehts und fängt an Spaß zu machen :-)
Antworten