添加新小部件时 Scrollarea 无法展开(滚动)

Scrollarea can't expand (scroll) when new widget added

我试着做了一些简单的本地聊天应用来练习。每次按下按钮时,我都会创建一个按钮来发送消息,但滚动区域变得更窄并且不会扩展滚动区域。我是否遗漏了某些内容或添加了我不应该在我的代码中添加的内容?我该如何解决这个问题?

这是我的代码:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

class Bubble(QLabel):
    def __init__(self,text):
        super(Bubble,self).__init__(text)
        self.setContentsMargins(5,5,5,5)

    def paintEvent(self, e):

        p = QPainter(self)
        p.setRenderHint(QPainter.Antialiasing,True)
        p.drawRoundedRect(0,0,self.width()-1,self.height()-1,5,5)

        super(Bubble,self).paintEvent(e)        

class MyWidget(QWidget):

    def __init__(self,text,left=True):
        super(MyWidget,self).__init__()

        hbox = QHBoxLayout()

        label = Bubble(text)

        if not left:
            hbox.addSpacerItem(QSpacerItem(1,1,QSizePolicy.Expanding,QSizePolicy.Preferred))

        hbox.addWidget(label)

        if left:
            hbox.addSpacerItem(QSpacerItem(1,1,QSizePolicy.Expanding,QSizePolicy.Preferred))            

        hbox.setContentsMargins(0,0,0,0)

        self.setLayout(hbox)
        self.setContentsMargins(0,0,0,0)

class Chatting(QWidget):
    def __init__(self, parent=None):
        super(Chatting, self).__init__(parent)
        self.vbox = QVBoxLayout()
        for _ in range(20):
            self.vbox.addWidget(MyWidget("Left side"))

        widget = QWidget()
        widget.setLayout(self.vbox)

        scroll = QScrollArea()
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(False)
        scroll.setWidget(widget)     

        #Scroll Area Layer add 
        vLayout = QVBoxLayout(self)
        vLayout.addWidget(scroll)

        send = QPushButton('')
        send.setIcon(QIcon('images/send.png'))
        send.setStyleSheet("background-color:#d00001; width: 44px")
        send.setIconSize(QSize(84,20))
        send.clicked.connect(self.send_messages)
        vLayout.addWidget(send)
        self.setLayout(vLayout)

    def send_messages(self):
        self.vbox.addWidget(MyWidget('testing'))

问题很简单,您必须将 widgetResizable 属性 设为 True,因为 属性 允许 QScrollArea 计算内容的大小。

scroll.setWidgetResizable(True)

另一方面,我花时间改进了您的代码并在下面展示:

from PyQt4 import QtGui, QtCore


class Bubble(QtGui.QLabel):
    def __init__(self, text):
        super(Bubble,self).__init__(text)
        self.setContentsMargins(5, 5, 5, 5)

    def paintEvent(self, e):
        p = QtGui.QPainter(self)
        p.setRenderHint(QtGui.QPainter.Antialiasing, True)
        p.drawRoundedRect(self.rect().adjusted(0, 0, -1, -1), 5, 5)
        super(Bubble, self).paintEvent(e)        

class MyWidget(QtGui.QWidget):
    def __init__(self, text, left=True):
        super(MyWidget,self).__init__()
        lay = QtGui.QVBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        self.setContentsMargins(0, 0, 0, 0)
        label = Bubble(text)
        lay.addWidget(label, alignment= QtCore.Qt.AlignLeft if left else QtCore.Qt.AlignRight)


class Chatting(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Chatting, self).__init__(parent)

        widget = QtGui.QWidget()
        self.vbox = QtGui.QVBoxLayout(widget)
        self.vbox.addStretch()
        self.scroll = QtGui.QScrollArea(widgetResizable=True)
        self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.scroll.setWidget(widget)     

        #Scroll Area Layer add 
        send = QtGui.QPushButton(icon= QtGui.QIcon('images/send.png'))
        send.setStyleSheet("background-color:#d00001; width: 44px")
        send.setIconSize(QtCore.QSize(84,20))
        send.clicked.connect(self.on_clicked)

        vLayout = QtGui.QVBoxLayout(self)
        vLayout.addWidget(self.scroll)
        vLayout.addWidget(send)

    def send_message(self, text, direction=True):
        widget = MyWidget(text, direction)
        self.vbox.insertWidget(self.vbox.count()-1, widget)
        scroll_bar = self.scroll.verticalScrollBar()
        # move to end
        QtCore.QTimer.singleShot(10, lambda: scroll_bar.setValue(scroll_bar.maximum()))

    @QtCore.pyqtSlot()
    def on_clicked(self):
        self.send_message("testing") 


if __name__ == '__main__':
    import sys
    import random

    app = QtGui.QApplication(sys.argv)
    w = Chatting()

    def test():
        for _ in range(8):
            w.send_message("testing", random.choice((True, False)))
    QtCore.QTimer.singleShot(1000, test)

    w.resize(240, 480)
    w.show()
    sys.exit(app.exec_())