Проблема с масштабированием виджетов в QScrollArea

Есть следующий минимально-воспроизводимый пример:

main.py:

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
from PyQt5.uic import loadUi


class MyWindow(QDialog):
    def __init__(self):
        super().__init__()
        loadUi('main.ui', self)

        for i in range(15):
            button = QPushButton(f'Button {i}')
            self.verticalLayout.addWidget(button)

        self.scrollArea.setWidgetResizable(False)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

main.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QScrollArea" name="scrollArea">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>401</width>
     <height>301</height>
    </rect>
   </property>
   <property name="widgetResizable">
    <bool>true</bool>
   </property>
   <widget class="QWidget" name="scrollAreaWidgetContents">
    <property name="enabled">
     <bool>true</bool>
    </property>
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>399</width>
      <height>299</height>
     </rect>
    </property>
    <widget class="QWidget" name="verticalLayoutWidget">
     <property name="geometry">
      <rect>
       <x>0</x>
       <y>0</y>
       <width>401</width>
       <height>301</height>
      </rect>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout"/>
    </widget>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

Необходимо, чтобы при заполнении QVBoxLayout-а (verticalLayout) кнопками (QPushButton), они, кнопки, не масштабировались, а добавляли в QScrollArea (scrollArea) скроллинг. Т.е. как слева, а не как справа.

Текст


Нашел такой вариант:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QWidget, QScrollArea


class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Пример QScrollArea без масштабирования виджетов')
        self.setGeometry(100, 100, 300, 300)

        # Создаем QScrollArea
        self.scrollArea = QScrollArea(self)
        self.scrollArea.setGeometry(10, 10, 280, 280)

        # Создаем содержимое для QScrollArea
        scroll_content_widget = QWidget()
        scroll_content_layout = QVBoxLayout(scroll_content_widget)

        # Добавляем кнопки в QVBoxLayout
        for i in range(20):
            button = QPushButton(f'Button {i}')
            scroll_content_layout.addWidget(button)

        # Устанавливаем содержимое для QScrollArea
        self.scrollArea.setWidget(scroll_content_widget)

        # Отключаем автоматическое масштабирование
        self.scrollArea.setWidgetResizable(False)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

... он рабочий, но там логика не отделена от графического интерфейса, пытаюсь сделать из второго варианта первый, но где-то загвоздка и ничего не получается. main.ui делал в Qt Designer.

В чем проблема?


Ответы (1 шт):

Автор решения: S. Nick

У вас лишний менеджер компоновки и self.scrollArea.setWidgetResizable(True)

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton


#from PyQt5.uic import loadUi
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 300)
        
        self.scrollArea = QtWidgets.QScrollArea(Dialog)
        self.scrollArea.setGeometry(QtCore.QRect(0, 0, 401, 301))
# !!!
        self.scrollArea.setWidgetResizable(True)                               # !!!
        self.scrollArea.setObjectName("scrollArea")
        
        self.scrollAreaWidgetContents = QtWidgets.QWidget()                    # !!!
        self.scrollAreaWidgetContents.setEnabled(True)
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 399, 299))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        
#        self.verticalLayoutWidget = QtWidgets.QWidget(self.scrollAreaWidgetContents)
#        self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 401, 301))
#        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        
#        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
# ! +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
        self.verticalLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents) # !!! +++

        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)                # !!!

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        

class MyWindow(QDialog, Ui_Dialog):
    def __init__(self):
        super().__init__()

#        loadUi('main.ui', self)
        self.setupUi(self)

        for i in range(15):
            button = QPushButton(f'Button {i}')
            button.clicked.connect(lambda ch, btn=button: print(f'Нажата {btn.text()}'))
            self.verticalLayout.addWidget(button)

# ?      self.scrollArea.setWidgetResizable(False) 
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

введите сюда описание изображения


Update:

я не совсем понимаю, что вы подразумеваете под лишним менеджером компоновки и self.scrollArea.setWidgetResizable(True).

Благодарю за работающий пример, но вопрос был в том, почему main.py не работает так, как надо и какие изменения в него (и/или main.ui) стоит внести, чтобы виджеты отображались нормально...хотелось, чтобы логика была отдельно, а интерфейс отдельно...

Я для вас сделал ровно то, что вы хотели и отметил в коде что не так.

То что ниже абсолютный аналог того что я написал выше.

main.py

import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
from PyQt5.uic import loadUi

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

        loadUi('q1572176.ui', self)

        for i in range(15):
            button = QPushButton(f'Button {i}')
            self.verticalLayout.addWidget(button)

#        self.scrollArea.setWidgetResizable(False)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())

q1572176.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QScrollArea" name="scrollArea">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>401</width>
     <height>301</height>
    </rect>
   </property>
   <property name="widgetResizable">
    <bool>true</bool>
   </property>
   <widget class="QWidget" name="scrollAreaWidgetContents">
    <property name="enabled">
     <bool>true</bool>
    </property>
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>399</width>
      <height>299</height>
     </rect>
    </property>
    <layout class="QVBoxLayout" name="verticalLayout"/>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

введите сюда описание изображения

→ Ссылка