PDA

View Full Version : Problems with using multiple instances of the same custom widget.



haze
30th June 2010, 12:30
Hi! I'm fairly new to Qt, C++ and OOP all together, but I'm working on a project as a part of a summerjob.

I'm developing a small custom widget which will graphically display a recived value (something akin to a speedometer), and what I have made so far works to certain degree... untill I try to use more than one instance of the widget in a UI. The first instance displays correctly, but the graphical items in the second one doesn't seem to initialize. On the other hand it seems like the second instance recives input which is actually only connected to the input-slot on the first instance. My gut-feeling says it's something to do with what inherits what?

All in all, I feel a bit confused. What precautions do you have to take when you're writing a widget meant to be reused several times in a GUI?

The widget is built so that it can be loaded directly into QtDesigner as a plugin and I use svg-graphics (just mentioning if this could be related to the problem). I've programmed it in QtCreator.

The widget itself is a huge mess of stuff, so I wrote a smaller example illustrating the basics of how I've made the it and which also demonstrates the same problem. It compiles without any warnings.

In short: I can't get two instances of a selfmade widget to work in a UI, while one single one works. Example code below:

displayclass.h


#ifndef DISPLAYCLASS_H
#define DISPLAYCLASS_H

#include <QtGui/QWidget>
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsSvgItem>
#include <QGraphicsItem>
#include <QSvgRenderer>
#include <QTimer>
#include <QDebug>
//#include <qdesignerexportwidget.h>
#include <QLabel>

class /*QDESIGNER_WIDGET_EXPORT*/ displayclass : public QWidget
{
Q_OBJECT

public:
displayclass(QWidget *parent = 0);

QSvgRenderer *svgRend;
QGraphicsScene *scene;
QGraphicsView *view;
QLabel *label;
QGraphicsRectItem *rectangle;
QGraphicsSvgItem *arrow;
QTimer *timer;
void UpdateAll(double rotation, int save_angle);
void DisplaySquare(double rotation);
void DisplayArrow();
void DisplayLabel(double rotation);

public slots:
void Input(int rotation);

private slots:
void Timer();
};

#endif

displayclass.cpp

#include "displayclass.h"

displayclass::displayclass(QWidget *parent) :
QWidget(parent)
{
//Definitions
scene = new QGraphicsScene(QRect(0, 0, 300, 300),this);
svgRend = new QSvgRenderer(QLatin1String(":/arrow.svg"),this);
view = new QGraphicsView(this);
arrow = new QGraphicsSvgItem();
rectangle = new QGraphicsRectItem(0,0,100,100);
label = new QLabel(this);

timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(Timer()));
timer->start(30);

scene->setSceneRect(0,0,300,300);
view->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
view->setGeometry(QRect(0, 0, 350, 350));
view->setScene(scene);
view->show();
UpdateAll(0,0);
}

void displayclass::UpdateAll(double rotation, int save_angle)
{
static double rot=0;

if(save_angle==1)
{
rot=rotation;
}
else
{
DisplaySquare(rot);
DisplayArrow();
DisplayLabel(rot);
}
}

void displayclass::DisplaySquare(double rotation)
{
static int rectangle_activated=0;

if(rectangle_activated==0)
{
rectangle->setBrush( Qt::red );
rectangle->setPos(0,0);
qDebug()<<rectangle->pos();
rectangle_activated=1;
scene->addItem(rectangle);
}
rectangle->setRotation(rotation);
}

void displayclass::DisplayArrow()
{
static int arrow_activated=0;

if(arrow_activated==0)
{
arrow->setParentItem(rectangle);
arrow->setSharedRenderer(svgRend);
arrow->setPos(0,0);
}
}

void displayclass::DisplayLabel(double rotation)
{
static bool label_activated=0;

if(label_activated==0)
{
label->setParent(this);
label->setGeometry((scene->width()/2)-25,(scene->height()/2)-(label->height()/2),50,25);
label->setAlignment(Qt::AlignCenter | Qt::AlignHCenter);
label->setAutoFillBackground(1);
label->setStyleSheet("QLabel { border-width: 2px; border-radius: 4px; border-style: solid; border-color: darkGray; background-color: white;}");
label_activated=1;
}
label->setText(QString::number(rotation));
}

void displayclass::Timer()
{
UpdateAll(0,0);
}

void displayclass::Input(int rotation)
{

UpdateAll(rotation,1);

}


I also attached a picture of how it looks when I use two instances in a quickly put together application.
Both of the instances are declared like so in ui_mainwindow of the application:


displayclass1 = new displayclass(centralWidget);
displayclass1->setObjectName(QString::fromUtf8("displayclass1"));
displayclass1->setGeometry(QRect(40, 60, 211, 231));
displayclass2 = new displayclass(centralWidget);
displayclass2->setObjectName(QString::fromUtf8("displayclass2"));
displayclass2->setGeometry(QRect(350, 60, 201, 231));

Hmm, anything else I should specify if it could be helpful?

Thanks for any help in advance! :D (oh and sorry for any bad English!)
Any comments about obvious c++/Qt basics I've missed are also welcome!

high_flyer
30th June 2010, 14:10
show the code where the two instances are.

haze
1st July 2010, 08:23
Allright, it's the form made when you start with a new, standard Qt4 GUI-application:


/************************************************** ******************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created: Wed 30. Jun 11:42:55 2010
** by: Qt User Interface Compiler version 4.6.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
************************************************** ******************************/

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include <QtCore/QVariant>
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QDial>
#include <QtGui/QHeaderView>
#include <QtGui/QMainWindow>
#include <QtGui/QMenuBar>
#include <QtGui/QStatusBar>
#include <QtGui/QToolBar>
#include <QtGui/QWidget>
#include "displayclass.h"

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
QWidget *centralWidget;
QDial *dial;
displayclass *displayclass1;
displayclass *displayclass2;
QMenuBar *menuBar;
QToolBar *mainToolBar;
QStatusBar *statusBar;

void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
MainWindow->resize(600, 400);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
dial = new QDial(centralWidget);
dial->setObjectName(QString::fromUtf8("dial"));
dial->setGeometry(QRect(280, 10, 50, 64));
dial->setMaximum(360);
displayclass1 = new displayclass(centralWidget);
displayclass1->setObjectName(QString::fromUtf8("displayclass1"));
displayclass1->setGeometry(QRect(40, 60, 211, 231));
displayclass2 = new displayclass(centralWidget);
displayclass2->setObjectName(QString::fromUtf8("displayclass2"));
displayclass2->setGeometry(QRect(350, 60, 201, 231));
MainWindow->setCentralWidget(centralWidget);
menuBar = new QMenuBar(MainWindow);
menuBar->setObjectName(QString::fromUtf8("menuBar"));
menuBar->setGeometry(QRect(0, 0, 600, 24));
MainWindow->setMenuBar(menuBar);
mainToolBar = new QToolBar(MainWindow);
mainToolBar->setObjectName(QString::fromUtf8("mainToolBar"));
MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar(MainWindow);
statusBar->setObjectName(QString::fromUtf8("statusBar"));
MainWindow->setStatusBar(statusBar);

retranslateUi(MainWindow);
QObject::connect(dial, SIGNAL(valueChanged(int)), displayclass1, SLOT(Input(int)));
QObject::connect(dial, SIGNAL(valueChanged(int)), displayclass2, SLOT(Input(int)));

QMetaObject::connectSlotsByName(MainWindow);
} // setupUi

void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0, QApplication::UnicodeUTF8));
} // retranslateUi

};

namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAINWINDOW_H


Hmm, is instance maybe the wrong term for what I'm trying to do?

haze
1st July 2010, 10:14
Allright, having played around with it a bit, I think some of the problems originate from the static variables. Anyone who can help me confirm this? :)

high_flyer
1st July 2010, 10:22
On the other hand it seems like the second instance recives input which is actually only connected to the input-slot on the first instance
No, both instances are connected to the dial:


QObject::connect(dial, SIGNAL(valueChanged(int)), displayclass1, SLOT(Input(int)));
QObject::connect(dial, SIGNAL(valueChanged(int)), displayclass2, SLOT(Input(int)));


But I have trouble following you code.
You have a timer that does UpdateAll(0,0); every 30 ms...
And in addition, you react to the dial, which also indirectly calls UpdateAll() with other values...

Can you describe what it is your application is trying to do?

haze
1st July 2010, 11:51
Heh, I actually posted the wrong code, the Dial was only supposed to be connected to one instance.

Mainly the application tried to provoke forth an error I've been experiencing, so it wasn't coded for elegance. ;)
Looks like I figured it out now. Appartently if you declare a static variable inside a function, it leads to some very strange program behaviour. I rewrote the widget without using static variables, and now it works just fine :)

Chalk it up to inexperience I guess! I'll see if I can find something more specific to ask about later, but thanks for now! :)

high_flyer
1st July 2010, 12:02
Looks like I figured it out now. Apparently if you declare a static variable inside a function, it leads to some very strange program behavior.
Nothing strange about it, if you know what static means.
I thought of this, but it actually should not be a problem in the way you implemented it, since you are creating them on the stack, per instance.
If you were to create them as a static member, then I could understand it.
With your implementation however, I have to think about why this actually interference between instances...
Maybe I have to refresh on static vars...

EDIT:
I fell to the trap.
Yes, static is static, basically, it gets to be global.
So yes, the static vars are shared between all instances.
This the reason C++ has calss members.

Statics are only used for static classes, or static member functions, but other than that they should not be used in C++.