PDA

View Full Version : slot control of new function



tommy
10th November 2007, 21:21
Hi guys,

I learned from danbetz to create new functions and started experimenting with how to turn on the new function using signals/slots. I wrote a code (see below) that displays a button and when the button is clicked something should be drawn above the button. The code compiles fine but no drawing appears when I click the button. Does anyone know why? I'm hugely thankful, as always.
I suspect that the setPixmap(pm) part is the problem, or the picture is not refreshed for some reason, or maybe the slot is declared incorrectly.

////////////////////////////The *.h file//////////////////////////////////////
#ifndef TOM_H
#define TOM_H

#include <QObject>
#include <QPixmap>

class QObject;
class QPixmap;

class MySlot : public QObject
{
Q_OBJECT

public:
MySlot (QObject *parent = 0);

public slots:
QPixmap testDraw();
};
#endif

///////////////////The main file///////////////////////////////////
#include <QtGui>
#include "tom.h"

MySlot::MySlot(QObject *parent):QObject(parent)
{}

QPixmap MySlot::testDraw()
{
QPixmap pm(100,100);
pm.fill();
QPainter p(&pm);
QPen pen(Qt::black, 10);
p.setPen(pen);
p.drawPoint(40,40);
return pm;
}

int main(int argc, char *argv[])
{
QApplication application(argc, argv);
QWidget window;
MySlot myslot;
QVBoxLayout* mainLayout = new QVBoxLayout(&window);
QPushButton* button1 = new QPushButton("Draw");
QLabel* pictureLabel = new QLabel;

QPixmap pm;
QObject::connect(button1, SIGNAL(clicked()), &myslot, SLOT(pm=testDraw()));
pictureLabel->setPixmap(pm);

mainLayout->addWidget(pictureLabel);
mainLayout->addWidget(button1);
window.show();
return application.exec();
}

momesana
11th November 2007, 00:54
Your code can't work, because the slot does not work on the pixmap it should. It creates a pixmap and returns it to nowhere. Slots should have void as return value. You should do your drawing in a QWidget derived class. Below I have put some code that do what you intended to do. I modified it a little to make it place the rectangle at a new random place every time you click on the draw button.



#include <QApplication>
#include <QtGui>

class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget (QWidget* parent = 0);

public slots:
void testDraw();
private:
QLabel* m_pixmapLabel;
QPushButton* m_drawButton;
};

MyWidget::MyWidget(QWidget* parent)
: QWidget(parent)
{
setWindowTitle(tr("Drawing on pixmaps"));

m_pixmapLabel = new QLabel;
m_drawButton = new QPushButton("&Draw");
connect(m_drawButton, SIGNAL(clicked()), this, SLOT(testDraw()));

// Layout
QVBoxLayout* mainLayout = new QVBoxLayout;
mainLayout->addWidget(m_pixmapLabel);
mainLayout->addWidget(m_drawButton);
setLayout(mainLayout);

// to adjust window size
testDraw();
}

void MyWidget::testDraw()
{
QPixmap pm(400, 300);
pm.fill();
QPainter p(&pm);
QPen pen(Qt::black, 10);
p.setPen(pen);
p.drawPoint(qrand()%390, qrand()%290);
m_pixmapLabel->setPixmap(pm);
}

#include "main.moc"
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
MyWidget window;
window.show();
return application.exec();
}

tommy
11th November 2007, 03:11
Momesana, this is truly amazing. Thank you so much!!
I had to remove #include "main.moc" in order to make this
code to compile but it works like charm.
What does #include "main.moc" do anyway?

momesana
11th November 2007, 03:58
You include main.moc from main.cpp if you have QObject based classes in it. You could have pasted the entire code into a file main.cpp and compiled the code without other files. This is helpful for testing since you don't need to mess around with many different files for small testing apps.
#include "main.moc" tells the moc compiler that it should create the corresponding classes for the QObject derived classes in main.cpp.

By the way, you could have made your example work without the new QWidget class by passing a pointer to the pixmap that is set in the QLabel to MySlot. The problem is that QLabel::pixmap() returns a pointer to a constant QPixmap but we need a non-const pointer. We also can't pass a pointer to the pixmap we originally created since it is not identical with the QLabels pixmal after we have called QLabel::setPixmap() (setPixmap() takes its arguments by value so the original pixmap is actually copied).

We can overcome this problem by using const_cast. This is an ugly hack. I did it for fun and you can play around with the code but it is probably not even safe because of the const_cast. Something like const_cast should always be viewed with suspicion. The general idea with this code is to use a handler class that has a pointer to the object that needs to be handled.




#include <QApplication>
#include <QtGui>

class QObject;
class QPixmap;

class MySlot : public QObject
{
Q_OBJECT

public:
MySlot (QPixmap* pixmap, QObject *parent = 0);

public slots:
void testDraw();
private:
QPixmap* m_pixmap;
};


MySlot::MySlot(QPixmap* pixmap, QObject *parent)
: QObject(parent),
m_pixmap(pixmap)
{
testDraw();
}

void MySlot::testDraw()
{
m_pixmap->fill();
QPainter p(m_pixmap);
QPen pen(Qt::black, 10);
p.setPen(pen);
p.drawPoint(qrand()%390, qrand()%290);
}

#include "main.moc"
int main(int argc, char *argv[])
{
QApplication application(argc, argv);
QWidget window;
QVBoxLayout* mainLayout = new QVBoxLayout(&window);
QPushButton* button1 = new QPushButton("Draw");
QLabel* pictureLabel = new QLabel;

QPixmap pm(400,300);
pictureLabel->setPixmap(pm);
MySlot myslot(const_cast<QPixmap*>(pictureLabel->pixmap()));
QObject::connect(button1, SIGNAL(clicked()), &myslot, SLOT(testDraw()));
QObject::connect(button1, SIGNAL(clicked()), pictureLabel, SLOT(update()));

mainLayout->addWidget(pictureLabel);
mainLayout->addWidget(button1);
window.show();
return application.exec();
}