Как в PyQt6 в QTableWidget текст в horizontalHeader расположить вертикально?
Есть большая таблица, названия колонок длинные, получаю их из БД.
Суть в том, что бы названия колонок отображались вертикально, а не горизонтально как по умолчанию.
Как есть:
Как хочу, что бы выгладили заголовки:
Минимальный код для примера:
from PyQt6 import QtWidgets, QtGui, QtCore
from PyQt6.QtCore import Qt
self.tableWidget.setRowCount(5)
self.tableWidget.setColumnCount(7)
self.columnLables = ['', '', 'Кф. товара', 'Выкладка', 'Код блюда', 'Блюдо', 'Категория блюда']
self.tableWidget.setHorizontalHeaderLabels(self.columnLables)
self.font = QtGui.QFont("Times", 10, QFont.Weight.Bold)
self.tableWidget.horizontalHeader().setFont(self.font)
Искал ответ, очень долго искал. Общался с ChatGPT. Решения так и не нашел. Пробовал:
self.tableWidget.horizontalHeader().setOrientation(QtCore.Qt.Vertical)self.tableWidget.horizontalHeader().setOrientation(Qt.Vertical)self.tableWidget.horizontalHeader().setRotation(90)self.tableWidget.horizontalHeader().setStyleSheet( 'QHeaderView::section {''writing-mode: vertical-lr;}')self.tableWidget.horizontalHeader().setStyleSheet( 'QHeaderView::section {''transform: rotate(90deg);}')
Ни одно из решений не помогло.
Ответы (3 шт):
Если ничего не путаю, ориентацию текста в Qt6 так и не добавили, поэтому видимо придется так же как раньше в Qt5 отрисовывать хэдер самому
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtGui, QtCore
class MyHeaderView(QHeaderView):
def __init__(self, parent=None):
super().__init__(QtCore.Qt.Horizontal, parent)
self._font = QtGui.QFont("helvetica", 15)
self._metrics = QtGui.QFontMetrics(self._font)
self._descent = self._metrics.descent()
self._margin = 10
def paintSection(self, painter, rect, index):
# Если надо рамочку, то рисовать тут же
data = self._get_data(index)
painter.rotate(-90)
painter.setFont(self._font)
painter.drawText(- rect.height() + self._margin,
rect.left() + (rect.width() + self._descent) / 2, data)
def sizeHint(self):
return QtCore.QSize(0, self._get_text_width() + 2 * self._margin)
def _get_text_width(self):
return max([self._metrics.width(self._get_data(i))
for i in range(0, self.model().columnCount())])
def _get_data(self, index):
return self.model().headerData(index, self.orientation())
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.central_widget = QWidget(self)
self.setCentralWidget(self.central_widget)
self.layout_main_window = QVBoxLayout()
self.central_widget.setLayout(self.layout_main_window)
self.layout_table = QHBoxLayout()
self.table = QTableWidget()
self.table.setColumnCount(3)
# заменяем хэдер
self.headerView = MyHeaderView()
self.table.setHorizontalHeader(self.headerView)
# ^^^^^^^^^^^^^^^
self.table.setHorizontalHeaderLabels(['первый', 'второй', 'третий'])
self.layout_table.addWidget(self.table)
self.layout_main_window.addLayout(self.layout_table)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
Как вариант:
import sys
from math import sqrt, sin, acos, hypot, degrees, radians
from PyQt5 import QtCore, QtGui, QtWidgets
class AngledHeader(QtWidgets.QHeaderView):
borderPen = QtGui.QColor(0, 190, 255)
labelBrush = QtGui.QColor(255, 212, 0)
def __init__(self, parent=None):
QtWidgets.QHeaderView.__init__(self, QtCore.Qt.Horizontal, parent)
self.setSectionResizeMode(self.Fixed)
self.setDefaultSectionSize(sqrt((self.fontMetrics().height() + 4)** 2 *2))
self.setSectionsClickable(True)
def sizeHint(self):
fm = self.fontMetrics()
width = minSize = self.defaultSectionSize()
count = self.count()
for s in range(count):
if self.isSectionHidden(s):
continue
rect = fm.boundingRect(str(self.model().headerData(s, QtCore.Qt.Horizontal)) + ' ')
diag = hypot(rect.width(), rect.height())
angle = degrees(acos((diag ** 2 + rect.width() ** 2 - rect.height() ** 2) / (2. * diag * rect.width())))
minSize = max(minSize, sin(radians(angle + 45)) * diag)
hint = QtCore.QSize(width * count + 2000, minSize)
return hint
def mousePressEvent(self, event):
width = self.defaultSectionSize()
first = self.sectionViewportPosition(0)
rect = QtCore.QRect(0, 0, width, -self.height())
transform = QtGui.QTransform().translate(0, self.height()).shear(-1, 0)
for s in range(self.count()):
if self.isSectionHidden(s):
continue
if transform.mapToPolygon(rect.translated(s * width + first, 0)).containsPoint(event.pos(), QtCore.Qt.WindingFill):
self.sectionPressed.emit(s)
return
def paintEvent(self, event):
qp = QtGui.QPainter(self.viewport())
qp.setRenderHints(qp.Antialiasing)
width = self.defaultSectionSize()
delta = self.height()
qp.translate(self.sectionViewportPosition(0) - .5, -.5)
fmDelta = (self.fontMetrics().height() - self.fontMetrics().descent()) * .5
rect = QtCore.QRectF(0, 0, width, -delta)
for s in range(self.count()):
if self.isSectionHidden(s):
continue
qp.save()
qp.save()
qp.setPen(self.borderPen)
qp.setTransform(qp.transform().translate(s * width, delta).shear(-1, 0))
qp.drawRect(rect)
qp.setPen(QtCore.Qt.NoPen)
qp.setBrush(self.labelBrush)
qp.drawRect(rect.adjusted(2, -2, -2, 2))
qp.restore()
qp.translate(s * width + width, delta)
qp.rotate(-45)
qp.drawText(0, -fmDelta, str(self.model().headerData(s, QtCore.Qt.Horizontal)))
qp.restore()
class AngledTable(QtWidgets.QTableView):
def __init__(self, *args, **kwargs):
QtWidgets.QTableView.__init__(self, *args, **kwargs)
self.setHorizontalHeader(AngledHeader(self))
self.fixLock = False
def setModel(self, model):
if self.model():
self.model().headerDataChanged.disconnect(self.fixViewport)
QtWidgets.QTableView.setModel(self, model)
model.headerDataChanged.connect(self.fixViewport)
def fixViewport(self):
if self.fixLock:
return
self.fixLock = True
QtCore.QTimer.singleShot(0, self.delayedFixViewport)
def delayedFixViewport(self):
QtWidgets.QApplication.processEvents()
header = self.horizontalHeader()
bar = self.horizontalScrollBar()
bar.blockSignals(True)
step = bar.singleStep() * (header.height() / header.defaultSectionSize())
bar.setMaximum(bar.maximum() + step)
bar.blockSignals(False)
self.fixLock = False
def resizeEvent(self, event):
QtWidgets.QTableView.resizeEvent(self, event)
self.fixViewport()
class TestWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
l = QtWidgets.QGridLayout()
self.setLayout(l)
self.table = AngledTable()
l.addWidget(self.table)
model = QtGui.QStandardItemModel(5, 7)
self.table.setModel(model)
self.table.setHorizontalScrollMode(self.table.ScrollPerPixel)
model.setVerticalHeaderLabels(
['Позиция 1', 'Позиция 2', 'Позиция 3', 'Позиция 4', 'Позиция 5'])
model.setHorizontalHeaderLabels(
['', '', 'Кф. товара', 'Выкладка', 'Код блюда', 'Блюдо',
'Категория блюда']
)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = TestWidget()
w.resize(400, 300)
w.show()
sys.exit(app.exec_())
class MyHeaderView(QHeaderView):
def __init__(self, parent=None):
super().__init__(QtCore.Qt.Orientation.Horizontal, parent)
self._font = QtGui.QFont("helvetica", 15)
self._metrics = QtGui.QFontMetrics(self._font)
self._descent = self._metrics.descent()
self._margin = 10
def paintSection(self, painter, rect, index):
# Если надо рамочку, то рисовать тут же
data = self._get_data(index)
painter.rotate(-90)
painter.setFont(self._font)
painter.drawText(
QtCore.QPointF(- rect.height() + self._margin, rect.left() + (rect.width() + self._descent) / 2), data)
def sizeHint(self):
return QtCore.QSize(0, self._get_text_width() + 2 * self._margin)
def _get_text_width(self):
return max([self._metrics.boundingRect(self._get_data(i)).width() for i in range(0, self.model().columnCount())])
def _get_data(self, index):
return self.model().headerData(index, self.orientation())
Вот так вот заработало!


