Problem with scrolling in QScrollArea
-
Hello,
I'm having a problem with a QScrollArea. What I'm doing is add an object that contains a QFrame to the QScrollArea. This object will store a variable number of other objects that function as rows that have some funcionalities.
The problem seems very simple: When adding rows to the scroll area to a point that the new row added it's below it's viewport, a method is called that scrolls to the value where the row is so it can be seen, but it fails to do so. It only scrolls to the "top" of the row. I think I found the origin of the error but I don't know how to solve it. It may be related to the range of the scroll area being updated after the method that shows the selected row is executed. So, if it scrolls to the maximum, it will scroll to the old maximum, before it's value was updated. I do not want to hard code the new maximum value, but I can't seem to find a solution.
Here I provide to images to illustrate how it should behave vs how it's currently behaving when a new row is added.1 - How it should behave:
2 - How it behaves:
Here is the code that is in charge of showing the row:
void blanqueo::showRow() { // Get the position of the frame within the scroll area QPoint frameInScrollArea = actualRow->mapTo(ui->scrollArea, QPoint(0, 0)); // Calculate the position of the custom object within the scroll area QPoint rowPosInScrollArea = frameInScrollArea + actualRow->getActualPoint(); // Get the y-coordinate of the top edge of the custom object int yTop = rowPosInScrollArea.y(); // Get the height of the scroll area viewport int viewportHeight = ui->scrollArea->viewport()->height(); // Get the current scrollbar value and range QScrollBar* scrollbar = ui->scrollArea->verticalScrollBar(); int currentValue = scrollbar->value(); int maximumValue = scrollbar->maximum(); // Only adjust the scrollbar value if the selected row is not currently visible if (yTop < currentValue || yTop > currentValue + viewportHeight) { // Calculate the new scrollbar value that will make the selected row fully visible int newValue = qMax(yTop - viewportHeight / 2, 0); newValue = qMin(newValue, maximumValue); // Set the new scrollbar value // scrollbar->setValue(newValue); scrollbar->setSliderPosition(newValue); } changeButtonStyle(actualRow->isActualScanned()); ui->currentTubeLabel->setText( insertTubeText.arg( actualRow->getActualTube() ) ); }
And this is the method getActualPoint() used:
QPoint noProtocolBlanqSpec::getActualPoint() { // Get position of row in QFrame and add row height QPoint rowPosInFrame = br->mapTo(ui->frame, QPoint(0, 0)) + QPoint(0, getActualHeight() ); qDebug() << rowPosInFrame << getActualHeight(); return rowPosInFrame; }
I'm open to suggestions on how to implement this, because I've seen strange behavior under certain scenarios, which I have fixed by not calling it when the user selects a Row manually, because that would mean they can already see it.
I hope it's not that confusing, feel free to ask any questions.
Thank you all in advance.
-
@superg said in Problem with scrolling in QScrollArea:
a method is called that scrolls to the value where the row is so it can be seen, but it fails to do so. It only scrolls to the "top" of the row. I think I found the origin of the error but I don't know how to solve it. It may be related to the range of the scroll area being updated after the method that shows the selected row is executed. So, if it scrolls to the maximum, it will scroll to the old maximum, before it's value was updated.
This could indeed be a part of the problem.
Either sort it out, or: maybe you can put the "scroll to visible" code into a
QTimer::singleShot(0, ...)
call? That delays it till after whatever updating is now going on has taken effect.Otherwise if the "scroll till visible" code really only makes the top of the item visible at the bottom of the pane you could then scroll down by the height of an item.
-
@superg
Have you tried this method :
*void QScrollArea::ensureWidgetVisible(QWidget childWidget, int xmargin = 50, int ymargin = 50)
Scrolls the contents of the scroll area so that the childWidget of QScrollArea::widget() is visible inside the viewport with margins specified in pixels by xmargin and ymargin. If the specified point cannot be reached, the contents are scrolled to the nearest valid position. The default value for both margins is 50 pixels. -
@superg said in Problem with scrolling in QScrollArea:
a method is called that scrolls to the value where the row is so it can be seen, but it fails to do so. It only scrolls to the "top" of the row. I think I found the origin of the error but I don't know how to solve it. It may be related to the range of the scroll area being updated after the method that shows the selected row is executed. So, if it scrolls to the maximum, it will scroll to the old maximum, before it's value was updated.
This could indeed be a part of the problem.
Either sort it out, or: maybe you can put the "scroll to visible" code into a
QTimer::singleShot(0, ...)
call? That delays it till after whatever updating is now going on has taken effect.Otherwise if the "scroll till visible" code really only makes the top of the item visible at the bottom of the pane you could then scroll down by the height of an item.
-
@JonB Thank you! I tried
QTimer::singleShot(0, ...)
and it kind of worked, but still I found that my code had several problems. I have decided to do some refactoring so that I'll be easier to make this and other functionalities work better and easier. I was trying to avoid that but seems like it's the only way. Thanks again for your help, I'll mark it as a solution. -