0

This picture illustrate the problem. It is a dialog with a QScrollArea in it. The area has a QWidget, using a QGridLayout with 3 columns containing mutliple QRadioButtons. But you see only 2 columns not 3. enter image description here

But I want to have it look like this. The dialog and its child do expand to its "full width" defined by its content (the three columns of radio buttons). No need to horizontal scroll anymore. The vertical scrolling is OK and I want to keep it that way.

enter image description here

Here is an MWE reproducing the first picture.

#!/usr/bin/env python3
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class LanguageDialog(QDialog):
    def __init__(self):
        super().__init__()

        # Language widget
        wdg_lang = self._language_widget()

        # Scroll area
        scroll = QScrollArea(self)
        # scroll.setWidgetResizable(True)
        # scroll.setFrameStyle(QFrame.NoFrame)
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Here I'm not sure what to do.
        # scroll.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        scroll.setWidget(wdg_lang)

        # Button "Apply"
        button = QDialogButtonBox(QDialogButtonBox.Apply)
        button.clicked.connect(self.accept)

        # Dialog layout
        layout = QVBoxLayout(self)
        layout.addWidget(scroll)
        layout.addWidget(button)

    def _language_widget(self):
        """
        nativ | ownlocal -> tooltip: english (code)
        """
        grid = QGridLayout()
        wdg = QWidget(self)
        wdg.setLayout(grid)

        for col in range(1, 4):
            for row in range(1, 24):

                r = QRadioButton(f'{col} x {row} | Lorem ipsum dolor sit amet', self)
                r.toggled.connect(self.slot_radio)
                r.mydata = (col, row)

                grid.addWidget(r, row, col)

        # ???
        # wdg.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        return wdg

    def slot_radio(self, val):
        btn = self.sender()
        if btn.isChecked():
            print(f'{btn.mydata=}')


class Window(QMainWindow):
    def __init__(self, parent=None):
        """Initializer."""
        super().__init__(parent)

    def showEvent(self, e):
        dlg = LanguageDialog()
        dlg.exec()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec())
buhtz
  • 10,774
  • 18
  • 76
  • 149

1 Answers1

0

I took the idea from https://stackoverflow.com/a/9081579/4865723 and adapted the code. The key points of this solution are:

  • Calculate the width for the scroll area based on its child widgets width and the width of its own scrollbar.
  • Use setMinimumWidth() on the QScrollArea using this calculated width.
  • Do all this after the dialog showed up (showEvent()) and not while building it (__init__()).

This solution don't use explict numbers. Not sure if this is an elegant solution. Looking forward to your feedback.

def _calculate_scroll_area_width(self):
    widget_width =  self._scroll.widget().sizeHint().width()
    scrollbar_width = self._scroll.verticalScrollBar().sizeHint().width()

    return widget_width + scrollbar_width

def showEvent(self, e):
    super().showEvent(e)
    self._scroll.setMinimumWidth(self._calculate_scroll_area_width())

Here is the full working MWE integrating the solution.

#!/usr/bin/env python3
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class LanguageDialog(QDialog):
    def __init__(self):
        super().__init__()

        # Scroll area with language widget
        scroll = QScrollArea(self)
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        scroll.setWidget(self._language_widget())
        self._scroll = scroll
        self._scroll.setMinimumWidth(self._calculate_scroll_area_width())

        # Button "Apply"
        button = QDialogButtonBox(QDialogButtonBox.Apply)
        button.clicked.connect(self.accept)

        # Dialog layout
        layout = QVBoxLayout(self)
        layout.addWidget(scroll)
        layout.addWidget(button)

    def _calculate_scroll_area_width(self):
        widget_width =  self._scroll.widget().sizeHint().width()
        scrollbar_width =  self._scroll.verticalScrollBar().sizeHint().width()

        return widget_width + scrollbar_width

    def _language_widget(self):
        """
        nativ | ownlocal -> tooltip: english (code)
        """
        grid = QGridLayout()
        wdg = QWidget(self)
        wdg.setLayout(grid)

        for col in range(1, 4):
            for row in range(1, 24):

                r = QRadioButton(f'{col} x {row} | Lorem ipsum dolor sit amet', self)
                r.toggled.connect(self.slot_radio)
                r.mydata = (col, row)

                grid.addWidget(r, row, col)

        # ???
        # print(wdg.size())
        # print(wdg.size().width())
        # print(wdg.minimumSize())
        # wdg.setMinimumWidth(wdg.size().width()+600)
        # wdg.setMinimumHeight(wdg.size().height())
        # print(wdg.minimumSize())
        # wdg.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)

        return wdg

    def slot_radio(self, val):
        btn = self.sender()
        if btn.isChecked():
            print(f'{btn.mydata=}')


class Window(QMainWindow):
    def __init__(self, parent=None):
        """Initializer."""
        super().__init__(parent)

    def showEvent(self, e):
        dlg = LanguageDialog()
        dlg.exec()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec())
buhtz
  • 10,774
  • 18
  • 76
  • 149