如何在 PyQt5 Python 中自动换行 QTableWidget 的 header 内容
How to word wrap the header contents of QTableWidget in PyQt5 Python
我正在 PyQt5
工作,我有一个 QTableWidget
。它有一个 header 列,我想自动换行。下面是 table 的样子:
我们可以看到 header 标签像 Maximum Variation Coefficient
有 3 个词,因此它占用了太多的列宽。怎样才能把header.
中的单词换行
代码如下:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
# Main Window
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 - QTableWidget'
self.left = 0
self.top = 0
self.width = 300
self.height = 200
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createTable()
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
# Show window
self.show()
# Create table
def createTable(self):
self.tableWidget = QTableWidget()
# Row count
self.tableWidget.setRowCount(3)
# Column count
self.tableWidget.setColumnCount(2)
self.tableWidget.setHorizontalHeaderLabels(["Maximum Variation Coefficient", "Maximum Variation Coefficient"])
self.tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.tableWidget.setItem(0, 0, QTableWidgetItem("3.44"))
self.tableWidget.setItem(0, 1, QTableWidgetItem("5.3"))
self.tableWidget.setItem(1, 0, QTableWidgetItem("4.6"))
self.tableWidget.setItem(1, 1, QTableWidgetItem("1.2"))
self.tableWidget.setItem(2, 0, QTableWidgetItem("2.2"))
self.tableWidget.setItem(2, 1, QTableWidgetItem("4.4"))
# Table will fit the screen horizontally
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
我尝试添加这个 self.tableWidget.setWordWrap(True)
但这没有做任何改变。任何人都可以提供一些好的解决方案。请帮忙。谢谢
编辑:
也试过这个:
self.tableWidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignHCenter | Qt.Alignment(QtCore.Qt.TextWordWrap))
但是没用
为了实现你所需要的,你必须自己设置header并进行以下两个假设:
- header 必须根据节内容提供正确的尺寸提示 高度,以防列的 宽度不够;
- 文字对齐必须包含
QtCore.Qt.TextWordWrap
标志,让画家知道它可以换行;
请注意,虽然第二个方面在某些情况下可能就足够了(因为 header 通常足够高以适合至少两行),但第一个方面是强制性的,因为文本可能需要更多垂直space,否则部分文字会被截掉。
第一点需要继承QHeaderView并重新实现sectionSizeFromContents()
:
class WrapHeader(QtWidgets.QHeaderView):
def sectionSizeFromContents(self, logicalIndex):
# get the size returned by the default implementation
size = super().sectionSizeFromContents(logicalIndex)
if self.model():
if size.width() > self.sectionSize(logicalIndex):
text = self.model().headerData(logicalIndex,
self.orientation(), QtCore.Qt.DisplayRole)
if not text:
return size
# in case the display role is numeric (for example, when header
# labels are not defined yet), convert it to a string;
text = str(text)
option = QtWidgets.QStyleOptionHeader()
self.initStyleOption(option)
alignment = self.model().headerData(logicalIndex,
self.orientation(), QtCore.Qt.TextAlignmentRole)
if alignment is None:
alignment = option.textAlignment
# get the default style margin for header text and create a
# possible rectangle using the current section size, then use
# QFontMetrics to get the required rectangle for the wrapped text
margin = self.style().pixelMetric(
QtWidgets.QStyle.PM_HeaderMargin, option, self)
maxWidth = self.sectionSize(logicalIndex) - margin * 2
rect = option.fontMetrics.boundingRect(
QtCore.QRect(0, 0, maxWidth, 10000),
alignment | QtCore.Qt.TextWordWrap,
text)
# add vertical margins to the resulting height
height = rect.height() + margin * 2
if height >= size.height():
# if the height is bigger than the one provided by the base
# implementation, return a new size based on the text rect
return QtCore.QSize(rect.width(), height)
return size
class App(QWidget):
# ...
def createTable(self):
self.tableWidget = QTableWidget()
self.tableWidget.setHorizontalHeader(
WrapHeader(QtCore.Qt.Horizontal, self.tableWidget))
# ...
然后,要设置自动换行标志,有两种选择:
- 在基础模型上为每个现有列设置对齐标志
setHeaderData()
:
# ...
model = self.tableWidget.model()
default = self.tableWidget.horizontalHeader().defaultAlignment()
default |= QtCore.Qt.TextWordWrap
for col in range(self.tableWidget.columnCount()):
alignment = model.headerData(
col, QtCore.Qt.Horizontal, QtCore.Qt.TextAlignmentRole)
if alignment:
alignment |= QtCore.Qt.TextWordWrap
else:
alignment = default
model.setHeaderData(
col, QtCore.Qt.Horizontal, alignment, QtCore.Qt.TextAlignmentRole)
- 通过在选项上应用标志,使用 QProxyStyle 覆盖 header 的绘制:
# ...
class ProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, control, option, painter, widget=None):
if control in (self.CE_Header, self.CE_HeaderLabel):
option.textAlignment |= QtCore.Qt.TextWordWrap
super().drawControl(control, option, painter, widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle(ProxyStyle())
ex = App()
sys.exit(app.exec_())
最后,考虑一下:
将 setSectionResizeMode
与 ResizeToContents
或 Stretch
以及 setStretchLastSection
一起使用将始终导致 table 尝试用作很多 space 是 header 第一次展示时所要求的;
默认情况下,QHeaderView 部分 不可点击(这是排序的强制要求)并且 highlightSections
属性 是还有False
; QTableView 和 QTableWidget 都使用这些值创建它们的 headers True
,因此当设置新的 header 时,如果需要排序和突出显示,您必须显式更改这些方面:
self.tableWidget.setHorizontalHeader(
WrapHeader(QtCore.Qt.Horizontal, self.tableWidget))
self.tableWidget.horizontalHeader().setSectionsClickable(True)
self.tableWidget.horizontalHeader().setHighlightSections(True)
排序和部分突出显示都会产生一些问题,因为排序指示器需要进一步水平 space 并且突出显示的部分通常显示为 粗体 字体(但在按下鼠标时正常显示);所有这些都可能会产生一些闪烁和奇怪的行为;不幸的是,这些问题没有明显的解决方案,但是当使用 QProxyStyle 时,可以通过覆盖字体样式来避免一些闪烁:
def drawControl(self, control, option, painter, widget=None):
if control in (self.CE_Header, self.CE_HeaderLabel):
option.textAlignment |= QtCore.Qt.TextWordWrap
if option.state & self.State_Sunken:
option.state |= self.State_On
super().drawControl(control, option, painter, widget)
我正在 PyQt5
工作,我有一个 QTableWidget
。它有一个 header 列,我想自动换行。下面是 table 的样子:
我们可以看到 header 标签像 Maximum Variation Coefficient
有 3 个词,因此它占用了太多的列宽。怎样才能把header.
代码如下:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
# Main Window
class App(QWidget):
def __init__(self):
super().__init__()
self.title = 'PyQt5 - QTableWidget'
self.left = 0
self.top = 0
self.width = 300
self.height = 200
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.createTable()
self.layout = QVBoxLayout()
self.layout.addWidget(self.tableWidget)
self.setLayout(self.layout)
# Show window
self.show()
# Create table
def createTable(self):
self.tableWidget = QTableWidget()
# Row count
self.tableWidget.setRowCount(3)
# Column count
self.tableWidget.setColumnCount(2)
self.tableWidget.setHorizontalHeaderLabels(["Maximum Variation Coefficient", "Maximum Variation Coefficient"])
self.tableWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.tableWidget.setItem(0, 0, QTableWidgetItem("3.44"))
self.tableWidget.setItem(0, 1, QTableWidgetItem("5.3"))
self.tableWidget.setItem(1, 0, QTableWidgetItem("4.6"))
self.tableWidget.setItem(1, 1, QTableWidgetItem("1.2"))
self.tableWidget.setItem(2, 0, QTableWidgetItem("2.2"))
self.tableWidget.setItem(2, 1, QTableWidgetItem("4.4"))
# Table will fit the screen horizontally
self.tableWidget.horizontalHeader().setStretchLastSection(True)
self.tableWidget.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
我尝试添加这个 self.tableWidget.setWordWrap(True)
但这没有做任何改变。任何人都可以提供一些好的解决方案。请帮忙。谢谢
编辑:
也试过这个:
self.tableWidget.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignHCenter | Qt.Alignment(QtCore.Qt.TextWordWrap))
但是没用
为了实现你所需要的,你必须自己设置header并进行以下两个假设:
- header 必须根据节内容提供正确的尺寸提示 高度,以防列的 宽度不够;
- 文字对齐必须包含
QtCore.Qt.TextWordWrap
标志,让画家知道它可以换行;
请注意,虽然第二个方面在某些情况下可能就足够了(因为 header 通常足够高以适合至少两行),但第一个方面是强制性的,因为文本可能需要更多垂直space,否则部分文字会被截掉。
第一点需要继承QHeaderView并重新实现sectionSizeFromContents()
:
class WrapHeader(QtWidgets.QHeaderView):
def sectionSizeFromContents(self, logicalIndex):
# get the size returned by the default implementation
size = super().sectionSizeFromContents(logicalIndex)
if self.model():
if size.width() > self.sectionSize(logicalIndex):
text = self.model().headerData(logicalIndex,
self.orientation(), QtCore.Qt.DisplayRole)
if not text:
return size
# in case the display role is numeric (for example, when header
# labels are not defined yet), convert it to a string;
text = str(text)
option = QtWidgets.QStyleOptionHeader()
self.initStyleOption(option)
alignment = self.model().headerData(logicalIndex,
self.orientation(), QtCore.Qt.TextAlignmentRole)
if alignment is None:
alignment = option.textAlignment
# get the default style margin for header text and create a
# possible rectangle using the current section size, then use
# QFontMetrics to get the required rectangle for the wrapped text
margin = self.style().pixelMetric(
QtWidgets.QStyle.PM_HeaderMargin, option, self)
maxWidth = self.sectionSize(logicalIndex) - margin * 2
rect = option.fontMetrics.boundingRect(
QtCore.QRect(0, 0, maxWidth, 10000),
alignment | QtCore.Qt.TextWordWrap,
text)
# add vertical margins to the resulting height
height = rect.height() + margin * 2
if height >= size.height():
# if the height is bigger than the one provided by the base
# implementation, return a new size based on the text rect
return QtCore.QSize(rect.width(), height)
return size
class App(QWidget):
# ...
def createTable(self):
self.tableWidget = QTableWidget()
self.tableWidget.setHorizontalHeader(
WrapHeader(QtCore.Qt.Horizontal, self.tableWidget))
# ...
然后,要设置自动换行标志,有两种选择:
- 在基础模型上为每个现有列设置对齐标志
setHeaderData()
:
# ...
model = self.tableWidget.model()
default = self.tableWidget.horizontalHeader().defaultAlignment()
default |= QtCore.Qt.TextWordWrap
for col in range(self.tableWidget.columnCount()):
alignment = model.headerData(
col, QtCore.Qt.Horizontal, QtCore.Qt.TextAlignmentRole)
if alignment:
alignment |= QtCore.Qt.TextWordWrap
else:
alignment = default
model.setHeaderData(
col, QtCore.Qt.Horizontal, alignment, QtCore.Qt.TextAlignmentRole)
- 通过在选项上应用标志,使用 QProxyStyle 覆盖 header 的绘制:
# ...
class ProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, control, option, painter, widget=None):
if control in (self.CE_Header, self.CE_HeaderLabel):
option.textAlignment |= QtCore.Qt.TextWordWrap
super().drawControl(control, option, painter, widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyle(ProxyStyle())
ex = App()
sys.exit(app.exec_())
最后,考虑一下:
将
setSectionResizeMode
与ResizeToContents
或Stretch
以及setStretchLastSection
一起使用将始终导致 table 尝试用作很多 space 是 header 第一次展示时所要求的;默认情况下,QHeaderView 部分 不可点击(这是排序的强制要求)并且
highlightSections
属性 是还有False
; QTableView 和 QTableWidget 都使用这些值创建它们的 headersTrue
,因此当设置新的 header 时,如果需要排序和突出显示,您必须显式更改这些方面:self.tableWidget.setHorizontalHeader( WrapHeader(QtCore.Qt.Horizontal, self.tableWidget)) self.tableWidget.horizontalHeader().setSectionsClickable(True) self.tableWidget.horizontalHeader().setHighlightSections(True)
排序和部分突出显示都会产生一些问题,因为排序指示器需要进一步水平 space 并且突出显示的部分通常显示为 粗体 字体(但在按下鼠标时正常显示);所有这些都可能会产生一些闪烁和奇怪的行为;不幸的是,这些问题没有明显的解决方案,但是当使用 QProxyStyle 时,可以通过覆盖字体样式来避免一些闪烁:
def drawControl(self, control, option, painter, widget=None): if control in (self.CE_Header, self.CE_HeaderLabel): option.textAlignment |= QtCore.Qt.TextWordWrap if option.state & self.State_Sunken: option.state |= self.State_On super().drawControl(control, option, painter, widget)