Slot wird nicht mehr gerufen nach Auslagerung in Klasse

Alles rund um die Programmierung mit Qt
Antworten
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Slot wird nicht mehr gerufen nach Auslagerung in Klasse

Beitrag von joebar »

Hallo,

ich moechte diverse Funktionen in eigene Klassen auslagern. Ich habe eine von QDialog abgeleitete Klasse "testapp", von testapp leite ich eine Klasse "add" ab die spaeter mal gewisse Funktionen haben soll. Die Klasse add hat einen slot "calcualte_add" den ich mit dem Button "add" ueber eine connect Anweisung verbunden habe. Leider wird der Slot nicht gerufen wenn ich den "add" Button druecke. Warum? Ich habe das gleiche nochmal mit "sub" welches nicht in einer eigenen Klasse ist, hier funktionierts. Habe wohl irgendwas mit der Vererbung vergessen...

Hier der code von testapp.cpp

Code: Alles auswählen

#include "testapp.h"

#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QValidator>
#include <QString>
#include <cmath>
#include <iostream>
#include <QMessageBox>

using namespace std;


class add : public testapp{
public:

private slots:
	void calculate_add();
};

void add::calculate_add(){
	qDebug() << "calling method calculate_add";
	c1 -> setText("add");
	// add a and b and display in c
	return;
}




testapp::testapp(QMainWindow *parent) : QMainWindow(parent){
	setupUi(this);

	
	// setup connections
	connect(actionQuit,SIGNAL (triggered()), this, SLOT(slotClose()));
	connect(add,SIGNAL (clicked()), this, SLOT(calculate_add()));
	connect(sub,SIGNAL (clicked()), this, SLOT(calculate_sub()));
	connect(actionA,SIGNAL (triggered()), this, SLOT(page_add()));
	connect(actionSub,SIGNAL (triggered()), this, SLOT(page_sub()));
	
	
}

testapp::~testapp(){
}

void testapp::page_sub(){
stackedWidget -> setCurrentIndex(1);
return;
}

void testapp::page_add(){
stackedWidget -> setCurrentIndex(0);
return;
}



void testapp::calculate_sub(){
	c2 -> setText("sub");
	// sub a and b and dispay in c
	return;
}

void testapp::slotClose(){
	close();
}

Danke,
J.
pumuckl
Beiträge: 23
Registriert: 26. November 2006 22:01

Beitrag von pumuckl »

Poste bitte mal auch die anderen Dateien...
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Alle Files angehangen. Hier zum direkt lesen (UI File nur im Anhang):

main.cpp

Code: Alles auswählen

#include "testapp.h"

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

QApplication a(argc, argv);

testapp w;
w.show();
return a.exec();
}
testapp.h

Code: Alles auswählen

#ifndef TESTAPP_H
#define TESTAPP_H

#include "ui_testapp.h"


class testapp : public QMainWindow, public Ui::testapp{
	Q_OBJECT

public:
	testapp (QMainWindow *parent = 0);
	~testapp();
private slots:
	//void calculate_add();
	void calculate_sub();
	void slotClose();
	void page_sub();
	void page_add();

private:

	
};

#endif 
Dateianhänge
testapp.tar.gz
(5.3 KiB) 164-mal heruntergeladen
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Das halte ich auch fuer richtig so. Als ich noch keine eigene Klasse "add" haben wollte musste es dort sein. Jetzt ist es in der Klassendeklaration der Klasse "add", siehe testapp.cpp.

Sehe ich das falsch? Falls es wirklich da stehen muss wo ich es extra auskommentiert habe, habe ich folgendes Problem: Wenn ich es rein nehme (und auch in der Klassendeklaration von add lasse) bekomme ich folgenden Uebersetzungsfehler:

Code: Alles auswählen

-lXext -lX11 -lm -lQtCore -lz -ldl -lpthread
moc_testapp.o: In function `testapp::qt_metacall(QMetaObject::Call, int, void**)':
moc_testapp.cpp:(.text+0xcc): undefined reference to `testapp::calculate_add()'
collect2: ld returned 1 exit status
make: *** [testapp] Error 1

pumuckl
Beiträge: 23
Registriert: 26. November 2006 22:01

Beitrag von pumuckl »

Also eins vorweg - ich bin ebenfalls recht neu in QT und mach hier mal einen auf Klugscheißer in der Hoffnung, dass sich mein Wissen ein wenig erweitert und ich dir dabei noch helfen kann! (das mit der auskommentierten Zeile war Quark meinerseits - deshalb hab ich den Beitrag auch gleich wieder gelöscht, nach dem ich es selbst hab durchlaufen lassen können)

Ich hab verstanden, was du vorhast und konnte das ganze bei mir auch ohne Probleme kompilieren (QT4.2 mit MSVC Express 2005).

Allerdings deklarierst du eine neue Klasse in der cpp-Datei - das gehört meines Erachtens aber in die Header-Datei. Das Erzeugen einer Instanz/eines Objektes deiner neuen Klasse add gehört dann in die cpp-Datei (Trennung zwischen Deklaration und Implementierung). Da hier der Compiler allerdings nicht meckert, kann man das wohl auch so machen, wie du es vorhast, obwohl ich es auf den ersten Blick verwirrend finde.

Was allerdings auf alle Fälle fehlt, ist die Erzeugung einer Instanz/eines Objektes deiner neuen Klasse add (so wie in der main.cpp w als Objekt der Klasse testapp erzeugt wird). Von daher kann ja nichts passieren, wenn du auf den Button drückst. Und hier kommt dann der Punkt, wo ich dir nicht weiterhelfen kann - ich weiß nicht, an welcher Stelle ich das Objekt erzeugen soll (ich hab es z.B. gleich nach der Zeile testapp w in der main.cpp probiert), aber da sowohl in der main.cpp als auch in der testapp.h keine Deklaration vorhanden ist, meldet der Compiler da einen Fehler.

Was meinst du zu meinem Hirnschmalz?
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Also das mit dem Trennen in .h und .cpp Files ist richtig. Das ist auch das Ziel, ich moechte nur erstmal weitere Fehlerquellen ausschliessen und habe daher alles zusammen gepackt. Lt. meinem schlauen C++ Buch sollte das auch gehen.

Das mit der Instanz klingt sehr gut, stimmt irgendwie habe gar keine erzeugt. Mir ist jetzt aber auch nicht klar wo ich diese erzeugen muss....

Hat dazu jemand eine Idee?

Danke!
J.
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Beitrag von MichaelS »

Hallo,

Code: Alles auswählen

 connect(add,SIGNAL (clicked()), this, SLOT(calculate_add())); 
in dem Konstruktor der Klasse testapp verbindest Du das Signal clicked des Schalters ADD mit dem Slot calculate_add derselben Klasse (this). Die Klasse testapp hat aber keinen Slot calculate_add, denn dieser ist Member der Klasse add! Calculate_sub hingegen funktioniert, weil dieser Slot Member der Klasse testapp ist.

Um das Problem zu lösen, kannst Du den connect entweder in den Konstruktor der abgeleiteten Klasse add verschieben, oder - m.E. deutlich besser - in der Klasse testapp den Slot calculate_add definieren und als virtual kennzeichnen:

Code: Alles auswählen

private slots:
           virtual void calculate_add(); 
Diesen virtuellen Slot kannst Du dann in der abgeleiteten Klasse überschreiben.

Gruß Michael
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Also wenn ich nur den virtual slot einfuege und sonst nichts aendere bekomme ich folgendes Problem bei make:

Code: Alles auswählen

-lXext -lX11 -lm -lQtCore -lz -ldl -lpthread
moc_testapp.o:(.gnu.linkonce.r._ZTV7testapp[vtable for testapp]+0xe4): undefined reference to `testapp::calculate_add()'
collect2: ld returned 1 exit status
make: *** [testapp] Error 1

Wenn ich statt dessen die Zeile nicht einfuege sondern das connect in den Konstruktor von add nehme also so

Code: Alles auswählen

#include "testapp.h"

#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QValidator>
#include <QString>
#include <cmath>
#include <iostream>
#include <QMessageBox>

using namespace std;


class add : public testapp{
public:

private slots:
	void calculate_add();
};

void add::calculate_add(){
	qDebug() << "calling method calculate_add";
	c1 -> setText("add");
	// add a and b and display in c
	return;
}

add::add(){
	connect(add,SIGNAL (clicked()), this, SLOT(calculate_add()));
}

add::~add(){
}

testapp::testapp(QMainWindow *parent) : QMainWindow(parent){
	setupUi(this);

	//add1 bla;
	
	// setup connections
	connect(actionQuit,SIGNAL (triggered()), this, SLOT(slotClose()));
	//connect(add,SIGNAL (clicked()), this, SLOT(calculate_add()));
	connect(sub,SIGNAL (clicked()), this, SLOT(calculate_sub()));
	connect(actionA,SIGNAL (triggered()), this, SLOT(page_add()));
	connect(actionSub,SIGNAL (triggered()), this, SLOT(page_sub()));
	
	
}

testapp::~testapp(){
}

void testapp::page_sub(){
stackedWidget -> setCurrentIndex(1);
return;
}

void testapp::page_add(){
stackedWidget -> setCurrentIndex(0);
return;
}



void testapp::calculate_sub(){
	c2 -> setText("sub");
	// sub a and b and dispay in c
	return;
}

void testapp::slotClose(){
	close();
}
Dann bekomme ich folgende Fehler:

Code: Alles auswählen

ude/QtGui -I/usr/include/QtCore -I/usr/include -I. -I. -I. -o testapp.o testapp.cpp
testapp.cpp:29: error: definition of implicitly-declared ?add::add()?
testapp.cpp: In constructor ?add::add()?:
testapp.cpp:30: error: expected primary-expression before ?,? token
testapp.cpp: At global scope:
testapp.cpp:33: error: definition of implicitly-declared ?virtual add::~add()?
make: *** [testapp.o] Error 1

MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Beitrag von MichaelS »

Also wenn ich nur den virtual slot einfuege und sonst nichts aendere bekomme ich folgendes Problem bei make:
Die Fehlermeldung des Compilers "undefined reference" ist ein deutlicher Hinweis darauf, dass eine Methode zwar deklariert, aber nicht implementiert wurde. Ich vermute, Du hast einfach den Codeteil

Code: Alles auswählen

private slots:
           virtual void calculate_add();
kopiert und die Implemation vergessen:

Code: Alles auswählen

            void testapp::calculate_add()
           {
           }
Wenn ich statt dessen die Zeile nicht einfuege sondern das connect in den Konstruktor von add nehme also so [...]
Hier hast Du jetzt das umgekehrte Problem, denn hier hast Du den Konstruktor implementiert, aber nicht deklariert:

Code: Alles auswählen

class add : public testapp{
     public:
         add();
         ~add() {};
           ...
sollte das Problem beheben.

Gruß Michael
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Also wenn ich die Deklartion von Konstruktor und Deskrutor aufnehme bekomme ich folgendes make Problem:

Code: Alles auswählen

ude/QtGui -I/usr/include/QtCore -I/usr/include -I. -I. -I. -o testapp.o testapp.cpp
testapp.cpp: In constructor ?add::add()?:
testapp.cpp:31: error: expected primary-expression before ?,? token
make: *** [testapp.o] Error 1

Wenn ich testapp::calculate_add() "implementiere", habe ich kein Compile Problem mehr, aber es tut sich auch nichts wenn das Signal gerufen wird. Hier nochmals mein aktueller Code von testapp.cpp

Wenn ich das mit dem virtual richtig verstanden habe schaut das Program zur Laufzeit nach welche calculate_add() es rufen soll (das von testapp oder das von add), da ich kein Objekt der Klasse add erzeugt habe wird er wohl das von testapp rufen. Woher weiss er dass er das von add nehmen soll? Muss ich keine Instanz von add anlegen?

Code: Alles auswählen

#include "testapp.h"

#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QValidator>
#include <QString>
#include <cmath>
#include <iostream>
#include <QMessageBox>

using namespace std;


class add : public testapp{
public:
add();
~add();
private slots:
	void calculate_add();
};

void add::calculate_add(){
	qDebug() << "calling method calculate_add";
	c1 -> setText("add");
	// add a and b and display in c
	return;
}

void testapp::calculate_add(){
}

add::add(){
	//connect(add,SIGNAL (clicked()), this, SLOT(calculate_add()));
}

add::~add(){
}

testapp::testapp(QMainWindow *parent) : QMainWindow(parent){
	setupUi(this);

	//add1 bla;
	
	// setup connections
	connect(actionQuit,SIGNAL (triggered()), this, SLOT(slotClose()));
	connect(add,SIGNAL (clicked()), this, SLOT(calculate_add()));
	connect(sub,SIGNAL (clicked()), this, SLOT(calculate_sub()));
	connect(actionA,SIGNAL (triggered()), this, SLOT(page_add()));
	connect(actionSub,SIGNAL (triggered()), this, SLOT(page_sub()));
	
	
}

testapp::~testapp(){
}

void testapp::page_sub(){
stackedWidget -> setCurrentIndex(1);
return;
}

void testapp::page_add(){
stackedWidget -> setCurrentIndex(0);
return;
}



void testapp::calculate_sub(){
	c2 -> setText("sub");
	// sub a and b and dispay in c
	return;
}

void testapp::slotClose(){
	close();
}

MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Beitrag von MichaelS »

Da es sehr schwierig ist, in dem nur in Auszügen vorhandenen Code die Ursache der Compilermeldungen zu finden, habe ich Dir den korrigierten und funktionierenden Quellcode im Anhang beigefügt.

Dabei habe ich die folgenden Änderungen vorgenommen:

a) Deklaration und Implementierung wurden in h und cpp Dateien getrennt.

b) Die Klasse testapp habe ich um einen virtuellen Slot ergänzt, der in der abgeleiteten Klasse add überschrieben wird.

c) Natürlich muss - wie von Dir korrekt bemerkt - in der main.cpp die Klasse add instanziert, damit der korrekte Slot aufgerufen wird. Auch das habe ich entsprechend geändert.

Ich hoffe, dass hilft Dir weiter.

Gruß Michael
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Hallo Michael,

ja ja geschickt gemacht:

Code: Alles auswählen

add w;
w.show();
Ich plane aber fuer diese Beispiel zur Zeit, drei Klassen:

testapp
add (abgeleitet von testapp)
sub (abgeleitet von testapp)

Wenn ich die Klasse add und sub habe, was mache ich dann im main.cpp?

Hintergrund ist dass ich die Funktionen die auf den einzelnen Seiten durchgefuehrt werden in verschiedenen Klassen und damit auch in verschiedenen Files haben moechte. So hoffe ich dass der Sourcecode uebersichtlicher bleibt.

Danke,
J.
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Beitrag von MichaelS »

Moin,
Ich plane aber fuer diese Beispiel zur Zeit, drei Klassen:

testapp
add (abgeleitet von testapp)
sub (abgeleitet von testapp)
Ich hatte mich schon bei der Anpassung des Codes darüber gewundert, warum Du add von testapp ableitest, mich aber erst einmal nicht weiter darum gekümmert. Für das, was Du erreichen willst, ist der Ansatz m.E. völlig verkehrt, denn testapp ist ein child von QMainWindow. Warum willst Du für die Funktionalität der einzelnen Seiten des StackedWidgets jeweils eine eigene Instanz des Hauptfensters (mit Menüleiste usw.) ableiten??

Ich würde dafür grob folgenden Ansatz wählen:

- Eine Klasse testapp, abgeleitet von QMainWindow mit Menü, Werkezeugleiste, Statuszeile und dem StackedWidget
- eine Klasse myWidget, abgeleitet von QWidget, mit den Eingabefeldern und dem Schalter, sowie einem virtuellen Slot "calculate". Das Signal des Schalters wird im Konstruktor dieser Klasse mit dem virtuellen Slot verbunden.
- eine Klasse add, abgeleitet von myWidget. Im Konstruktor werden die Label und der Schalter korrekt beschriftet. Außerdem wird der virtuelle Slot calculate mit der gewünschten Funktionalität überschrieben.
- eine weitere Klasse sub, ebenfalls abgeleitet von myWidget und - wie bereits die Klasse add - entsprechend angepasst.

Alle genannten Klassen werden in einer eigenen h und cpp Datei deklariert und implementiert.

Die beiden Klassen add und sub werden im Konstruktor der Klasse testapp instanziert:

addWidget=new add(this);
subWidget=new sub(this);

Anschließend werden die beiden Widgets dem StackedWidget mit der Methode addWidget zugefügt.

In der main.cpp braucht dann nur noch testapp instanziert zu werden.

Gruß Michael
Zuletzt geändert von MichaelS am 28. Dezember 2006 19:00, insgesamt 2-mal geändert.
joebar
Beiträge: 177
Registriert: 3. Oktober 2006 02:04

Beitrag von joebar »

Klingt gut... leider auch kompliziert...

Werde es mal probieren und dann ggf. (oder sollte ich sagen sicher) einen neuen Thread aufmachen.

Danke soweit, das schubst mich hoffentlich auf die richtige Bahn.

J.
MichaelS
Beiträge: 240
Registriert: 27. Dezember 2005 12:49

Beitrag von MichaelS »

Klingt gut... leider auch kompliziert...
So kompliziert ist das gar nicht. Ich bin nun 2 Wochen im Urlaub. Aber es sind ja noch andere da, die Dir ggf. hgelfen können.

Gruß Michael
Antworten