[SOLVED] PyQt - Define a widget visible area in a QGridLayout



  • Hi everyone!

    Nowadays I'm dealing with a widget full of labels and buttons that it's really big (like 400x400 aprox.) the fact is that I want to put it in my main window which have set a QGridLayout. The problem it's that, as it's "so" big, IT CHANGES MY WINDOW SHAPE! because the gridlayout put it all (I mean, all the widget itself) in the window and, as it doesn't fit in the space of the gridlayout that I provided, it changes my widnow shape.

    Because of that, I was wondering how can I define a cell of the gridlayout as a "visible area". I would like to put my widget in that visible area and that only show the part of the widget that fit in the space. To move around the widget I would use the method QWidget.scroll() and some buttons assigment for that method, as if it was attached to a widget without layout (at the begging I stablished like that but I want to be able of change the window size and, with it, changing all the widget sizes). Also, of course, the "visible area" shouldn't take more space than which is provided by the GridLayout.

    Also, please don't recomend me QScrollArea because I don't like the scrollbars and as the space I provide it's relative little, it seeems ugly to me. And if it can't be done I will definetely use a ScrollArea but I'm pretty sure that PyQt provides a possiblity the problem it's that I'm really noob at it and I'm not founding any solution!

    Hope someone helps me out! :)



  • The easiest way is to use a QScrollArea and then hide the scrollbars from view via the ScrollBarPolicy property as per the following example:

    @
    import sip
    sip.setapi('QString',2)
    sip.setapi('QVariant',2)

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    class Widget(QWidget):
    def init(self, parent=None, **kwargs):
    QWidget.init(self, parent, **kwargs)

        l=QGridLayout(self)
    
        for i in xrange(10):
            for j in xrange(10):
                l.addWidget(QPushButton("Button ({0},{1})".format(i,j), self), i, j)
    

    class DockWidget(QWidget):
    def init(self, scrollArea, parent=None, **kwargs):
    QWidget.init(self, parent, **kwargs)

        self._scrollArea=scrollArea
    
        l=QHBoxLayout(self)
    
        l.addWidget(QPushButton("Up", self, clicked=lambda: self.scroll(0,-10)))
        l.addWidget(QPushButton("Down", self, clicked=lambda: self.scroll(0,10)))
        l.addWidget(QPushButton("Left", self, clicked=lambda: self.scroll(-10,0)))
        l.addWidget(QPushButton("Right", self, clicked=lambda: self.scroll(10,0)))
    
    @pyqtSlot()
    def scroll(self, x, y):
        if x!=0:
            hsb=self._scrollArea.horizontalScrollBar()
            hsb.setValue(hsb.value()+x)
            
        if y!=0:
            vsb=self._scrollArea.verticalScrollBar()
            vsb.setValue(vsb.value()+y)           
    

    class MainWindow(QMainWindow):
    def init(self, parent=None, **kwargs):
    QMainWindow.init(self, parent, **kwargs)

        self.resize(400,200)
    
        self._scrollArea=QScrollArea(self)
        self._widget=Widget(self)
        self._scrollArea.setWidget(self._widget)
        self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setCentralWidget(self._scrollArea)
    
        self._dockWidget=DockWidget(self._scrollArea, self)
        self._qDockWidget=QDockWidget("Scroll", self)
        self._qDockWidget.setWidget(self._dockWidget)
        self.addDockWidget(Qt.BottomDockWidgetArea, self._qDockWidget)
    

    if name=="main":
    from sys import argv, exit

    a=QApplication(argv)
    m=MainWindow()
    m.show()
    m.raise_()
    exit(a.exec_())
    

    @

    The QScrollArea is set as the central widget of the QMainWindow. The QScrollArea contains the oversized QWidget and I've used a QDockWidget (which can be repositioned by the user) to contain the navigation buttons.

    Hope this helps ;o)



  • [quote author="jazzycamel" date="1398954648"]The easiest way is to use a QScrollArea and then hide the scrollbars from view via the ScrollBarPolicy property as per the following example:

    @
    import sip
    sip.setapi('QString',2)
    sip.setapi('QVariant',2)

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    class Widget(QWidget):
    def init(self, parent=None, **kwargs):
    QWidget.init(self, parent, **kwargs)

        l=QGridLayout(self)
    
        for i in xrange(10):
            for j in xrange(10):
                l.addWidget(QPushButton("Button ({0},{1})".format(i,j), self), i, j)
    

    class DockWidget(QWidget):
    def init(self, scrollArea, parent=None, **kwargs):
    QWidget.init(self, parent, **kwargs)

        self._scrollArea=scrollArea
    
        l=QHBoxLayout(self)
    
        l.addWidget(QPushButton("Up", self, clicked=lambda: self.scroll(0,-10)))
        l.addWidget(QPushButton("Down", self, clicked=lambda: self.scroll(0,10)))
        l.addWidget(QPushButton("Left", self, clicked=lambda: self.scroll(-10,0)))
        l.addWidget(QPushButton("Right", self, clicked=lambda: self.scroll(10,0)))
    
    @pyqtSlot()
    def scroll(self, x, y):
        if x!=0:
            hsb=self._scrollArea.horizontalScrollBar()
            hsb.setValue(hsb.value()+x)
            
        if y!=0:
            vsb=self._scrollArea.verticalScrollBar()
            vsb.setValue(vsb.value()+y)           
    

    class MainWindow(QMainWindow):
    def init(self, parent=None, **kwargs):
    QMainWindow.init(self, parent, **kwargs)

        self.resize(400,200)
    
        self._scrollArea=QScrollArea(self)
        self._widget=Widget(self)
        self._scrollArea.setWidget(self._widget)
        self._scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self._scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setCentralWidget(self._scrollArea)
    
        self._dockWidget=DockWidget(self._scrollArea, self)
        self._qDockWidget=QDockWidget("Scroll", self)
        self._qDockWidget.setWidget(self._dockWidget)
        self.addDockWidget(Qt.BottomDockWidgetArea, self._qDockWidget)
    

    if name=="main":
    from sys import argv, exit

    a=QApplication(argv)
    m=MainWindow()
    m.show()
    m.raise_()
    exit(a.exec_())
    

    @

    The QScrollArea is set as the central widget of the QMainWindow. The QScrollArea contains the oversized QWidget and I've used a QDockWidget (which can be repositioned by the user) to contain the navigation buttons.

    Hope this helps ;o)[/quote]

    WOW! That's an incredible example, ! I really like it! :D

    I'll try out this afternoon but I have a couple questions:

    1- The widget on the example has a GridLayout, wonderful! But if I resize the window the only that resizes is the ScrollArea, not the widget inside too. There's a way to make that work? I mean, that when I resize my window, the ScrollArea changes but the windget changes too showing THE SAME buttons but resized due to the resizement in the window. Is it possible?

    2- (this i would try out this afternoon but I asked anyway in a way to be sure) If I put that ScrollArea in a MainWindow's QGridLayout, QScrollArea would only fill the space given for the GridLayout?

    Thanks again for the example!!! ^^



    1. Try setting the widget resizable by calling QScrollArea.setWidgetResizable(True) after line 49, something like:

    @
    ...
    self._scrollArea.setWidgetResizable(True)
    ...
    @

    This should cause the layout to expand when excess space is available and scroll otherwise.

    1. QMainWindow doesn't have a layout (QGridLayout or otherwise) you have to set a central widget and apply a layout to that.... so I'm afraid I don't quite understand your question.

    Hope this helps ;o)



  • [quote author="jazzycamel" date="1399278711"]1) Try setting the widget resizable by calling QScrollArea.setWidgetResizable(True) after line 49, something like:

    @
    ...
    self._scrollArea.setWidgetResizable(True)
    ...
    @

    This should cause the layout to expand when excess space is available and scroll otherwise.

    1. QMainWindow doesn't have a layout (QGridLayout or otherwise) you have to set a central widget and apply a layout to that.... so I'm afraid I don't quite understand your question.

    Hope this helps ;o)[/quote]

    Great! :D Thank you so much!

    In my second questin I meant a widget, a widget where you set his layout as a gridlayout and that gidlayout contains this ScrollArea. sorry for that!



  • Yes, thats perfectly reasonable. The following example shows four QScrollArea's placed into a QGridLayout:

    @
    import sip
    sip.setapi("QVariant",2)
    sip.setapi("QString",2)

    from PyQt4.QtCore import *
    from PyQt4.QtGui import *

    class Widget(QWidget):
    def init(self, parent=None, **kwargs):
    QWidget.init(self, parent, **kwargs)

        l=QGridLayout(self)
    
        for i in xrange(2):
            for j in xrange(2):
    
                w=QWidget(self)
                ll=QVBoxLayout(w)
    
                for _ in xrange(5):
                    ll.addWidget(
                        QPushButton(
                            "Button ({0},{1}".format(i,j),
                            self
                        )
                    )
    
                s=QScrollArea(self)
                s.setWidget(w)
    
                l.addWidget(s, i, j)
    
        self.resize(100,100)
    

    if name=="main":
    from sys import argv, exit

    a=QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())
    

    @

    Hope this helps ;o)



  • Thank you so much! I love your last example too! With that teh post it's complete, thanks for eveything :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.