带有 setWordWrap(True) 的 QLabel 在放置在 QListWidget 中时不会垂直调整大小,即使 resizeMode 已设置为 Adjust
Posted
技术标签:
【中文标题】带有 setWordWrap(True) 的 QLabel 在放置在 QListWidget 中时不会垂直调整大小,即使 resizeMode 已设置为 Adjust【英文标题】:QLabels with setWordWrap(True) are not resizing vertically when placed in a QListWidget, even though resizeMode has been set to Adjust 【发布时间】:2021-04-07 21:50:13 【问题描述】:如何将QLabel
s 和setWordWrap(True)
放在QListWidget
/QListView
内,以便在调整父窗口小部件大小时正确调整QLabel
s 的大小?
当我尝试这样做时,我遇到了 QLabels 没有得到正确高度的问题(参见下面的示例 2)
我正在使用
Python:3.8.5 PyQt5:5.15.2示例 1:使用 QVBoxLayout
为了展示我想要完成的事情,我想先展示一个示例,我只是将QLabel
s 添加到 QVBoxLayout
这如我所愿:如果我水平调整(主)窗口的大小,带有环绕文本的QLabel
将在垂直方向占用更多空间
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumHeight(150)
self.main_widget = QWidget()
self.setCentralWidget(self.main_widget)
vbox = QVBoxLayout()
self.main_widget.setLayout(vbox)
label_1 = QLabel("label_1")
vbox.addWidget(label_1)
label_1.setStyleSheet("*background-color: #f0f000;")
label_2 = QLabel("[wrapped] label_2 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
vbox.addWidget(label_2)
label_2.setStyleSheet("*background-color: #00f0f0;")
label_2.setWordWrap(True)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
示例 2:使用QListWidget
这是我遇到问题的代码:运行此代码不会为带有包装文本的 Qlabel
s 提供任何额外空间
(在示例中使用QListWidget
而不是QListView
,但据我所见 - 以及我对这些类的理解 - 应该没有区别)
请注意resizeMode property 已设置为QListView.Adjust
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.list_widget = QListWidget()
self.setCentralWidget(self.list_widget)
self.list_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.list_widget.setResizeMode(QListView.Adjust) # <-----
self.list_widget.setUniformItemSizes(False) # -should already be false, but just in case
self.list_widget.setWordWrap(True) # -AFAIK this should only effect text that is put directly into QListWidgetItems, but just in case
item_1_text_str = f"Item number 1"
lwi_item_1 = QListWidgetItem(self.list_widget)
self.list_widget.addItem(lwi_item_1)
item_1_widget_qlabel = QLabel(item_1_text_str)
item_1_widget_qlabel.setStyleSheet("*background-color: #f0f000;")
self.list_widget.setItemWidget(lwi_item_1, item_1_widget_qlabel)
item_2_text_str = "[wrapped] label_2 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
lwi_item_2 = QListWidgetItem(self.list_widget)
self.list_widget.addItem(lwi_item_2)
item_widget_2_qlabel = QLabel(item_2_text_str)
item_widget_2_qlabel.setWordWrap(True) # <-------
item_widget_2_qlabel.setStyleSheet("*background-color: #00f0f0;")
self.list_widget.setItemWidget(lwi_item_2, item_widget_2_qlabel)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
我试过的
将大小策略设置为Expanding
通常策略设置为MinimumExpanding
,所以我认为Expanding
会更好,因为Expanding
包含Shrink
标志
在上面的代码示例 2 中,它看起来像这样:
item_widget_2_qlabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
这并没有帮助
使用QLabel
中的sizeHint
设置QListWidgetItem 的大小提示
如果在__init__
完成:
lwi_item_2.setSizeHint(item_widget_2_qlabel.sizeHint())
这样做的问题是它只会设置一次大小提示,然后sizeHint会卡住,所以带有包裹文本的QLabel不能垂直扩展或收缩
或者使用此代码,以便在调整主窗口大小时更新 sizeHint:
class MainWindow(QMainWindow):
[...]
def resizeEvent(self, a0: QResizeEvent) -> None:
super().resizeEvent(a0)
row = 0
while row < self.list_widget.count():
lwi_item = self.list_widget.item(row)
item_widget = self.list_widget.itemWidget(lwi_item)
widget_size_hint = item_widget.sizeHint()
lwi_item.setSizeHint(widget_size_hint) # <--------
row += 1
令人惊讶的是,使用此代码时的结果是相同的:QLabel 的垂直大小没有改变
【问题讨论】:
为什么需要使用 QListWidget 和 QLabels? @musicamante 嗨,谢谢你的问题。我在我正在处理的几个项目中使用QListWidget
/QListView
,我想放QLabel
的一个典型原因是因为我有一个自定义行/项目小部件(使用setItemWidget
添加)持有两个或三个其他小部件,其中一个是 QLabel
,带有我想要包装的文本 --- 我愿意使用其他方法,所以如果你知道好的替代方法,请分享!
好吧,QLabel 有点奇怪,因为它是唯一一个拥有自己处理尺寸方式的小部件,因为它能够根据可用空间调整其内容(并且,部分,反之亦然)。鉴于您所描述的,我可以理解这种方法的必要性,并且您的回答中提出的解决方案对我来说似乎可以接受:问题的 resizeEvent 实现不起作用的原因是标签返回的 sizeHint 是基于水平列表视图在映射后立即提供的可用空间。
@musicamante 好的,这是有道理的,感谢您抽出宝贵时间发表评论和解释。我已经使用 PyQt 几年了,但现在我开始真正学习它是第一次。让我感到困惑的一件事是setResizeMode(QListView.Adjust)
似乎没有做任何事情,那么这是否仅对其他类型的小部件有效? doc.qt.io/qt-5/qlistview.html#resizeMode-prop
【参考方案1】:
在写这个问题时,我能够在 QListWidget
子类的覆盖 resizeEvent
中使用 QLabel.heightForWidth
找到解决方案
这不是我所希望的(我曾希望我错过了一些简单的东西,以便解决方案更加优雅)。 如果有人有比这更好的解决方案,请分享!
这里是我用来让它工作的代码
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ListWidget(QListWidget):
def __init__(self):
super().__init__()
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
"""
self.list_widget.setResizeMode(QListView.Adjust)
-this is not needed with this solution. (Maybe because it only effects QListWidgetItems?)
"""
def resizeEvent(self, a0: QResizeEvent) -> None:
super().resizeEvent(a0)
row = 0
while row < self.count():
lwi_item = self.item(row)
item_widget = self.itemWidget(lwi_item)
if isinstance(item_widget, QLabel) and hasattr(item_widget, "wordWrap") and item_widget.wordWrap():
width_int = self.width() - self.contentsMargins().left() - self.contentsMargins().right()
height_hfw_int = item_widget.heightForWidth(width_int) # <--------
widget_size_hint = QSize(width_int, height_hfw_int)
lwi_item.setSizeHint(widget_size_hint)
row += 1
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.list_widget = ListWidget()
self.setCentralWidget(self.list_widget)
lwi_item_1 = QListWidgetItem(self.list_widget)
self.list_widget.addItem(lwi_item_1)
item_1_widget_qlabel = QLabel("Item number 1")
item_1_widget_qlabel.setStyleSheet("*background-color: #f0f000;")
self.list_widget.setItemWidget(lwi_item_1, item_1_widget_qlabel)
lwi_item_2 = QListWidgetItem(self.list_widget)
self.list_widget.addItem(lwi_item_2)
item_2_html_rich_str = "<span style='font-size:12pt'>Listen, strange women lyin in ponds distributin swords is no basis for a system of government.</span><br><span>Supreme executive power derives from a mandate from the masses, not from some farcical aquatic ceremony.</span>"
item_2_markdown_str = "## Listen, strange women lyin in ponds distributin swords is no basis for a system of government.\n\nSupreme executive power derives from a mandate from the masses, not from some farcical aquatic ceremony."
item_2_text_str = "Listen, strange women lyin in ponds distributin swords is no basis for a system of government.\n\nSupreme executive power derives from a mandate from the masses, not from some farcical aquatic ceremony."
item_widget_2_qlabel = QLabel(item_2_html_rich_str)
item_widget_2_qlabel.setTextFormat(Qt.RichText) # Qt.RichText, Qt.MarkdownText, Qt.PlainText
item_widget_2_qlabel.setWordWrap(True) # <-------
item_widget_2_qlabel.setStyleSheet("*background-color: #00f0f0;")
self.list_widget.setItemWidget(lwi_item_2, item_widget_2_qlabel)
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
【讨论】:
以上是关于带有 setWordWrap(True) 的 QLabel 在放置在 QListWidget 中时不会垂直调整大小,即使 resizeMode 已设置为 Adjust的主要内容,如果未能解决你的问题,请参考以下文章