PDA

View Full Version : Hiding QProxyWidget in QGraphicsLinearLayout not working



bmahf
8th March 2011, 21:10
Hi all,

I have been working on this issue for some time now, scouring the net, and reading on this forum, but haven't yet found an answer to my issue. I have a QGraphicsLinearLayout on my QGraphicsScene with a horizontal layout, and I added a number of widgets to it that are extended from QGraphicsProxyWidget. At some point during the program, something happens that causes one of the widgets, an indicator icon, either to show or hide. My expectation for this linear layout is that it should be able to adjust the cell widths according to the stretch factors given. It doesn't. Since I have the background that the layout is sitting over set to black, whenever the icon disappears, there is a black box in its place.

I have created a minimal version of what I am doing:

TestClasses.h


#ifndef TESTCLASSES_H
#define TESTCLASSES_H

#include <QtGui>

class QGraphicsWidget;
class InnerClass1;
class InnerClass2;

class OuterClass : public QGraphicsWidget
{
Q_OBJECT

public:
OuterClass(QGraphicsItem* parent = 0);
virtual ~OuterClass();

void showIcon(const bool show);

public slots:
void resetLayout();

protected:
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget);

private:
QGraphicsLinearLayout* m_layout;
InnerClass1* m_string1Widget;
InnerClass1* m_string2Widget;
InnerClass2* m_iconWidget;
};

class InnerClass1 : public QGraphicsProxyWidget
{
Q_OBJECT

public:
InnerClass1(QGraphicsWidget* parent = 0, const QString & string = "");
~InnerClass1();

private:
QLabel *m_string;

};

class InnerClass2 : public QGraphicsProxyWidget
{
Q_OBJECT

public:
InnerClass2(QGraphicsWidget* parent = 0);
~InnerClass2();

void showIcon(const bool show);

signals:
void visibilityChanged();

protected:
void timerEvent(QTimerEvent * event);

private:
QLabel *m_icon;
int m_width;
int m_timerId;

};

#endif // TESTCLASSES_H


TestClasses.cpp


#include "TestClasses.h"
#include <QGraphicsWidget>


/************************************************** ***************************************
** OuterClass class definitions
************************************************** ***************************************/

OuterClass::OuterClass(QGraphicsItem* parent)
: QGraphicsWidget(parent)
{
m_layout = new QGraphicsLinearLayout();
m_layout->setContentsMargins(0,0,0,0);
m_layout->setSpacing(0);
setLayout(m_layout);

m_string1Widget = new InnerClass1(this, "First string");
m_string2Widget = new InnerClass1(this, "Second string");
m_iconWidget = new InnerClass2(this);

m_layout->addItem(m_string1Widget);
m_layout->setStretchFactor(m_string1Widget, 2);

m_layout->addItem(m_iconWidget);
m_layout->setStretchFactor(m_iconWidget, 1);

m_layout->addItem(m_string2Widget);
m_layout->setStretchFactor(m_string2Widget, 2);

connect(m_iconWidget, SIGNAL(visibilityChanged()), this, SLOT(resetLayout()));
}

OuterClass::~OuterClass() {

}

void OuterClass::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) {
Q_UNUSED(option);
Q_UNUSED(widget);

painter->save();
painter->setRenderHint(QPainter::Antialiasing);
// First, fill rectangle with background color.
painter->fillRect(boundingRect(), Qt::black);
painter->restore();
}

void OuterClass::resetLayout()
{
// NOTE: adding these didn't help
//this->updateGeometry();
//m_layout->updateGeometry();
m_layout->invalidate();
m_layout->activate();
}


/************************************************** ***************************************
** InnerClass1 class definitions
************************************************** ***************************************/

InnerClass1::InnerClass1(QGraphicsWidget* parent, const QString & string)
: QGraphicsProxyWidget(parent)
{
m_string = new QLabel();
m_string->setText(string);
QStyle* style = m_string->style();
style->visualAlignment(Qt::LayoutDirectionAuto, Qt::AlignCenter);
this->setWidget(m_string);
}

InnerClass1::~InnerClass1()
{
}


/************************************************** ***************************************
** InnerClass2 class definitions
************************************************** ***************************************/

static const QString ICON_SRC = ":/circle.png";

InnerClass2::InnerClass2(QGraphicsWidget* parent)
: QGraphicsProxyWidget(parent)
{
QPixmap pixmap(ICON_SRC);
m_icon = new QLabel();
m_icon->setPixmap(pixmap);
this->setWidget(m_icon);
m_width = m_icon->rect().width();
m_timerId = startTimer(1000);
}

InnerClass2::~InnerClass2()
{
}

void InnerClass2::timerEvent(QTimerEvent * event)
{
if (event->timerId() == m_timerId)
showIcon(!m_icon->isVisible());
}

void InnerClass2::showIcon(bool show)
{
if (m_icon->isVisible() != show) {
m_icon->setVisible(show);
// adjust the icon geometry and tell the layout to reset itself
QRect rect = m_icon->rect();
if (show)
rect.setWidth(m_width);
else
rect.setWidth(0);
// NOTE: adding this didn't help
//m_icon->setGeometry(rect);
this->setGeometry(rect);
emit visibilityChanged();
}
}


and I instantiate this from MainWindow.h:


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "TestClasses.h"
#include <QGraphicsView>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
OuterClass *outer = new OuterClass();
m_scene = new QGraphicsScene(outer->rect());
m_scene->addItem(outer);

QGraphicsView *view = new QGraphicsView(m_scene);
this->setCentralWidget(view);
}

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


You could use any .png file, but I'm using the one from the Qt examples directory found at widgets/tooltips/images/circle.png.

In the OuterClass constructor, I create three widgets, two strings and one icon. I place then into the layout with the icon as the second in line. I give the two string widgets greater priority for stretching with a factor of 2 for each. I give the icon widget less with a factor of 1. Then I connect the signal from the icon widget to a slot belonging to OuterClass.

InnerClass1, the string class, sets the string into its QLabel. InnerClass2, the icon class, sets the pixmap and a timer for 1 second intervals. When the timer goes off, InnerClass2's timerEvent() calls showIcon() to toggle the icon visibility, and showIcon() emits the signal that was connected to by OuterClass. The OuterClass slot then calls invalidate() and activate() on the layout. I also tried adding a call to updateGeometry() for both the QLabel icon object and the icon widget itself.

From everything I've heard and read, this should cause the layout to reset itself, but if you grab my code and run it with some icon, you'll see that the text cells don't get allocated more space to cover over the icon's area, and then less space to make room for the icon again, but rather the black background is being shown. I even went to the extent of having InnerClass2::showIcon() reset the InnerClass2 rect to have the width of the icon when it is to be show, and 0 when it is not to be shown. I set the icon widget's geometry with this rect, and even tried setting the QLabel for icon as well with this rect. This didn't help.

bmahf
11th March 2011, 14:56
So I guess no one has any good ideas here. I have fixed my issue, but it seems kind of a kludge. Instead of calling invalidate() and then activate() on the layout, I am removing the widget and hiding it when I want it not shown, and then I am adding it and showing it when I want it shown. This works, but I am still stumped as to why my other implementation didn't.