PDA

View Full Version : custom slots / generated ui



mboeni
12th October 2010, 21:31
Hi all

I have created a Qt application using MSVS 2010 with the Qt Add-In. I created the UI in designer and added two new slots for the QMainWindow (called generat0rXClass in my case). I have created two actions (exit_slot(), addScene2D_slot() ) which trigger the slots. Designer has generated the following code:



QObject::connect(actionExit, SIGNAL(activated()), generat0rXClass, SLOT(exit_slot()));
QObject::connect(actionNew_2D_Scene, SIGNAL(activated()), generat0rXClass, SLOT(addScene2D_slot()));


In my main class (generated by the add-In as well, called "generat0rX"

like so:



void exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();
}


But nothing happens (the click on the corresponding ui element does not trigger the slot). Why is that?

Cheers,
Michael

mboeni
12th October 2010, 22:34
Sniffed around in the logs a bit and found the following errors:


Object::connect: No such slot generat0rX::exit_slot() in E:\workspace\QTProjects\generat0rX\generat0rX\Gene ratedFiles\ui_generat0rx.h:421
Object::connect: (sender name: 'actionExit')
Object::connect: (receiver name: 'generat0rXClass')
Object::connect: No such slot generat0rX::addScene2D_slot() in E:\workspace\QTProjects\generat0rX\generat0rX\Gene ratedFiles\ui_generat0rx.h:422
Object::connect: (sender name: 'actionNew_2D_Scene')
Object::connect: (receiver name: 'generat0rXClass')


Im a bit lost on this one - seems to be a namespace problem with the code (generated by the Add-In)

Anyone?

squidge
12th October 2010, 22:41
Where is the class definition for exit_slot() ?

mboeni
12th October 2010, 22:48
in the generat0rx.h file:


#ifndef GENERAT0RX_H
#define GENERAT0RX_H

#include <QtGui/QMainWindow>
#include "ui_generat0rx.h"

class generat0rX : public QMainWindow
{
Q_OBJECT

public:
generat0rX(QWidget *parent = 0, Qt::WFlags flags = 0);
~generat0rX();

void exit_slot();

private:
Ui::generat0rXClass ui;

};

#endif // GENERAT0RX_H

squidge
12th October 2010, 23:06
There's no slots defined in that header file. You need at least something like "public slots:" for example to show which functions are slots.

Secondly, this:



void exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();
}


Isn't valid C++. This a C function.

mboeni
13th October 2010, 09:14
Using the Designer, I was not really sure what needs to be done where.
So when I try to add the "public slots:" in "generat0rX.h", I get a linker error that states something around

"symbol not found in ui_generat0rX.h"
(I am not at my workstation, can post the exact linker error later). When I remove the "public slots:" statements, the code compiles again.

I have created the two additional slots also in Designer (as part of generat0rXClass) by right-clicking on generat0rXClass and editing "Signals/Slots".

On the "isn't valid c++": What is wrong with it? (btw: I copy/pasted it from a Nokia Qt tutorial ;) )

squidge
13th October 2010, 13:24
Once you have added slots to a header file, you must invoke the MOC to generate the appropriate dependancies. This is typically done by updating the project (In QtCreator I think is called "run qmake" or similar).

Please post a link to the Nokia Qt tutorial.

mboeni
13th October 2010, 15:02
I changed generat0rx.h to this:


#ifndef GENERAT0RX_H
#define GENERAT0RX_H

#include <QtGui/QMainWindow>
#include "ui_generat0rx.h"

class generat0rX : public QMainWindow
{
Q_OBJECT

public:
generat0rX(QWidget *parent = 0, Qt::WFlags flags = 0);
~generat0rX();

//void exit_slot();
//void addScene2D_slot();

public slots:
void exit_slot(void);
void addScene2D_slot(void);


private:
Ui::generat0rXClass ui;

};

#endif // GENERAT0RX_H


which, when compiling gives me this output:


1>------ Build started: Project: generat0rX, Configuration: Debug Win32 ------
1> Moc'ing include\generat0rx.h...
1> moc_generat0rx.cpp
1> generat0rx.cpp
1> main.cpp
1> Generating Code...
1>moc_generat0rx.obj : error LNK2019: unresolved external symbol "public: void __thiscall generat0rX::addScene2D_slot(void)" (?addScene2D_slot@generat0rX@@QAEXXZ) referenced in function "public: virtual int __thiscall generat0rX::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@generat0rX@@UAEHW4Call@QMetaObject@@ HPAPAX@Z)
1>moc_generat0rx.obj : error LNK2019: unresolved external symbol "public: void __thiscall generat0rX::exit_slot(void)" (?exit_slot@generat0rX@@QAEXXZ) referenced in function "public: virtual int __thiscall generat0rX::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@generat0rX@@UAEHW4Call@QMetaObject@@ HPAPAX@Z)
1>E:\workspace\QTProjects\generat0rX\\generat0rX.exe : fatal error LNK1120: 2 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


I pasted the full output so you see what the compiler (MSVS / QT Add-In in my case) does. It says that it does moc things as far as I can tell.

Here is the link to the sample: http://doc.trolltech.com/4.7/qmessagebox.html

Lesiok
14th October 2010, 07:40
Did have any CPP file with methods void generat0rX::exit_slot(void) and void generat0rX::addScene2D_slot(void) definitions ?

squidge
14th October 2010, 07:50
By "not valid C++", I mean this:



void exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();
}


to this:



void generat0rX::exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();
}

mboeni
14th October 2010, 12:38
@Lesiok:

Yes, the methods are defined here:


#include "generat0rx.h"
#include "qmessagebox.h"


generat0rX::generat0rX(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags)
{
ui.setupUi(this);

}

generat0rX::~generat0rX()
{

}


void generat0rX::exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();

//qApp->quit();
//qApp->exit();
}

void generat0rX::addScene2D_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: addScene2D triggered");
msgBox.exec();
}


Any idea whats wrong?

boudie
14th October 2010, 12:53
...
[/CODE]
But nothing happens (the click on the corresponding ui element does not trigger the slot). Why is that?
...


What ui element should trigger the action?
Did you connect the action to that element?
How did you do that?

Note that a slot can also be called without the use of a QAction. So, when you use a QPushButton, you can connect directly to the clicked() signal. No QAction involved.

mboeni
14th October 2010, 17:22
@boudie: Here is what I did:
1. I created a project in MSVS 2010 using the Add-In
2. In Designer, I created my UI
3. I created two actions: "actionExit" and "actionNew_2D_Scene" in the action editor
4. I drag/dropped the two actions to a menubar and to a toolbar
5. Then I added two custom slots (in the designer), named "exit_slot()" and "addScene2D_slot()" to "generat0rXClass" which is my QMainWindow
6. Then I created two connections in the designer, which shows like follows in "ui_generat0rx.h":


QObject::connect(actionExit, SIGNAL(activated()), generat0rXClass, SLOT(exit_slot()));
QObject::connect(actionNew_2D_Scene, SIGNAL(activated()), generat0rXClass, SLOT(addScene2D_slot()));


I assumed that this should work - but I keep getting this error


Object::connect: No such slot generat0rX::exit_slot() in E:\workspace\QTProjects\generat0rX\generat0rX\Gene ratedFiles\ui_generat0rx.h:532
Object::connect: (sender name: 'actionExit')
Object::connect: (receiver name: 'generat0rXClass')


Please note: If I use a standard slot like "close()" of generat0rXClass - it works!

So i must be missing something somewhere :s

mboeni
14th October 2010, 17:54
Ok, I tried it the manual way also: I removed the two custom slots and the connection in designer and did it in code. My class now looks like this:

generat0rx.h


#ifndef GENERAT0RX_H
#define GENERAT0RX_H

#include <QtGui/QMainWindow>
#include "ui_generat0rx.h"

class generat0rX : public QMainWindow, private Ui::generat0rXClass
{
Q_OBJECT

public:
generat0rX(QWidget *parent = 0, Qt::WFlags flags = 0);
~generat0rX();

public slots:
void exit_slot(void);
void addScene2D_slot(void);

private:
Ui::generat0rXClass ui;
};

#endif // GENERAT0RX_H


and generat0rx.cpp


#include "generat0rx.h"
#include "qmessagebox.h"


generat0rX::generat0rX(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags)
{
ui.setupUi(this);
QObject::connect(actionExit, SIGNAL(activated()), this, SLOT(exit_slot()));
QObject::connect(actionNew_2D_Scene, SIGNAL(activated()), this, SLOT(addScene2D_slot()));
}

generat0rX::~generat0rX()
{

}

void generat0rX::exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();

//qApp->quit();
//qApp->exit();
}

void generat0rX::addScene2D_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: addScene2D triggered");
msgBox.exec();
}


Now the above error is gone but I get a runtime exception:


> QtCored4.dll!QObject::connect(const QObject * sender=0x00690000, const char * signal=0x00c5d294, const QObject * receiver=0x0026f5c0, const char * method=0x00c5d270, Qt::ConnectionType type=AutoConnection) Line 2483 + 0x8 bytes C++
generat0rX.exe!generat0rX::generat0rX(QWidget * parent=0x00000000, QFlags<enum Qt::WindowType> flags={...}) Line 8 + 0x31 bytes C++
generat0rX.exe!main(int argc=1, char * * argv=0x0069f9f0) Line 24 + 0x1d bytes C++
generat0rX.exe!WinMain(HINSTANCE__ * instance=0x00980000, HINSTANCE__ * prevInstance=0x00000000, char * __formal=0x00072b9a, int cmdShow=1) Line 131 + 0x12 bytes C++
generat0rX.exe!__tmainCRTStartup() Line 547 + 0x2c bytes C
generat0rX.exe!WinMainCRTStartup() Line 371 C


and the debugger points to this line in "qobject.cpp":


const QMetaObject *smeta = sender->metaObject();


Any ideas?

boudie
15th October 2010, 11:39
Your toolbar probably contains one or more QToolButtons. When such button is clicked, the clicked signal for that button is emitted. To react to the signal, you must have in the constructor of your class something like this:


connect(myButton, SIGNAL(clicked()), this, SLOT(mySlot()));

Then every time the button is clicked, the code in function mySlot() is executed.

In your GeneratorClass.h you have something like:


private slots:
void mySlot();


And.. please read the Qt-docs about signals and slots. It really helps...

Ceelaz
15th October 2010, 11:54
Try the signal "triggered()" of your actions instead of "activated()":

#
QObject::connect(actionExit, SIGNAL(triggered()), this, SLOT(exit_slot()));
#
QObject::connect(actionNew_2D_Scene, SIGNAL(triggered()), this, SLOT(addScene2D_slot()));

mboeni
15th October 2010, 13:35
Hi all

First of all, thanks for the input! Alas, none of it worked.

I tried something very simple and get exceptions too. So I assume the problem is rooted deeper. I have added a simple widget manipulation to generat0rx.cpp:


#include "generat0rx.h"
#include "qmessagebox.h"
#include <QxtLogger>
#include <QxtBasicFileLoggerEngine>

generat0rX::generat0rX(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags)
{
ui.setupUi(this);
//QObject::connect(actionExit, SIGNAL(activated()), this, SLOT(exit_slot()));
//QObject::connect(actionNew_2D_Scene, SIGNAL(activated()), this, SLOT(addScene2D_slot()));

//Start logging
qxtLog->addLoggerEngine("FileLogger", new QxtBasicFileLoggerEngine("log/g0x.log"));
qxtLog->info("Huhu INFO log entry!");


outputTabTextEdit->append("TEST");
}

generat0rX::~generat0rX()
{

}

void generat0rX::exit_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: exit_slot triggered");
msgBox.exec();

//qApp->quit();
//qApp->exit();
}

void generat0rX::addScene2D_slot()
{
QMessageBox msgBox;
msgBox.setText("SLOT: addScene2D triggered");
msgBox.exec();
}


This line was added in the constructor:


outputTabTextEdit->append("TEST");


and even THIS raises an exception. There must be something fundamentally wrong with the way I call ui elements...(I am using multiple inheritance btw). Just to make sure you get the full picture, here is generat0rx.h as well:



#ifndef GENERAT0RX_H
#define GENERAT0RX_H

#include <QtGui/QMainWindow>
#include "ui_generat0rx.h"

class generat0rX : public QMainWindow, private Ui::generat0rXClass
{
Q_OBJECT

public:
generat0rX(QWidget *parent = 0, Qt::WFlags flags = 0);
~generat0rX();

public slots:
void exit_slot(void);
void addScene2D_slot(void);

private:
Ui::generat0rXClass ui;
};
#endif // GENERAT0RX_H


This is getting really frustrating :s

Here is the call stack:


> QtGuid4.dll!QTextEdit::append(const QString & text="TEST") Line 2617 + 0x7 bytes C++
generat0rX.exe!generat0rX::generat0rX(QWidget * parent=0x00000000, QFlags<enum Qt::WindowType> flags={...}) Line 17 + 0x2e bytes C++
generat0rX.exe!main(int argc=1, char * * argv=0x0079fab0) Line 17 + 0x1d bytes C++
generat0rX.exe!WinMain(HINSTANCE__ * instance=0x01040000, HINSTANCE__ * prevInstance=0x00000000, char * __formal=0x000a2b9a, int cmdShow=1) Line 131 + 0x12 bytes C++
generat0rX.exe!__tmainCRTStartup() Line 547 + 0x2c bytes C
generat0rX.exe!WinMainCRTStartup() Line 371 C
kernel32.dll!76e31194()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!775eb495()
ntdll.dll!775eb468()

Lykurg
16th October 2010, 08:18
You are mixing the different approaches of using ui files: See "Using a Designer UI File in Your Application" at the docs.
Use
setupUi(this);instead of
ui.setupUi(this); and rerun qmake.

mboeni
18th October 2010, 15:57
Hi Lykurg

I have read the article you mentioned, but missed the part about setupUI. Changing that as you proposed has solved my main issues. Thanks!

Cheers,
Michael

mboeni
20th October 2010, 14:21
For all other newbies out there, I have put together a little tutorial on how to master the first few steps with Qt4 and MS Visual Studio using the Add-In.

You find the tutorial here: Beginners Tutorial Qt 4.7 with Visual Studio and the Qt Add-In (http://www.michaelboeni.net/?p=355)

Please comment or PM me in the blog if you find any errors.

All the best,
Michael