Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QScrollArea with only vertical scrolling - how do I make one and how does it work?
Forum Updated to NodeBB v4.3 + New Features

QScrollArea with only vertical scrolling - how do I make one and how does it work?

Scheduled Pinned Locked Moved Unsolved General and Desktop
9 Posts 4 Posters 4.7k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • N Offline
    N Offline
    NeatNit
    wrote on last edited by NeatNit
    #1

    So, I'm dealing with an issue as old as time: I'm trying to make a QScrollArea which only scrolls vertically, and make the widget in it always take up exactly the full width available - no more, no less. The widget in question is QWidget with a VBoxLayout and all of its children are QLabels.

    Desired result:

     ------------------------------------------------
    | QScrollArea (window, resize freely)         |^||
    |  -----------------------------------------  | ||
    | | QWidget                                 | | ||
    | |  -------------------------------------  | | ||
    | | | QVBoxLayout                         | | | ||
    | | |  ---------------------------------  | | |o||
    | | | | QLabel                          | | | | ||
    | | |  ---------------------------------  | | | ||
    | | |  ---------------------------------  | | | ||
    | | | | QLabel with a bunch of text     | | | | ||
    | | | | that gets word-wrapped          | | | | ||
    | | |  ---------------------------------  | | | ||
    | | |           ...                       | | | ||
    | |  -------------------------------------  | | ||
    |  -----------------------------------------  |v||
     ------------------------------------------------
    

    Note that both the QWidget and QLabels should fill the whole width, even if they don't need it, but they must never exceed the width given to them by the scroll area. The horizontal scroll bar must never appear, not just hidden but because it's never needed.

    If I search for this question online I get a few different answers. This is one of the top results, and to me it seems wrong: it installs an event filter to detect when the size of one thing changes and sets the width of something else so it fits. (I think it actually changes the width of the scroll area rather than the widget like I want, but that's besides the point for now). I've seen a similar solution discussed on StackOverflow, too.

    But Qt already has a layout engine! Maybe I'm wrong about this, but I think I shouldn't have to tell it what size things should be, I should only have to tell it how I want things to fit together and have the layout engine take care of the rest. So, is there a way to achieve this without hacks?

    Much more importantly... Can you explain the relevant bits of the layout engine? I tried reading the relevant part of the QScrollArea page as well as the entirety of the Layout Management page, but I just don't understand how the different bits affect each other. I've found that Size Policy would give me a great deal of control over the preferred behaviour of the vertical and horizontal sizes separately, but with a layout set (QVBoxLayout in my case) this option is nullified - the layout's size policy is used instead, but it doesn't have one, the only thing it seems to have is setSizeConstraint which has an extremely limited enum of options, and doesn't explain what they mean for the different axes in a VBoxLayout.

    Sorry for the novel. TL;DR:

    1. What is the best-practice way to make a ScrollArea only scroll vertically, and make the child widget take up the full width?
    2. Can you explain how it works, so that I can learn something from it and perhaps apply it later on in my project?

    Thanks.

    Edit: I'm using PySide6 but this shouldn't matter, C++ answers would be just as good.

    M 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      If memory serves well, you have to set the layout QSizePolicy to fixed.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • N NeatNit

        So, I'm dealing with an issue as old as time: I'm trying to make a QScrollArea which only scrolls vertically, and make the widget in it always take up exactly the full width available - no more, no less. The widget in question is QWidget with a VBoxLayout and all of its children are QLabels.

        Desired result:

         ------------------------------------------------
        | QScrollArea (window, resize freely)         |^||
        |  -----------------------------------------  | ||
        | | QWidget                                 | | ||
        | |  -------------------------------------  | | ||
        | | | QVBoxLayout                         | | | ||
        | | |  ---------------------------------  | | |o||
        | | | | QLabel                          | | | | ||
        | | |  ---------------------------------  | | | ||
        | | |  ---------------------------------  | | | ||
        | | | | QLabel with a bunch of text     | | | | ||
        | | | | that gets word-wrapped          | | | | ||
        | | |  ---------------------------------  | | | ||
        | | |           ...                       | | | ||
        | |  -------------------------------------  | | ||
        |  -----------------------------------------  |v||
         ------------------------------------------------
        

        Note that both the QWidget and QLabels should fill the whole width, even if they don't need it, but they must never exceed the width given to them by the scroll area. The horizontal scroll bar must never appear, not just hidden but because it's never needed.

        If I search for this question online I get a few different answers. This is one of the top results, and to me it seems wrong: it installs an event filter to detect when the size of one thing changes and sets the width of something else so it fits. (I think it actually changes the width of the scroll area rather than the widget like I want, but that's besides the point for now). I've seen a similar solution discussed on StackOverflow, too.

        But Qt already has a layout engine! Maybe I'm wrong about this, but I think I shouldn't have to tell it what size things should be, I should only have to tell it how I want things to fit together and have the layout engine take care of the rest. So, is there a way to achieve this without hacks?

        Much more importantly... Can you explain the relevant bits of the layout engine? I tried reading the relevant part of the QScrollArea page as well as the entirety of the Layout Management page, but I just don't understand how the different bits affect each other. I've found that Size Policy would give me a great deal of control over the preferred behaviour of the vertical and horizontal sizes separately, but with a layout set (QVBoxLayout in my case) this option is nullified - the layout's size policy is used instead, but it doesn't have one, the only thing it seems to have is setSizeConstraint which has an extremely limited enum of options, and doesn't explain what they mean for the different axes in a VBoxLayout.

        Sorry for the novel. TL;DR:

        1. What is the best-practice way to make a ScrollArea only scroll vertically, and make the child widget take up the full width?
        2. Can you explain how it works, so that I can learn something from it and perhaps apply it later on in my project?

        Thanks.

        Edit: I'm using PySide6 but this shouldn't matter, C++ answers would be just as good.

        M Offline
        M Offline
        mpergand
        wrote on last edited by
        #3

        @NeatNit
        Look at:
        setHorizontalScrollBarPolicy
        and
        setSizeAdjustPolicy

        You may have to set
        setWidgetResizable(bool resizable)
        to true.

        1 Reply Last reply
        0
        • N Offline
          N Offline
          NeatNit
          wrote on last edited by
          #4

          @SGaist and @mpergand
          Please take a look at this minimal example:

          import sys
          from PySide6.QtWidgets import QApplication, QPlainTextEdit, QScrollArea, QWidget, QVBoxLayout, QLabel
          
          full_text = """First line
          Second line
          This is the third line, with a lot of words that make this line very long and causes word wrap to come into effect
          aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaa
          bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbb
          cccccccccccccccccccccccccccccc ccc
          dddddddddddddddddddddddddddddd ddd
          eeeeeeeeeeeeeeeeeeeeeeeeeeeeee eee
          ffffffffffffffffffffffffffffff fff
          gggggggggggggggggggggggggggggg ggg
          hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh hhh
          iiiiiiiiiiiiiiiiiiiiiiiiiiiiii iii
          jjjjjjjjjjjjjjjjjjjjjjjjjjjjjj jjj
          kkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkk
          llllllllllllllllllllllllllllll lll
          mmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmm
          nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn nnn
          oooooooooooooooooooooooooooooo ooo
          pppppppppppppppppppppppppppppp ppp
          qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqq
          rrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrr
          ssssssssssssssssssssssssssssss sss
          tttttttttttttttttttttttttttttt ttt
          uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu uuu
          vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvv
          wwwwwwwwwwwwwwwwwwwwwwwwwwwwww www
          xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxx
          yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy yyy
          zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz zzz"""
          
          app = QApplication(sys.argv)
          
          # QTextEdit = desired behaviour:
          text_edit = QPlainTextEdit()
          text_edit.setReadOnly(True)
          text_edit.setPlainText(full_text.replace('\n', "\n\n"))
          text_edit.setWindowTitle("Desired behaviour")
          text_edit.show()
          
          # QScrollArea + QWidget + QVBoxLayout + QLabels = desired layout structure, bad behaviour:
          view = QWidget()
          layout = QVBoxLayout(view)
          layout.setSizeConstraint(QVBoxLayout.SetMinimumSize)
          
          for txt in full_text.split('\n'):
              lbl = QLabel(txt)
              lbl.setWordWrap(True)
              layout.addWidget(lbl)
          
          
          scroll = QScrollArea()
          scroll.setWidget(view)
          scroll.setWindowTitle("Bad behaviour, desired layout structure")
          
          scroll.show()
          sys.exit(app.exec())
          

          49369d9f-4ea3-4a6f-926d-5c2f1b948822-image.png

          I tried various combinations of the settings you mentioned and couldn't get it working. Can you run this code on your end and do it?

          For now you can assume that the window is always wider than the longest word - but spoiler alert, getting word-wrap working mid-word on QLabels will be my next question ;)

          M 1 Reply Last reply
          0
          • N NeatNit

            @SGaist and @mpergand
            Please take a look at this minimal example:

            import sys
            from PySide6.QtWidgets import QApplication, QPlainTextEdit, QScrollArea, QWidget, QVBoxLayout, QLabel
            
            full_text = """First line
            Second line
            This is the third line, with a lot of words that make this line very long and causes word wrap to come into effect
            aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaa
            bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb bbb
            cccccccccccccccccccccccccccccc ccc
            dddddddddddddddddddddddddddddd ddd
            eeeeeeeeeeeeeeeeeeeeeeeeeeeeee eee
            ffffffffffffffffffffffffffffff fff
            gggggggggggggggggggggggggggggg ggg
            hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh hhh
            iiiiiiiiiiiiiiiiiiiiiiiiiiiiii iii
            jjjjjjjjjjjjjjjjjjjjjjjjjjjjjj jjj
            kkkkkkkkkkkkkkkkkkkkkkkkkkkkkk kkk
            llllllllllllllllllllllllllllll lll
            mmmmmmmmmmmmmmmmmmmmmmmmmmmmmm mmm
            nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn nnn
            oooooooooooooooooooooooooooooo ooo
            pppppppppppppppppppppppppppppp ppp
            qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqq
            rrrrrrrrrrrrrrrrrrrrrrrrrrrrrr rrr
            ssssssssssssssssssssssssssssss sss
            tttttttttttttttttttttttttttttt ttt
            uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu uuu
            vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvv
            wwwwwwwwwwwwwwwwwwwwwwwwwwwwww www
            xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxx
            yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy yyy
            zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz zzz"""
            
            app = QApplication(sys.argv)
            
            # QTextEdit = desired behaviour:
            text_edit = QPlainTextEdit()
            text_edit.setReadOnly(True)
            text_edit.setPlainText(full_text.replace('\n', "\n\n"))
            text_edit.setWindowTitle("Desired behaviour")
            text_edit.show()
            
            # QScrollArea + QWidget + QVBoxLayout + QLabels = desired layout structure, bad behaviour:
            view = QWidget()
            layout = QVBoxLayout(view)
            layout.setSizeConstraint(QVBoxLayout.SetMinimumSize)
            
            for txt in full_text.split('\n'):
                lbl = QLabel(txt)
                lbl.setWordWrap(True)
                layout.addWidget(lbl)
            
            
            scroll = QScrollArea()
            scroll.setWidget(view)
            scroll.setWindowTitle("Bad behaviour, desired layout structure")
            
            scroll.show()
            sys.exit(app.exec())
            

            49369d9f-4ea3-4a6f-926d-5c2f1b948822-image.png

            I tried various combinations of the settings you mentioned and couldn't get it working. Can you run this code on your end and do it?

            For now you can assume that the window is always wider than the longest word - but spoiler alert, getting word-wrap working mid-word on QLabels will be my next question ;)

            M Offline
            M Offline
            mpergand
            wrote on last edited by
            #5

            @NeatNit
            try:
            scroll.setWidgetResizable(true)

            if it doesn't work add:
            scroll.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents)

            N 1 Reply Last reply
            0
            • M mpergand

              @NeatNit
              try:
              scroll.setWidgetResizable(true)

              if it doesn't work add:
              scroll.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents)

              N Offline
              N Offline
              NeatNit
              wrote on last edited by NeatNit
              #6

              @mpergand This causes the vertical layout to stretch when the window is taller than the contents.

              Edit: See the screenshot:

              d027e946-e977-454b-99d4-9f1ebe3dc3c0-image.png

              M 1 Reply Last reply
              0
              • N NeatNit

                @mpergand This causes the vertical layout to stretch when the window is taller than the contents.

                Edit: See the screenshot:

                d027e946-e977-454b-99d4-9f1ebe3dc3c0-image.png

                M Offline
                M Offline
                mpergand
                wrote on last edited by mpergand
                #7

                @NeatNit

                Add at the end.
                layout.addStretch(0)

                N 1 Reply Last reply
                0
                • M mpergand

                  @NeatNit

                  Add at the end.
                  layout.addStretch(0)

                  N Offline
                  N Offline
                  NeatNit
                  wrote on last edited by
                  #8

                  @mpergand said:

                  @NeatNit

                  Add at the end.
                  layout.addStretch(0)

                  Thank you! Sorry for the late reply. Worked like a charm :)

                  1 Reply Last reply
                  0
                  • Z Offline
                    Z Offline
                    zfigura
                    wrote on last edited by
                    #9

                    For the benefit of future readers, who are frustrated to find that (despite the indications to the contrary) the code does not actually work, even after adding the modifications from comment #5:

                    What you actually need to do here is set the horizontal size policy on the inner widget (i.e. "view") to Ignored. This causes it to use the size of its container. You do not actually need setSizeConstraint() on the layout nor setSizeAdjustPolicy() on the scroll area.

                    Also, wrt the problem mentioned in comment 6, instead of adding stretch you can (and probably should) instead set the vertical size policy on the view to Maximum.

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved