PDA

View Full Version : Issue with visuals when updating QGridLayout inside QScrollArea



TimeBomb
17th June 2011, 08:21
Hey guys, I have a QGridLayout inside a QFrame inside a QScrollArea. The QGridLayout holds lots of widgets.
Each time a widget is added or removed, it runs this method:

Notes:
-TabBar is the main class; it is a QWidget
-The updateLayout method is called whenever a tab is added or removed
-tabLayoutScroller is a QScrollArea
-tabLayout is a QGridLayout inside a QFrame. The QFrame is set as the main widget for tabLayoutScroller
-tabs is a QList<QPushButton*>

void TabBar::updateLayout()
{
tabLayoutScroller->setUpdatesEnabled(false);

for(int i = 0; i < tabLayout->columnCount(); i++)
{
if(!(tabLayout->itemAtPosition(0, i)))
break;
tabLayout->itemAtPosition(0, i)->widget()->hide();
tabLayout->itemAtPosition(0, i)->widget()->deleteLater();
delete tabLayout->itemAtPosition(0, i);
}

QLayoutItem *child;
while ((child = tabLayout->takeAt(0)) != 0)
delete child;

tabLayout->invalidate();

for(int i = 0; i < tabs.count(); i++)
tabLayout->addWidget(tabs.at(i), 0, tabLayout->columnCount());

tabLayoutScroller->setUpdatesEnabled(true);

tabLayoutScroller->update();
tabLayoutScroller->repaint();
}


The problem is, if I have lots of widgets open, and I close lots of them, then open lots of them... the visuals glitch out quite a bit. See:
http://i.imgur.com/oer5q.png

This is an odd problem that I have yet to figure out. Any help is highly appreciated; thanks all.

Rachol
17th June 2011, 09:49
It seems to me like some widgets are left behind and they are not deleted. Try to check for memory leaks.

Also calling update() and repaint() does nothing when you use setUpdatesEnabled (false).

Santosh Reddy
17th June 2011, 10:16
Have a look at inline comments


void TabBar::updateLayout()
{
tabLayoutScroller->setUpdatesEnabled(false);

QLayoutItem *child;
while ((child = tabLayout->takeAt(0)) != 0)
delete child;

tabLayoutScroller->update(); //Redundant, Consider Removing
tabLayoutScroller->repaint(); //Redundant, Consider Removing

tabLayout->invalidate (); //try adding this, usually not required

for(int i = 0; i < tabs.count(); i++)
tabLayout->addWidget(tabs.at(i), 0, tabLayout->columnCount());

tabLayoutScroller->update(); //Redundant, Consider Removing
tabLayoutScroller->repaint(); //Redundant, Consider Removing

tabLayoutScroller->setUpdatesEnabled(true);
}

Rachol
17th June 2011, 10:27
Have a look at inline comments



QLayoutItem *child;
while ((child = tabLayout->takeAt(0)) != 0) // This does not seem to a good way, because the item returned may not always be your widget
delete child; // it may also be some layout internally added spacer, for layout adjustment


Since the point of this code is to remove everything from the layout, I don't see a problem here. According to the documentation that is the recommended way of clearing the layout. Take a look at QLayoutItem * QLayout::takeAt ( int index ) method description.

TimeBomb
17th June 2011, 10:35
Hey guys; thanks a lot for the tips. Frustratingly, my problem still occurs. I have updated my original post in this thread with the latest code I am using. I have done my best to make sure everything is deleted, and there are no leaks or whatnot.

As always, any help is highly appreciated. Thanks again all!

Rachol
17th June 2011, 10:53
How do you handle the content of tabs.at(i)?

TimeBomb
17th June 2011, 10:57
How do you handle the content of tabs.at(i)?

When you add or remove a tab, it modifies the tabs object. Then, the tabs object is loaded in to the QGridLayout via the updateLayout method.

The base of the method that adds a tab:

TabButton *button = new TabButton(this, tab);
button->setMouseTracking(true);
button->resize(24, 24);
button->setMaximumSize(24, 24);
button->setStyleSheet(stylesheetDefault);
connect(button, SIGNAL(pressed()), SLOT(changeTab()));
tabs.insert(index, button);
updateLayout();

The base of the method that removes a tab:

TabButton *button = tabs.at(index);

tabs.removeAt(index);

updateLayout();


..Do you think it may be a better idea to just add or remove a single widget from the QGridLayout (instead of removing them all and then adding them all back in each time a tab is added/removed)?

Santosh Reddy
17th June 2011, 16:18
Since the point of this code is to remove everything from the layout, I don't see a problem here. According to the documentation that is the recommended way of clearing the layout. Take a look at QLayoutItem * QLayout::takeAt ( int index ) method description.
You are right, I just was reading it as itemAt() instead of takeAt():)

Added after 26 minutes:

Try this..


void TabBar::updateLayout()
{
tabLayoutScroller->setUpdatesEnabled(false);

// remove tabs
for(int i = 0; i < tabs.count(); i++)
tabLayout->removeWidget(tabs.at(i));

// add tabs
for(int i = 0; i < tabs.count(); i++)
tabLayout->addWidget(tabs.at(i), 0, tabLayout->columnCount());

tabLayoutScroller->setUpdatesEnabled(true);
}

TimeBomb
17th June 2011, 22:20
@Santosh Reddy:
The tabs I may be removing may no longer be in the tabs QList. That is why I remove everything in the layout, not just from the tabs QList.


I have modified my code a bit so it only adds/takes one at a time. I can't edit my previous posts now for whatever odd reason, so here is the new code:

void TabBar::updateLayout(int index, TabButton *tab)
{

tabLayoutScroller->setUpdatesEnabled(false);

if (index == -1)
tabLayout->removeWidget(tab);
else {
tabLayout->addWidget(tab, 0, index);
}

tabLayoutScroller->setUpdatesEnabled(true);

tabLayoutScroller->update();
tabLayoutScroller->repaint();
}

Code for adding a tab:

TabButton *button = new TabButton(this, tab);
button->setMouseTracking(true);
connect(button, SIGNAL(pressed()), SLOT(changeTab()));
updateLayout(index, button);
tabs.insert(index, button);

Code for removing a tab:

TabButton *button = tabs.at(index);

updateLayout(-1, tabs.at(index));

tabs.removeAt(index);

The original visual glitch still occurs though, even when it is removing/adding so simplistically =/. As always, any help is highly appreciated; thanks.

Rachol
17th June 2011, 23:53
Hi,

I took a little time and have written a short program that in my opinion represent what I think you are trying to achieve.

There is a MainWidget which contains QScrollArea and QPushButton("Add"). QScrollArea contains a TabBar(QFrame) on which I have set QHBoxLayout. Clicking "Add" button will add a new TabButton(QPushButton) into the TabBar. Clicking TabButton will remove it.

Please take a look at it, it might help you to find the solution, or then you can guide me more based on what I have written, so I can maybe reproduce your problem.

--Rachol

TimeBomb
18th June 2011, 03:59
Hi,

I took a little time and have written a short program that in my opinion represent what I think you are trying to achieve.

There is a MainWidget which contains QScrollArea and QPushButton("Add"). QScrollArea contains a TabBar(QFrame) on which I have set QHBoxLayout. Clicking "Add" button will add a new TabButton(QPushButton) into the TabBar. Clicking TabButton will remove it.

Please take a look at it, it might help you to find the solution, or then you can guide me more based on what I have written, so I can maybe reproduce your problem.

--Rachol

Hi Rachol; thanks a lot for all your help.
Changing from QGridLayout to QHBoxLayout seemed to speed up some things a tad.

But the problem still occurred.
After some moderate testing, I have deduced that this problem occurs because each button has a QIcon. When all the buttons never have QIcons, this 'visual glitch' never occurs. The QIcon is the website's favicon, and it is one of the main ways to identify one tab from another (the project that is around this tab system is a web browser).

I have tried using the setIcon method to set the current icon to an empty QIcon() right before removing the button from the QList<button> as well as from the QHBoxLayout, but the visual glitch still occurs.

Once again, any help is appreciated. Thanks a lot all.

Rachol
18th June 2011, 10:43
I tried to play with Icons, I couldn't get any visual problem. How is your TabButton implemented?

TimeBomb
18th June 2011, 11:03
Hi. In the attached zip, you will find 4 files.
Please note that this tab layout, in case I have not already explained, is used in a web browser application.
tabs.cpp and tabs.h
This includes the following classes:
TabPreviewer - A QLabel designed to display a QPixmap(The website rendered and resized), as well as the website title.
TabButton - The QPushButton that is the base tab
TabMenu - This can be ignored. It is a QListWidget that is designed to essentially show a list of all the tabs upon a button being clicked.
TabBar - This is where most of the guts occurs. It is the QWidget containing all the visual stuff.
Tabs - This is the QStackedWidget. Each widget in here is a "Tab" object, and inside that "Tab" object is a QWebView(well, a derived version of QWebView ). The only reason you should need to look through this is if you want to see how variables in TabBar/TabButton interact with it.

And then there is [B]tab.cpp and tab.h:
This is just the Tab class. A Tab is a QWidget containing a BrowserView[derived from QWebView].

At some point soon, I will more than likely seperate each class in to it's own file.

I apologize for the messiness of my code. If there is something you can not understand or have questions about, feel free to ask me.

I thought it would be best to send you this instead of going back and forth between everything, as the problem may or may not be in something I have not talked about much yet.

Thanks a lot for your help, it is really appreciated.



UPDATE: Issue is fixed. Someone on IRC told me that removeAt/removeWidget just removes the item from the list/layout - it doesn't delete the item. In the TabBar::removeAt method, I remove the TabButton, and then (I didn't do this before) I delete the actual TabButton object. This seems to solve all my problems.
Thanks a lot to everyone who helped.

Rachol
18th June 2011, 11:30
I was just going to write that...

TimeBomb
18th June 2011, 11:32
I was just going to write that...

Hehe. Nonetheless, thanks a lot for baring with me, I appreciate the help.
This issue was blocking the release of the next alpha version of my app, so I am glad it is fixed.