PDA

View Full Version : Signals Slots and Class Pointers



Atomic_Sheep
17th July 2012, 04:29
Hi guys, this is more of a class pointer and C++ but anyway. I created a window with a widget in Qt and also added a horizontal slider widget to the main window as well. My widget is an OpenGL widget and I want to be able to control an object in the OpenGL window using the slider. I went into the default constructor of the window and added the connect code:


connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), *GLWidget, SLOT(setAngle int angle))

However I'm not really familiar with class pointers in projects with multiple files (very confused as to which files are accessed first, which second when the main window pops up and calls subsequent stuff). My understanding is that to use a class pointer, you need to first create the class pointer and assign a value to it just like any other pointer. However, in my case, I'm trying to send a value from my slider widget to an object which should already exist? Simply writing *GLWidget in the connect function doesn't seem to be able to have the program recognise the existence of my GLWidget - I don't get the auto pop up of the available slots for my GLWidget class i.e.


SLOT(... nothing pops up)

ChrisW67
17th July 2012, 06:48
You are correct, this is a C++ question.

A class is a definition of a thing. An object is one, possibly among many, of those things. A pointer is the address of an object not a class: i.e. "class pointer" makes no sense. The QObject::connect() call requires pointers to sender and receiver objects. You are responsible for creating the object and providing the pointer to it. You can create objects as local variables (on the stack) or on in allocated memory (on the heap):


class Foo {
public:
...
int value;
void doStuff() { }
...
};

void somefunc(const Foo &f) {...}
void otherfunc(Foo *f) {...}

Foo foo; // an object of class Foo on the stack
Foo *bar; // a pointer to an object of class Foo. The object dos not exist yet.
bar = new Foo; // now it does
Foo *baz = &foo; // a pointer to an object of class Foo, pointing at the same object as foo.

// Using the Foo objects
foo.doStuff();
bar->doStuff();

somefunc(*bar); // Supply the object pointed to by bar
otherfunc(bar); // Supply the pointer to an object
otherfunc(&foo); // Supply the pointer to the object foo

delete bar; // free the allocated memory


Objects are created in the order your code creates them, and the do what your code commands of them. Its irrelevant what file contains which class definition: classes don't do anything until you create an object or objects based on them.

In your code ui->horizontalSlider is a pointer to an object of the QSlider class. How have you created your GLWidget object, or is GLWidget the name of the class?

Atomic_Sheep
18th July 2012, 02:07
You are correct, this is a C++ question.

Objects are created in the order your code creates them, and the do what your code commands of them. Its irrelevant what file contains which class definition: classes don't do anything until you create an object or objects based on them.

In your code ui->horizontalSlider is a pointer to an object of the QSlider class. How have you created your GLWidget object, or is GLWidget the name of the class?

Yep I understand. The bit I don't get is that when I look under main.cpp, I can clearly see my main window object being created and called 'w'. After that, I don't see where my GLWidget gets created other than seeing the .ccp code and .h head files for it and know that it works when I run the application after compilation... so it's getting called somewhere, but no idea where... hence the confusion as to what get's called in what order.



How have you created your GLWidget object, or is GLWidget the name of the class?

It's the class itself.

ChrisW67
18th July 2012, 02:24
It's the class itself.
That won't work.

If you have put a widget of class GLWidget into a Designer UI (using the promotion feature) then the code generated from the .ui file (e.g. ui_mainwindow.h) is creating an object of that class during the setupUi() call you see in widget class constructors. A pointer to such an object will be accessible through the ui pointer just like the slider. I suggest you open the ui_mainwindow.h file and read it to get a better understanding.

If you are not using Designer then I'll quote myself:

You are responsible for creating the object and providing the pointer to it. You can create objects as local variables (on the stack) or on in allocated memory (on the heap):

Atomic_Sheep
18th July 2012, 03:22
That won't work.
A pointer to such an object will be accessible through the ui pointer just like the slider.

Of course! Thanks, pretty sure I'll be able to get it to work now...

Atomic_Sheep
18th July 2012, 11:29
Nope, looks I'm too stupid to figure this one out, after spending a few hours on this, I still can't get it to work.

I've got the two classes (mainwindow and glwidget)... in the mainwindow class constructor I've connected:


connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->widget, SLOT(...);

However as the ... indicates... no slots from my glwidget class pop up.

The glwidget and mainwindow are two seperate classes with their own .h and .cpp files and I've definitely added the includes but still no luck.

I added a label to my ui and connected that to the horizontalslider and the label shows the values no problems and also pops up with "setNum(int)" when I'm typing up the connect command...


connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->label, SLOT(setNum(int)));

I've also written the code for my slot in my glwidget.cpp file:


void GLWidget::SetValue(int Value)
{
if (Value != m_Value)
{
m_Value = Value;
}
}

But still no luck.

wysota
18th July 2012, 12:33
The fact that Creator does not recognize your slot, doesn't mean you can't use it. Just type in the slot signature manually. And make sure you really declared the function to be a slot.

Atomic_Sheep
18th July 2012, 13:42
I was wondering whether this might be the case so I tried what you suggested but the implementation of my receiving objects slot doesn't appear to be registering because my opengl's object rotation doesn't appear to be happening. I put a messagebox inside my slot implementation and it doesn't pop up as I move the slider about... the value in the label changes though.

wysota
18th July 2012, 16:32
We'd have to see your actual code as currently with those little pieces you posted it is hard to tell anything. In general you need your method to be declared as a slot and you need to be passing a pointer to an instance of that class to the connect() statement to be able to connect to the slot.

ChrisW67
18th July 2012, 23:43
Also look at the debug output of your program for warnings of the type:


QObject::connect: No such slot GLWidget::SetValue(int)

indicating that the SetValue() function is not declared as a slot. The glwidget.h file should look like:


...
class GLWidget: public ...
{
Q_OBJECT
public:
GLWidget(...);
...
public slots: // <<<< this
void SetValue(int value);
...
};
...

and be included in the PRO file HEADERS variable.

Atomic_Sheep
19th July 2012, 06:10
Looks like HEADERS in pro are fine.

As for the code...

mainwindow.h


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "glwidget.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();

int m_Angle;
void AngleUpdate(int m_Angle);
private:
Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H


glwidget.h


#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QtOpenGL/QGLWidget>
#include <cmath>
#include "mainwindow.h"
#include <QMessageBox>

class GLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit GLWidget(QWidget *parent = 0);

int m_Angle;


void initializeGL();
void paintGL();
void resizeGL(int w, int h);

public slots:
void SetAngle(int Angle);

};

#endif // GLWIDGET_H

mainwindow.cpp


#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->Widget, SLOT(SetAngle(int Angle))); //Widget is just the standard widget which I've promoted to GLWidget in form editor
connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), ui->label, SLOT(setNum(int)));
}

MainWindow::~MainWindow()
{
delete ui;
}



glwidget.cpp



#include "glwidget.h"

GLWidget::GLWidget(QWidget *parent) :
QGLWidget(parent)
{

}

void GLWidget::SetAngle(int Angle)
{
if (Angle != m_Angle)
{
m_Angle = Angle;
QMessageBox::information(0, "inside slot", "inside slot");
}
}


P.S. There might be one or two mistakes here or there e.g. m_Angle used instead of Angle or something like that... I'm 99% sure I don't have that mistake in my actual program.

ChrisW67
19th July 2012, 08:22
In the last few posts the slot has been referred to as SetNum(), SetValue(), and now SetAngle(). I asked you to check for run time error messages like:


QObject::connect: No such slot GLWidget::SetAngle(int Angle)

Line 9 of mainwindow.cpp is probably generating one because the slot signature is incorrect. You do not include the argument names in the signal or slot signatures.

Atomic_Sheep
20th July 2012, 00:21
Forgot to say that yes I did check for those errors, I didn't see any, but I just realised that run-time errors are under "Application Output" and yes it's spitting out the "no such slot" error. i.e. :

Object::connect: No such slot GLWidget::SetAngle(int Angle) in ...\MainWindow.cpp:9
Object::connect: (sender name: 'horizontalSlider')
Object::connect: (receiver name: 'Widget')

...exited with code 0

I used different names for my slots previously because I was simply typing the code up as I was writing up the message. In the latest iteration it was copy paste with some minor editing, but the latest code that I provided should have all the naming issues sorted out.

Atomic_Sheep
3rd September 2012, 05:36
Still unable to get my signals and slots to work :(.

my .cpp files are :main.cpp, button.cpp, mainWindow.cpp and mainWindowWidget.cpp.

mainWindowWidget.cpp has the following signal:


void mainWindowWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton) //left mouse button - need to check this.
{
cMousePositionAtClick = event->pos();
emit MouseClickLocation(cMousePositionAtClick);
//QMessageBox::information(this, "Mouse Click Detected", "Mouse Click Detected");
}
}

I have a slot in my button.h and its implementation.

here's the code in main.cpp:


mainWindow w;
Button button1(0,0,50,50);
connect(w, SIGNAL(MouseClickLocation(QPoint)), button1, SLOT(LocationGrabber(QPoint)));
w.show();

However when I compile it I get the following errors:

main.cpp:11: error: cannot convert 'mainWindow' to 'SOCKET' for argument '1' to 'int connect(SOCKET, const sockaddr*, int)'

ChrisW67
3rd September 2012, 06:04
Two basic C++, not Qt, problems:

The error message tells you that the compiler has found only function 'int connect (http://linux.die.net/man/2/connect)(SOCKET, const sockaddr*, int)' when trying to compile line 11 (I assume line 3 of your posted snippet). The arguments you have given cannot be coerced into the arguments required by that function so it fails. You want QObject::connect() and you cannot access that as an unqualified name outside of the implementation of a QObject sub-class. Use the static QObject::connect() function.

QObject::connect() requires a pointer to an object, i.e. &w and &button1, not the actual objects as you are current trying to do.

Atomic_Sheep
4th September 2012, 07:00
Deleted... figured it out.

Atomic_Sheep
6th September 2012, 12:50
Thanks Chris for helping out with the previous problem.

Another question. In the Qt example with the spinning boxes:


Window::Window()
{
QGridLayout *mainLayout = new QGridLayout;

for (int i = 0; i < NumRows; ++i) {
for (int j = 0; j < NumColumns; ++j) {
QColor clearColor;
clearColor.setHsv(((i * NumColumns) + j) * 255
/ (NumRows * NumColumns - 1),
255, 63);

glWidgets[i][j] = new GLWidget(0, 0);
glWidgets[i][j]->setClearColor(clearColor);
glWidgets[i][j]->rotateBy(+42 * 16, +42 * 16, -21 * 16);
mainLayout->addWidget(glWidgets[i][j], i, j);

connect(glWidgets[i][j], SIGNAL(clicked()),
this, SLOT(setCurrentGlWidget()));
}
}

The code creates glWidget objects for each of the cubes to spin. I just have:


#include "mainwindow.h"
#include "ui_mainwindow.h"

mainwindow::mainwindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::Jetstream41)
{
ui->setupUi(this);
}

mainwindow::~mainwindow()
{
delete ui;
}


and a .cpp file for mainwindowGLWidget.

I'm trying to connect a signal from another class to a slot of my mainwindowGLWidget class, however I don't know the name of the object of my mainwindowGLWidget class. How do I find this out?

My limited understanding is that when the compiler runs, ?moc? generates the ui.h file in the -build-desktop folder? But does this somehow solve my problem?

I know it says this in relation to my glwidget...

widget = new MyOpenGLWidget(centralWidget);

ChrisW67
6th September 2012, 23:00
uic generates the ui_stuff.h file from your stuff.ui file.

The name of the variable holding a pointer to your custom widget is set by you in Designer and carried over into generated UI code. In Designer:
8193
and in the generated code is the class that the ui pointer points to an instance of:


#include <QtGui/QButtonGroup>
#include <QtGui/QFrame>
#include <QtGui/QHBoxLayout>
#include <QtGui/QHeaderView>
#include <QtGui/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Form
{
public:
QHBoxLayout *horizontalLayout;
QFrame *myCustomFrame; // <<<<< here

void setupUi(QWidget *Form)
{
...
// <<<< and here
myCustomFrame = new QFrame(Form);
myCustomFrame->setObjectName(QString::fromUtf8("myCustomFrame"));
myCustomFrame->setFrameShape(QFrame::Box);
...
} // setupUi
...
};
...
#endif // UI_UNTITLED_H

so in your code you can access it through the ui pointer thus:


ui->myCustomFrame;

Atomic_Sheep
7th September 2012, 09:08
Thanks Chris... had to reread this whole thread a few times to pick out to what information I applied the principle of 'selective hearing'. Finally got everything to work as desired! Now comes the fun task of fixing the next problem caused by the solving of the old prob :). Thanks again.