如何使 QScrollArea 内的 QLabel 子部件使用 wordwrap 而不是扩展容器部件?

How to make QLabel subwidgets inside a QScrollArea use wordwrap instead of expanding the container widget?

我的代码有两个文件。一个是使用固定大小 QFrame 而不是标准 window 框架的“主”文件。第二个也是 QWidget 的子类,用于添加到主小部件中的 QScrollArea

我希望子部件的 QFrame 得到修复,标签实际使用 wordwrap 属性 而不是让它更长以适合并搞砸整个 QScrollArea滚动区域。我已尝试将 QSizePolicy 用于子小部件的 container_widgetQFrame。一个最小的可重现示例如下。

main_window.py

import sys

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg

from subwidget import SubWidget


class Window(qtw.QWidget):
    def __init__(self):
        super().__init__()
        
        # Configure Window
        self.setWindowFlags(qtc.Qt.FramelessWindowHint)
        self.setAttribute(qtc.Qt.WA_TranslucentBackground)
        self.setFixedSize(350, 450)
        
        # Init Methods
        self.setupUI()
        self.refresh_entries()
        
        self.show()
    
    def setupUI(self):
        # Main Layout/Frame
        self.main_layout = qtw.QHBoxLayout()
        self.main_frame = qtw.QFrame()
        self.main_frame_layout = qtw.QVBoxLayout()
        # Set Layouts
        self.setLayout(self.main_layout)
        self.main_layout.addWidget(self.main_frame)
        self.main_frame.setLayout(self.main_frame_layout)
        # Configure QFrame
        self.main_frame.setContentsMargins(10, 10, 10, 10)
        self.main_frame.setObjectName("main_frame")  
        self.main_frame.setStyleSheet("""
                                      border: None;
                                      border-radius: 10px;
                                      background: #1E1E1E;
                                      """)      
        # Secondary Layout/Widget
        self.main_scroll_area = qtw.QScrollArea()
        self.container_widget = qtw.QWidget()
        self.container_layout = qtw.QVBoxLayout()
        self.main_scroll_area.setObjectName("main_scroll_area")                
        self.main_scroll_area.setWidgetResizable(True)
        self.main_scroll_area.setVerticalScrollBarPolicy(qtc.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.main_scroll_area.setHorizontalScrollBarPolicy(qtc.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.main_scroll_area.setWidget(self.container_widget)
        self.container_widget.setLayout(self.container_layout)
        self.container_widget.setObjectName("container_widget")
        # Widget -> Layout
        self.main_frame_layout.addWidget(self.main_scroll_area, 0, qtc.Qt.AlignmentFlag.AlignHCenter | qtc.Qt.AlignmentFlag.AlignCenter)
    
    def refresh_entries(self):
        self.clear_entries()
                
        for i in range(5):
            self.container_layout.addWidget(SubWidget(i ** i ** i, i))

    def clear_entries(self):
        for i in reversed(range(self.container_layout.count())):
            try:
                widget = self.container_layout.itemAt(i).widget()
                widget.setParent(None)
            except Exception:
                pass
        
        
if __name__ == "__main__":
    app = qtw.QApplication(sys.argv)
    win = Window()
    sys.exit(app.exec())

subwidget.py

from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
from PyQt5 import QtGui as qtg


class SubWidget(qtw.QWidget):
    def __init__(self, name, index):
        super().__init__()
        
        # Variables
        self.name = str(name)
        self.index = index    
           
        # Init Methods
        self.setupUI()        
        
        self.show()
    
    def setupUI(self):
        # Main Layout/Frame
        self.main_layout = qtw.QHBoxLayout()
        self.main_frame = qtw.QFrame()
        self.main_frame_layout = qtw.QVBoxLayout()
        # Set Layouts
        self.setLayout(self.main_layout)
        self.main_layout.addWidget(self.main_frame)
        self.main_frame.setLayout(self.main_frame_layout)
        # Configure QFrame
        self.main_frame.setContentsMargins(5, 5, 5, 5)
        self.main_frame.setStyleSheet("""
                                      border: None;
                                      border-radius: 5px;
                                      background: #000000;
                                      """)               
        # Secondary Layout/Widget        
        self.name_lbl = qtw.QLabel()
        # Configure Seondary Widgets                
        self.name_lbl.setText(self.name)      
        self.name_lbl.setStyleSheet("""
                                    color: #FFFFFF;
                                    """)   
        self.name_lbl.setWordWrap(True)  #         
        # Widget -> Layout
        self.main_frame_layout.addWidget(self.name_lbl, 0, qtc.Qt.AlignmentFlag.AlignHCenter | qtc.Qt.AlignmentFlag.AlignCenter)

这是结果的视觉效果 window/frame。

可随处包装的 QLabel

我实现此目的的方法是子类化 QLabel 并实现 paintEvent,您可以在 drawItemText 时将文本对齐方式设置为 TextWrapAnywhere

这是示例代码。

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QStyleOption, QVBoxLayout, QWidget, QStyle

class SuperQLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super(SuperQLabel, self).__init__(*args, **kwargs)

        self.textalignment = Qt.AlignLeft | Qt.TextWrapAnywhere
        self.isTextLabel = True
        self.align = None

    def paintEvent(self, event):

        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)

        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)

        self.style().drawItemText(painter, self.rect(),
                                  self.textalignment, self.palette(), True, self.text())


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        self.setFixedSize(100, 200)

        self.label = QLabel()
        self.label.setWordWrap(True)
        self.label.setText("1111111111111111111111111111")

        self.slabel = SuperQLabel()
        self.slabel.setText("111111111111111111111111111")

        self.centralwidget = QWidget()
        self.setCentralWidget(self.centralwidget)

        self.mainlayout = QVBoxLayout()
        self.mainlayout.addWidget(self.label)
        self.mainlayout.addWidget(self.slabel)

        self.centralwidget.setLayout(self.mainlayout)


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

结果。

注意:这项工作是用 pyqt5、pyside2、pyside6 测试的,应该也能在 qt5 中工作(在 c++ 中)。