Customize widget
Qt provide list of widgets that we can use with signal and
slot as we used in last blog. We used QWidget as a container in our last
example. We can make combination of Qt widgets, that can be used as a single object
or widget having its own signal and slots . It is possible by inheriting
QWidget class and adding components to it. Here is a complete example with
explanation:
First we have to specify what it will do?
We are going to make a widget which is a dialog box that can
take user's input to add or remove RGB point in a color transfer function of a
volume visualization object. It require following inputs:
1.
Point where user want to add/remove RGB values
2.
Value of R
3.
Value of G
4.
Value of B
5.
User want to add
6. User
want to remove
7.
User don't want any action
Now we have to use appropriate Qt widgets to get these
inputs. So here is the list:
1.
Point where user want to add/remove RGB values:
spinbox that is QSpinBox.
2.
Value of R: Since we want that user can specify
a smallest value 0.1 so we need a spinbox that take double value, so
QDoubleSpinBox.
3.
Value of G: QDoubleSpinBox
4.
Value of B: QDoubleSpinBox
5.
User want to add: an Add button so QPushButton
6.
User want to remove: A Remove button so again
QPushButton
7.
User don't want any action: a cancel button,
QPushButton
These input should be passed by widget to the main
application where these parameters will be used. So we have to make a signal
which will carry all the input information and pass it to slot of main
application that will be connected to it.
We have decided the way of input and output now how widget
should appear?
First we have to decide what other things required to make widget's
presentation more informative to user and second draw a rough layout where we
want to place these all widgets.
We need labels for each spinbox to show which spinbox can be
use to input point/r/g/b. Also some layout objects to position these all widgets
as so that our widget appear as:
Now we have to define class
of our widget. Make a header file with name ColorTFWidget.h
as follow:
#ifndef _ColorTFWidget_H_
#define _ColorTFWidget_H_
#include <QWidget>
class QPushButton;
class QSpinBox;
class QDoubleSpinBox;
class ColorTFWidget : public QWidget
{
Q_OBJECT
public:
ColorTFWidget(QWidget
* parent = 0);
~ColorTFWidget();
private:
QSpinBox
* point ;
QDoubleSpinBox
* r ;
QDoubleSpinBox
* g ;
QDoubleSpinBox
* b;
QPushButton
* Add;
QPushButton
* Remove;
QPushButton
* Cancel;
signals:
void SignalColorTF(int
action, int point, double
r, double g, double
b );
private slots:
//internal slots
void OnAdd();
void OnRemove();
};
#endif _ColorTFWidget_H_
As a general rule starting
with header gaurds we are includeing QWidget since our class is inheriting it. For
other classes that belong to widgets we are using forwared declaration.
If you having question that why
we are doing forward declaration? here is a nice
explanation.
We are inherting QWidget with
public scope. Since we are going to make our own signal and slots it is
mandatory to add Q_OBJECT macro in the class defination.
Q_OBJECT macro is responsible
to involve meta object compiler(moc). Here
moc will generate neccessary code to
setup signal slot mechanism. You can
find more info about moc here.
Constructor and destructor
will be defined in cpp file.
We are creating data members
only those widgets that are input or output or objects those will be accessed
by any method of class.
Other objects like layouts and labels will not
be changed once they created and not used by any method so they will be created
and added to widget in consturctor.
Signal SignalColorTF will provide connectivty to our widget object to
share its data with other widget. This signal have five parameters which we
want to pass. action will be used as a flag whcih carry info about either user
want to remove point or add.
cpp file of class is as
follows:
//include class declaration header
#include "ColorTFWid.h"
//include all headers of widgets used in class else
those added in headers
#include <QPushButton>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
ColorTFWidget::ColorTFWidget(QWidget * parent
):QWidget(parent)
//parent is used to make this widget a child of some
other widget
{
//creating label
// ampersion symbol (&) before any word will make it as
a Alt+ shortcut key
//to select its buddy widget
QLabel * pointlabel
= new QLabel("At
&Point: ");
//instantiating spin box
point = new QSpinBox;
//setting range of spin box
point->setRange(0,1024);
//a buddy is widget that will be selected when Alt +
shortcut key pressed
pointlabel->setBuddy(point);
QLabel * rlabel = new QLabel("&R:
");
r = new QDoubleSpinBox;
r->setSingleStep(0.1);
r->setRange(0,255);
rlabel->setBuddy(r);
QLabel * glabel = new QLabel("&G:
");
g = new QDoubleSpinBox;
g->setSingleStep(0.1);
r->setRange(0,255);
glabel->setBuddy(g);
QLabel * blabel = new QLabel("&B:
");
b = new QDoubleSpinBox;
b->setSingleStep(0.1);
b->setRange(0,255);
blabel->setBuddy(b);
//creating layout to arrange labels in a horizontal line
QHBoxLayout *
labelLayout = new QHBoxLayout;
//adding labels to layout according to order they shoud
appear
labelLayout->addWidget
(pointlabel);
labelLayout->addWidget
(rlabel);
labelLayout->addWidget
(glabel);
labelLayout->addWidget
(blabel);
//creating layout for spin boxes
QHBoxLayout *
spinboxLayout = new QHBoxLayout;
//adding spin box to layout according to order they should
appear
spinboxLayout->addWidget
(point);
spinboxLayout->addWidget
(r);
spinboxLayout->addWidget
(g);
spinboxLayout->addWidget
(b);
//adding layouts to a vertical layout
QVBoxLayout *
upperLayout = new QVBoxLayout;
upperLayout->addLayout(labelLayout);
upperLayout->addLayout(spinboxLayout);
//creating pushbuttons
Add = new QPushButton("Add");
Remove = new QPushButton("Remove");
Cancel = new QPushButton("Cancel");
//creating button layout and adding them
QHBoxLayout *
buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(Add);
buttonLayout->addWidget(Remove);
buttonLayout->addWidget(Cancel);
//arranging all layouts in a single layout
QVBoxLayout *
mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(upperLayout);
mainLayout->addLayout(buttonLayout);
//we can set single layout to a QWidget
this->setLayout(mainLayout);
//adding title of window
this->setWindowTitle("Edit
Color TF");
//connecting buttons signal to internal slots
connect(Add,SIGNAL(clicked()),this,SLOT(OnAdd()));
connect(Remove,SIGNAL(clicked()),this,SLOT(OnRemove()));
connect(Cancel,SIGNAL(clicked()),this,SLOT(close()));
}
ColorTFWidget::~ColorTFWidget()
{
//delete all widgets
delete point ;
delete r;
delete g;
delete b;
delete Add;
delete Remove;
delete Cancel;
}
void
ColorTFWidget::OnAdd()
{
// when Add is clicked this slot will emit signal with
following information
emit SignalColorTF( 1, point->value(),
r->value(),g->value(),b->value());
this->close();
}
void
ColorTFWidget::OnRemove()
{
// when Remove is clicked this slot will emit signal with
following information
emit SignalColorTF( 0, point->value(),
r->value(),g->value(),b->value());
this->close();
}
Now write a main.cpp to use this widget:
#include <QApplication>
#include "ColorTFWid.h"
int main(int argc, char
*argv[])
{
QApplication
a(argc, argv);
ColorTFWidget w;
w.show();
return a.exec();
}
To do:
You can make another widget
in main and can connect ColorTFWidget's signal to slot of another widget to
display those values.
Error log:
1.If you make a mistake in connection like this:
connect(Add,SIGNAL(clicked()),this,SLOT(OnAdd(int)));
It will not cause any compiler error but your signal will not be connected to slot. A warning will be print on command line.
It happens usually when you edit slots or when slot have parameters.
In slots that have parameters for example OnABCD(int a) if you write parameter name in connect
connect(Add,SIGNAL(clicked()),this,SLOT(OnABCD(int a)));
same problem will appear.
2. Some linker errors like following will appear if you forget to add header and cpp properly to .pro. Don't forget to qmake after any change in .pro file.
main.obj:-1: error: LNK2019: unresolved external symbol "public: virtual __cdecl ColorTFWidget::~ColorTFWidget(void)" (??1ColorTFWidget@@UEAA@XZ) referenced in function main
Sometimes you need to clean build and then qmake to generate new moc rules.
It happens usually when you edit slots or when slot have parameters.
In slots that have parameters for example OnABCD(int a) if you write parameter name in connect
connect(Add,SIGNAL(clicked()),this,SLOT(OnABCD(int a)));
same problem will appear.
2. Some linker errors like following will appear if you forget to add header and cpp properly to .pro. Don't forget to qmake after any change in .pro file.
main.obj:-1: error: LNK2019: unresolved external symbol "public: virtual __cdecl ColorTFWidget::~ColorTFWidget(void)" (??1ColorTFWidget@@UEAA@XZ) referenced in function main
Sometimes you need to clean build and then qmake to generate new moc rules.
hi can you tell me how to generate a signal on mouse move event
ReplyDeleteoverride mouseMove virtual function (you will have to subclass)
DeleteYou have to enable mouse tracking along with suggestion of osirisgothra
ReplyDelete