Reputation: 2200
I have a QScrollArea with a QWidget (content_widget) inside in my application. I want to fill it with images while the application is running. I would like to have the scroll behavior if the number of images exceeds the number which can be shown without scrolling. I came this far:
children_occupied_width = self.ui.content_widget.childrenRect().width()
image_view = QtGui.QGraphicsView(self.ui.content_widget)
image_view.setGeometry(QtCore.QRect( children_occupied_width, 0, 210, 210 ))
image_view.setObjectName("New Image")
placeholder_image = QtGui.QGraphicsScene()
placeholder_image.addPixmap(QtGui.QPixmap('placeholder.png'))
image_view.setScene(placeholder_image)
image_view.show()
Although the images appear in the list at the right position there is no scrolling if the images start to be placed outside of the visible area. The size of content_widget seems not to change even with
self.ui.content_widget.resize(...)
or
self.ui.content_widget.adjustSize()
How to make it grow/resize?
Upvotes: 0
Views: 4066
Reputation: 92627
This first part turned out to not be what you really needed. I am leaving it for informational purposes, but see the update at the bottom
The problem is that the QGraphicsView is itself a type of scroll widget, representing a slice of the scene it is viewing. Ideally you would just use the view by itself and make use of its own scrollbars.
But if you have specific need to forward the content size of the view up to a normal QWidget scroll, then what you would need to do is make your QGraphicsView always resize itself when the contents of the scene change. The QScrollArea is only going to respond to size changes of the widget it is set to. The view needs to change size. The process would be that the view needs to listen to signals from the scene for added or removed items, and then resize itself to completely enclose all of those children.
Here is an example of how the QGraphicsView widget, on its own, is perfectly capable of serving as the scroll functionality:
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
scene = QtGui.QGraphicsScene()
view = QtGui.QGraphicsView()
view.setScene(scene)
view.resize(600,300)
pix = QtGui.QPixmap("image.png")
w,h = pix.width(), pix.height()
x = y = 0
pad = 5
col = 3
for i in xrange(1,20):
item = scene.addPixmap(pix)
item.setPos(x,y)
if i % col == 0:
x = 0
y += h+pad
else:
x+=w+pad
view.show()
view.raise_()
app.exec_()
You can see that when the images overflow the current size of the view, you get scrollbars.
If you really need to have some parent scroll area acting as the scroll for the view (for reasons I do not really understand), then here is a more complex example showing how you would need to watch for some event on the scene and then constantly update the size of the view to force scrollbars on the parent scrollarea. I have chosen to watch a signal on the scene for when its rect changes (more children are added)
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
win = QtGui.QDialog()
win.resize(600,300)
layout = QtGui.QVBoxLayout(win)
scroll = QtGui.QScrollArea()
scroll.setWidgetResizable(True)
layout.addWidget(scroll)
view = QtGui.QGraphicsView(scroll)
view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scene = QtGui.QGraphicsScene(scroll)
view.setScene(scene)
scroll.setWidget(view)
pix = QtGui.QPixmap("image.png")
w,h = pix.width(), pix.height()
x = y = i = 0
pad = 5
col = 3
def createImage():
global x,y,i
item = scene.addPixmap(pix)
item.setPos(x,y)
i+=1
if i % col == 0:
x = 0
y += h+pad
else:
x+=w+pad
def changed():
size = scene.itemsBoundingRect().size()
view.setMinimumSize(size.width()+pad, size.height()+pad)
scene.changed.connect(changed)
t = QtCore.QTimer(win)
t.timeout.connect(createImage)
t.start(500)
win.show()
win.raise_()
app.exec_()
The scroll area is always looking at the size of the widget that has set as the child. The view must be resized.
Update
As it turns out from your comments, you didn't really need to use the QGraphics objects for this approach. It only made your task more complicated. Simply use a vertical layout and add QLabel widgets to it:
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
win = QtGui.QDialog()
win.resize(300,300)
layout = QtGui.QVBoxLayout(win)
scroll = QtGui.QScrollArea()
scroll.setWidgetResizable(True)
layout.addWidget(scroll)
scrollContents = QtGui.QWidget()
layout = QtGui.QVBoxLayout(scrollContents)
scroll.setWidget(scrollContents)
pix = QtGui.QPixmap("image.png")
def createImage():
label = QtGui.QLabel()
label.setPixmap(pix)
layout.addWidget(label)
t = QtCore.QTimer(win)
t.timeout.connect(createImage)
t.start(500)
win.show()
win.raise_()
app.exec_()
Upvotes: 1
Reputation: 2200
I found at least a reason why resize
did not worked in my code. Setting widgetResizable
property of QScroll area to false (or disabling it in the Qt Designer) made it work.
Upvotes: 0