QListWidget работает не так, как я планировал
Мой код состоит из трех классов:
RoundArt, который создает изображение с закругленными углами;ListWidget, который создаетQListWidgetс определенными характеристиками (важно - он не имеет полос прокрутки);MyWindow, который создает окно приложения.
В MyWindow я создаю главный контейнер box, куда добавляю вертикальный контейнер box_1 с полосой прокрутки.
В box_1 располагается 4 виджета:
- Надпись
label_1; - Виджет
list_widget_1, который является копией классаListWidgetи представляет из себяQListWidget; - Надпись
label_2; - Виджет
list_widget_2, который является копией классаListWidgetи представляет из себяQListWidget.
Виджеты list_widget_1 и list_widget_2 я заполняю изображениями с закругленными углами, копиями класса RoundArt.
Я предполагал, что box_1, имея вертикальную полосу прокрутки, позволит виджетам list_widget_1 и list_widget_2 занять необходимое пространство и растянуться по вертикали - но этого не произошло. Содержимое ListWidget'ов смещается вниз и перестало быть видимым:
Мне же необходимо, чтобы list_widget_1 и list_widget_2 занимали столько места, сколько необходимо для нормального отображения всех элементов, находящихся внутри них:
Как я показал на картинке, list_widget_1 занял столько места, сколько ему нужно, а остальные виджеты (label_2 и list_widget_2) отодвинулись вниз, и просмотреть их позволяет полоса прокрутки. При этом list_widget_1 и list_widget_2 должны сохранить способность располагать элементы по горизонтали, если есть свободное место. Пожалуйста, скажите, как мне это сделать?
from PyQt5 import QtCore, QtWidgets, QtGui
class RoundArt(QtWidgets.QLabel):
clicked = QtCore.pyqtSignal()
def __init__(self, picture: str, x: int, *args, **kwargs):
super(RoundArt, self).__init__(*args, **kwargs)
self.setFixedSize(x, x)
self.x = x
self.radius = 10
self.setPicture(picture)
def setPicture(self, picture: str):
target = QtGui.QPixmap(self.size())
target.fill(QtCore.Qt.transparent)
p = QtGui.QPixmap(picture).scaled(
self.x, self.x, QtCore.Qt.KeepAspectRatioByExpanding,
QtCore.Qt.SmoothTransformation
)
painter = QtGui.QPainter(target)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)
path = QtGui.QPainterPath()
path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
painter.setClipPath(path)
painter.drawPixmap(0, 0, p)
self.setPixmap(target)
painter.end()
target = None
def mouseReleaseEvent(self, event):
self.clicked.emit()
class ListWidget(QtWidgets.QListWidget):
def __init__(self, *args, **kwargs):
super(ListWidget, self).__init__(*args, **kwargs)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setEditTriggers(self.NoEditTriggers)
self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
self.setSelectionMode(self.ContiguousSelection)
self.setFlow(self.LeftToRight)
self.setWrapping(True)
self.setResizeMode(self.Adjust)
self.setSpacing(14)
self.setContentsMargins(30, 30, 0, 30)
self._rubberPos = None
self._rubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
def makeItem(self, lb):
item = QtWidgets.QListWidgetItem(self)
item.setSizeHint(QtCore.QSize(140, 140))
self.setItemWidget(item, lb)
class MyWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
scrollArea = QtWidgets.QScrollArea()
content_widget = QtWidgets.QWidget()
scrollArea.setStyleSheet("border-style: hidden;")
scrollArea.setWidget(content_widget)
scrollArea.setWidgetResizable(True)
scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
box_1 = QtWidgets.QVBoxLayout(content_widget)
box = QtWidgets.QHBoxLayout(self)
box.addWidget(scrollArea)
list_widget_1 = ListWidget()
list_widget_2 = ListWidget()
label_1 = QtWidgets.QLabel('label 1')
box_1.addWidget(label_1)
box_1.addWidget(list_widget_1)
label_2 = QtWidgets.QLabel('label 2')
box_1.addWidget(label_2)
box_1.addWidget(list_widget_2)
for i in range(5):
art = RoundArt('album', 150)
list_widget_1.makeItem(art)
for i in range(5):
art = RoundArt('album', 150)
list_widget_2.makeItem(art)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowTitle(' ')
window.show()
sys.exit(app.exec_())
UPD:
from PyQt5 import QtCore, QtWidgets, QtGui
class RoundArt(QtWidgets.QLabel):
clicked = QtCore.pyqtSignal()
def __init__(self, picture: str, x: int, *args, **kwargs):
super(RoundArt, self).__init__(*args, **kwargs)
self.setFixedSize(x, x)
self.x = x
self.radius = 10
self.setPicture(picture)
def setPicture(self, picture: str):
target = QtGui.QPixmap(self.size())
target.fill(QtCore.Qt.transparent)
p = QtGui.QPixmap(picture).scaled(
self.x, self.x,
QtCore.Qt.KeepAspectRatioByExpanding,
QtCore.Qt.SmoothTransformation
)
painter = QtGui.QPainter(target)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)
path = QtGui.QPainterPath()
path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
painter.setClipPath(path)
painter.drawPixmap(0, 0, p)
self.setPixmap(target)
painter.end()
target = None
def mouseReleaseEvent(self, event):
self.clicked.emit()
class ListWidget(QtWidgets.QListWidget):
def __init__(self, *args, **kwargs):
super(ListWidget, self).__init__(*args, **kwargs)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setEditTriggers(self.NoEditTriggers)
self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
self.setSelectionMode(self.ContiguousSelection)
self.setFlow(self.LeftToRight)
self.setWrapping(True)
self.setResizeMode(self.Adjust)
self.setSpacing(10)
#self.setContentsMargins(100, 100, 100, 100)
self._rubberPos = None
self._rubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
def makeItem(self, lb):
item = QtWidgets.QListWidgetItem(self)
item.setSizeHint(QtCore.QSize(150, 150))
self.setItemWidget(item, lb)
def updateHeight(self):
self.setMaximumHeight(self.sizeHint().height())
def getHeight(self, parent=None):
height = 0
if not parent:
parent = self.rootIndex()
for row in range(self.model().rowCount(parent)):
child = self.model().index(row, 0, parent)
height += 150
return height
def sizeHint(self):
hint = super().sizeHint()
hint.setHeight(self.getHeight())
return hint
def minimumSizeHint(self):
return self.sizeHint()
class MyWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.dict = {} # словарь для хранения QListWidget'ов и количества изображений внутри них
scrollArea = QtWidgets.QScrollArea()
content_widget = QtWidgets.QWidget()
#scrollArea.setStyleSheet("border-style: hidden;")
scrollArea.setWidget(content_widget)
scrollArea.setWidgetResizable(True)
scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.box_1 = QtWidgets.QVBoxLayout(content_widget)
box = QtWidgets.QHBoxLayout(self)
box.addWidget(scrollArea)
self.list_widget_1 = ListWidget()
self.list_widget_2 = ListWidget()
self.label_1 = QtWidgets.QLabel('label 1')
self.box_1.addWidget(self.label_1)
self.box_1.addWidget(self.list_widget_1, stretch = 1)
label_2 = QtWidgets.QLabel('label 2')
self.box_1.addWidget(label_2)
self.box_1.addWidget(self.list_widget_2, stretch=1)
self.box_1.addStretch(100)
a = 11 # любое число, позже программа будет получать его самостоятельно
for i in range(a):
art = RoundArt('album.png', 150)
self.list_widget_1.makeItem(art)
self.dict[self.list_widget_1] = a
b = 5 # любое число, позже программа будет получать его самостоятельно
for i in range(b):
art = RoundArt('album.png', 150)
self.list_widget_2.makeItem(art)
self.dict[self.list_widget_2] = b
def resizeEvent(self, event):
l = 150 # размер стороны изображения
d = 10 # отступ между изображениями
m = l + 2*d
for widget in self.dict:
n = self.dict[widget] # количество изображений
k = (widget.width() - 4.5)//m
if n % k == 0:
h = m*(n // k) + 4
else:
h = m*(n // k + 1) + 4
widget.setMinimumHeight(h)
self.update()
super(MyWindow, self).resizeEvent(event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowTitle(' ')
window.show()
sys.exit(app.exec_())
Ответы (1 шт):
я может чего и лишнего написал пока делал отладку, поэкспериментируйте и если что найдете ненужное уберете или подкорректируете. Удачи.
from PyQt5 import QtCore, QtWidgets, QtGui
class RoundArt(QtWidgets.QLabel):
clicked = QtCore.pyqtSignal()
def __init__(self, picture: str, x: int, num, *args, **kwargs): # num для отладки
super(RoundArt, self).__init__(*args, **kwargs)
self.num = num # num для отладки
self.setFixedSize(x, x)
self.x = x
self.radius = 10
self.setPicture(picture)
def setPicture(self, picture: str):
target = QtGui.QPixmap(self.size())
target.fill(QtCore.Qt.transparent)
p = QtGui.QPixmap(picture).scaled(
self.x, self.x,
QtCore.Qt.KeepAspectRatioByExpanding,
QtCore.Qt.SmoothTransformation
)
painter = QtGui.QPainter(target)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True)
painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True)
path = QtGui.QPainterPath()
path.addRoundedRect(0, 0, self.width(), self.height(), self.radius, self.radius)
painter.setClipPath(path)
painter.drawPixmap(0, 0, p)
# +++ vvv для отладки
path.addText(QtCore.QPoint(65, 90),
QtGui.QFont("Times", 30, QtGui.QFont.Bold),
str(self.num));
painter.setBrush(QtGui.QColor(255, 0, 0, 15));
painter.setPen(QtCore.Qt.red);
painter.drawPath(path)
# +++ ^^^
self.setPixmap(target)
painter.end()
target = None
def mouseReleaseEvent(self, event):
self.clicked.emit()
class ListWidget(QtWidgets.QListWidget):
def __init__(self, *args, **kwargs):
super(ListWidget, self).__init__(*args, **kwargs)
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.setEditTriggers(self.NoEditTriggers)
self.setDefaultDropAction(QtCore.Qt.IgnoreAction)
self.setSelectionMode(self.ContiguousSelection)
self.setFlow(self.LeftToRight)
self.setWrapping(True)
self.setResizeMode(self.Adjust)
self.setSpacing(14)
self.setContentsMargins(30, 30, 0, 30)
self._rubberPos = None
self._rubberBand = QtWidgets.QRubberBand(QtWidgets.QRubberBand.Rectangle, self)
def makeItem(self, lb):
item = QtWidgets.QListWidgetItem(self)
item.setSizeHint(QtCore.QSize(150, 150)) # ??? 140, 140
self.setItemWidget(item, lb)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
def updateHeight(self):
self.setMaximumHeight(self.sizeHint().height())
def getHeight(self, parent=None):
height = 0
if not parent:
parent = self.rootIndex()
for row in range(self.model().rowCount(parent)):
child = self.model().index(row, 0, parent)
height += 150
return height
def sizeHint(self):
hint = super().sizeHint()
hint.setHeight(self.getHeight())
return hint
def minimumSizeHint(self):
return self.sizeHint()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class MyWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
scrollArea = QtWidgets.QScrollArea()
content_widget = QtWidgets.QWidget()
scrollArea.setStyleSheet("border-style: hidden;")
scrollArea.setWidget(content_widget)
scrollArea.setWidgetResizable(True)
scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.box_1 = QtWidgets.QVBoxLayout(content_widget) # self.
box = QtWidgets.QHBoxLayout(self)
box.addWidget(scrollArea)
self.list_widget_1 = ListWidget() # self.
self.list_widget_2 = ListWidget()
self.label_1 = QtWidgets.QLabel('label 1') # self.
self.box_1.addWidget(self.label_1)
self.box_1.addWidget(self.list_widget_1, stretch=1) # stretch=1
label_2 = QtWidgets.QLabel('label 2')
self.box_1.addWidget(label_2)
self.box_1.addWidget(self.list_widget_2, stretch=1) # , stretch=1
self.box_1.addStretch(1) # +
for i in range(5):
# art = RoundArt('album', 150)
art = RoundArt('_ph_.png', 150, i) # + i для отладки
self.list_widget_1.makeItem(art)
for i in range(5):
art = RoundArt('150_150.png', 150, i) # + i для отладки
self.list_widget_2.makeItem(art)
def resizeEvent(self, event):
if self.width() < 414:
self.list_widget_1.setMinimumHeight(960)
self.list_widget_2.setMinimumHeight(960)
elif 414 <= self.width() < 592:
self.list_widget_1.setMinimumHeight(576)
self.list_widget_2.setMinimumHeight(576)
elif 592 <= self.width() < 948:
self.list_widget_1.setMinimumHeight(384)
self.list_widget_2.setMinimumHeight(384)
else:
self.list_widget_1.setMinimumHeight(192)
self.list_widget_2.setMinimumHeight(192)
self.update()
super(MyWindow, self).resizeEvent(event)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowTitle(' ')
window.show()
sys.exit(app.exec_())
Update
... Я изменил ваш код и добавил его в свой вопрос. Почему-то пока не могу найти в нем подвох, т.к. кажется, что все работает. Это странно, поэтому я решил снова обратиться к вам. Не могли бы вы проверить этот код на ошибки, если будет время?
Я проверил ваш пример, который вы опубликовали в обновлении, на нескольких вариантах (логику расчета я не проверял). Единственное что я обнаружил, что при загрузке приложения, окно неправильно отображается, но изменив размер окна отображается правильно.
Предлагаю поправить это так:
...
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.setWindowTitle(' ')
window.show()
print(window.size()) # +++ для отладки
window.resize(300, 335) # +++ после show() !!!
sys.exit(app.exec_())


