Issue
I have created a scroll area and set the layout to a grid in qt designer. Then I added some frames(city profile) with fixed sizes inside the scroll area(QGridLayout). I'm trying to dynamically add or delete the city profile frames. When I add "city profile" to the grid layout, it should adapt the position automatically according to the screen size. Like, when the window is not maximized, in one row, it should show only four or five "city profile" frames but when the window is maximized, there is more space in the row and it should occupy more "city profile" frames in a single row. I tried setting the layout size constraint but it's not giving the output I want. Also, I have googled about it but couldn't find any results.
Demo Image when the window is in normal view
Demo Image when the window is maximized
Markings
Solution
Considering the images you provided, a possible alternative would be to use a QListView with a related model or even a QListWidget (which inherits from QListView), and then set it up to show items from left to right and ensure that wrapping is enabled. This is how Qt shows files in a non-native QFileDialog.
Then, for simple cases, you can use setIndexWidget()
(or setItemWidget()
for QListWidget, which does the same).
This is a possible implementation:
class CityFrame(QWidget):
def __init__(self, city):
super().__init__()
self.imageLabel = QLabel(pixmap=QPixmap('city.png'))
self.cityLabel = QLabel(city, alignment=Qt.AlignCenter)
self.button = QToolButton(icon=QIcon.fromTheme('document-save'))
layout = QGridLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(1)
layout.addWidget(self.imageLabel)
layout.addWidget(self.cityLabel)
layout.addWidget(self.button, 0, 1, 2, 1, alignment=Qt.AlignTop)
self.setFixedSize(layout.sizeHint())
class FlowContainer(QListWidget):
def __init__(self):
super().__init__()
# make it look like a normal scroll area
self.viewport().setBackgroundRole(QPalette.Window)
# display items from left to right, instead of top to bottom
self.setFlow(self.LeftToRight)
# wrap items that don't fit the width of the viewport
# similar to self.setViewMode(self.IconMode)
self.setWrapping(True)
# prevent user repositioning
self.setMovement(self.Static)
# always re-layout items when the view is resized
self.setResizeMode(self.Adjust)
self.setHorizontalScrollMode(self.ScrollPerPixel)
self.setVerticalScrollMode(self.ScrollPerPixel)
self.setSpacing(4)
def addCity(self, city):
item = QListWidgetItem(city)
item.setFlags(item.flags() & ~(Qt.ItemIsSelectable|Qt.ItemIsEnabled))
self.addItem(item)
frame = CityFrame(city)
item.setSizeHint(frame.sizeHint())
self.setItemWidget(item, frame)
class Test(QWidget):
def __init__(self):
super().__init__()
self.resize(640, 320)
self.cityList = FlowContainer()
layout = QVBoxLayout(self)
layout.addWidget(self.cityList)
Cities = ['New York', 'London', 'Budapest', 'Novi Grad', 'Malibu', 'Kathmandu']
for city in Cities:
self.cityList.addCity(city)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec())
The code above is for PyQt5, for PyQt6 you'll need to adapt the namespaces of enums, like QPalette.ColorRole.Window
, etc.
And this is the result of the code above:
And after resizing:
Note that if you plan on displaying lots of items (in the order of hundreds or thousands), then you should consider using a custom delegate instead of index widgets, which allows advanced and custom painting and is much lighter for bigger models.
Answered By - musicamante
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.