Marat Gainutdinov
Marat Gainutdinov

Reputation: 132

QScrollArea.ensureWidgetVisible method does not show target widget

I am trying to make the last QPushButton visible by using method QScrollArea().ensureWidgetVisible(), but as you can see this method doesn't scroll till the last QPushButton.

Example

Could you please assist and solve my issue perhaps issue with setFrameStyle? thank you in advance.

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class Widget(QWidget):

    def __init__(self, parent= None):
        super(Widget, self).__init__()
        self.setFixedHeight(200)

        #Container Widget        
        widget = QWidget()
        #Layout of Container Widget
        layout = QVBoxLayout(self)
        for _ in range(20):
            btn = QPushButton("test")
            layout.addWidget(btn)
        widget.setLayout(layout)


        #Scroll Area Properties
        scroll = QScrollArea()
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(False)
        scroll.setWidget(widget)

        # print(scroll.verticalScrollBar().maximum())
        # vbar = scroll.verticalScrollBar()
        # vbar.setValue(vbar.maximum())
        #vbar.setValue(vbar.maximum())


        #Scroll Area Layer add 
        vLayout = QVBoxLayout(self)
        vLayout.addWidget(scroll)
        self.setLayout(vLayout)


        # items = (layout.itemAt(i) for i in range(layout.count())) 
        # for w in items:
        #     print(w)
        print(layout.count())
        #scroll.ensureWidgetVisible(layout.itemAt(layout.count()-5).widget(), xMargin=10, yMargin=10 )
        scroll.ensureWidgetVisible(layout.itemAt(layout.count()-1).widget() )
        print(layout.itemAt(layout.count()-1).widget(),"last widget")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    dialog = Widget()
    dialog.show()

    app.exec_()

Upvotes: 2

Views: 2227

Answers (1)

eyllanesc
eyllanesc

Reputation: 244369

The problem is that for efficiency reasons widgets sizes are not calculated or updated until they are displayed, in your case the viewport of QScrollArea has not updated its size and therefore moves the scroll to an intermediate position. A possible solution is to use QTimer::singleShot() to call the function ensureWidgetVisible() a moment after it has been displayed:

import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent= None):
        super(Widget, self).__init__(parent)
        self.setFixedHeight(200)

        #Container Widget        
        widget =QtWidgets.QWidget()
        #Layout of Container Widget
        layout = QtWidgets.QVBoxLayout(widget)
        for _ in range(20):
            btn = QtWidgets.QPushButton("test")
            layout.addWidget(btn)

        scroll = QtWidgets.QScrollArea()
        scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(False)
        scroll.setWidget(widget)

        #Scroll Area Layer add 
        vLayout = QtWidgets.QVBoxLayout(self)
        vLayout.addWidget(scroll)

        last_widget = layout.itemAt(layout.count()-1).widget()
        QtCore.QTimer.singleShot(0, partial(scroll.ensureWidgetVisible, last_widget))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dialog = Widget()
    dialog.show()
    sys.exit(app.exec_())

or simply call show() before:

...
last_widget = layout.itemAt(layout.count()-1).widget() 
self.show()
scroll.ensureWidgetVisible(last_widget)

Upvotes: 4

Related Questions