qtableview单元格中的意外填充pyqt
Unexpected padding pyqt in qtableview cell
我正在尝试为 QTableView
创建自定义 TableModel
class。包含 1
作为数据的单元格必须有红色轮廓。大纲是通过 returning 来自 TableModel
的像素图(带有红色边框和在顶部绘制的文本)而不是 returning 简单字符串来制作的。
问题出在像素图的意外填充中,我 return 为 DecorationRole
。我在 return pixmap
行之前检查了像素图是否正确绘制(它实际上是 21x21 像素,轮廓做得很好,没有填充,正如计划的那样)。
这是右侧绘制的像素图,它是在 return
来自 TableModel 之前保存的:
最终,某些东西将 returned 像素图从 QTableView
单元格的左边界移动了 3px。我没有在 QtDesigner 中为 QTableView
设置任何填充,以后也没有在我的代码中更改它。我还尝试使用样式表手动将填充设置为零,但没有给出不同的结果。
有什么解决办法吗?谢谢。
这是我的示例 TableModel
:
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, topology=None):
super().__init__()
...
# Hardcode cell size and path to rectangle image
self.cell_width, self.cell_height = 21, 21
self.fpath_red_rect = './path/to/red_rect.png'
def rowCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[0]
def columnCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[1]
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
...
def size(self):
return QtCore.QSize((self.columnCount() + 1) * self.cell_width,
(self.rowCount() + 1) * self.cell_height)
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
i = index.row()
j = index.column()
if role == QtCore.Qt.DisplayRole:
if self.data[i, j] == 0: # empty
return ''
elif self.data[i, j] == 1: # cell with red rectangle
# the text will be drawn on pixmap manually later
return None
else:
return '{0}'.format(self.data[i, j]) # display default data
if role == QtCore.Qt.DecorationRole:
# Create pixmap, draw the rectangle on it and then draw text on top
pixmap = QtGui.QPixmap(self.cell_width, self.cell_height)
image = QtGui.QImage(self.fpath_red_rect).scaled(self.cell_width, self.cell_height)
painter = QtGui.QPainter(pixmap)
painter.drawImage(pixmap.rect().topLeft(), image)
painter.drawText(pixmap.rect(), QtCore.Qt.AlignCenter, '{0}'.format(self.data[i, j]))
painter.end()
# If we save the pixmap to PNG image here (see the link above),
# we get the expected 21 x 21 px image, with nice
# and properly drawn rectangle and centered text.
# But something goes wrong after returning
return pixmap
if role == QtCore.Qt.BackgroundRole:
return QtGui.QBrush(self.getQtColor(self.data[i, j]))
if role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignCenter
return QtCore.QVariant()
DecorationRole
用于绘制图标,因为你观察到位移,在你的情况下你不应该使用那个角色,此外绘画任务不应该在模型中完成,因为他只需要提供数据,如果你想修改绘图,更好的选择是使用委托,如下所示:
import sys
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
ValueRole = QtCore.Qt.UserRole + 1
max_val = 4
colors = [QtGui.QColor(*np.random.randint(255, size=3)) for i in range(max_val)]
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.data = np.random.randint(max_val, size=(10, 10))
def rowCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[0]
def columnCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[1]
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
i = index.row()
j = index.column()
val = self.data[i, j]
if role == QtCore.Qt.DisplayRole:
return str(val)
elif role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignCenter
elif role == QtCore.Qt.BackgroundRole:
return colors[val]
if role == ValueRole:
return val
return QtCore.QVariant()
class Delegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
if index.data(ValueRole) == 1:
painter.save()
pen = painter.pen()
pen.setColor(QtCore.Qt.red)
painter.setPen(pen)
r = QtCore.QRect(option.rect)
r.adjust(0, 0, -pen.width(), -pen.width())
painter.drawRect(r)
painter.restore()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QTableView()
w.setItemDelegate(Delegate(w))
model = TableModel()
w.setModel(model)
w.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
for i in range(model.rowCount()):
w.verticalHeader().resizeSection(i, 21)
for j in range(model.columnCount()):
w.horizontalHeader().resizeSection(j, 21)
w.show()
sys.exit(app.exec_())
我正在尝试为 QTableView
创建自定义 TableModel
class。包含 1
作为数据的单元格必须有红色轮廓。大纲是通过 returning 来自 TableModel
的像素图(带有红色边框和在顶部绘制的文本)而不是 returning 简单字符串来制作的。
问题出在像素图的意外填充中,我 return 为 DecorationRole
。我在 return pixmap
行之前检查了像素图是否正确绘制(它实际上是 21x21 像素,轮廓做得很好,没有填充,正如计划的那样)。
这是右侧绘制的像素图,它是在 return
来自 TableModel 之前保存的:
最终,某些东西将 returned 像素图从 QTableView
单元格的左边界移动了 3px。我没有在 QtDesigner 中为 QTableView
设置任何填充,以后也没有在我的代码中更改它。我还尝试使用样式表手动将填充设置为零,但没有给出不同的结果。
有什么解决办法吗?谢谢。
这是我的示例 TableModel
:
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, topology=None):
super().__init__()
...
# Hardcode cell size and path to rectangle image
self.cell_width, self.cell_height = 21, 21
self.fpath_red_rect = './path/to/red_rect.png'
def rowCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[0]
def columnCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[1]
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
...
def size(self):
return QtCore.QSize((self.columnCount() + 1) * self.cell_width,
(self.rowCount() + 1) * self.cell_height)
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
i = index.row()
j = index.column()
if role == QtCore.Qt.DisplayRole:
if self.data[i, j] == 0: # empty
return ''
elif self.data[i, j] == 1: # cell with red rectangle
# the text will be drawn on pixmap manually later
return None
else:
return '{0}'.format(self.data[i, j]) # display default data
if role == QtCore.Qt.DecorationRole:
# Create pixmap, draw the rectangle on it and then draw text on top
pixmap = QtGui.QPixmap(self.cell_width, self.cell_height)
image = QtGui.QImage(self.fpath_red_rect).scaled(self.cell_width, self.cell_height)
painter = QtGui.QPainter(pixmap)
painter.drawImage(pixmap.rect().topLeft(), image)
painter.drawText(pixmap.rect(), QtCore.Qt.AlignCenter, '{0}'.format(self.data[i, j]))
painter.end()
# If we save the pixmap to PNG image here (see the link above),
# we get the expected 21 x 21 px image, with nice
# and properly drawn rectangle and centered text.
# But something goes wrong after returning
return pixmap
if role == QtCore.Qt.BackgroundRole:
return QtGui.QBrush(self.getQtColor(self.data[i, j]))
if role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignCenter
return QtCore.QVariant()
DecorationRole
用于绘制图标,因为你观察到位移,在你的情况下你不应该使用那个角色,此外绘画任务不应该在模型中完成,因为他只需要提供数据,如果你想修改绘图,更好的选择是使用委托,如下所示:
import sys
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
ValueRole = QtCore.Qt.UserRole + 1
max_val = 4
colors = [QtGui.QColor(*np.random.randint(255, size=3)) for i in range(max_val)]
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.data = np.random.randint(max_val, size=(10, 10))
def rowCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[0]
def columnCount(self, parent=QtCore.QModelIndex()):
return self.data.shape[1]
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid():
return QtCore.QVariant()
i = index.row()
j = index.column()
val = self.data[i, j]
if role == QtCore.Qt.DisplayRole:
return str(val)
elif role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignCenter
elif role == QtCore.Qt.BackgroundRole:
return colors[val]
if role == ValueRole:
return val
return QtCore.QVariant()
class Delegate(QtWidgets.QStyledItemDelegate):
def paint(self, painter, option, index):
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
if index.data(ValueRole) == 1:
painter.save()
pen = painter.pen()
pen.setColor(QtCore.Qt.red)
painter.setPen(pen)
r = QtCore.QRect(option.rect)
r.adjust(0, 0, -pen.width(), -pen.width())
painter.drawRect(r)
painter.restore()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QTableView()
w.setItemDelegate(Delegate(w))
model = TableModel()
w.setModel(model)
w.verticalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
w.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Fixed)
for i in range(model.rowCount()):
w.verticalHeader().resizeSection(i, 21)
for j in range(model.columnCount()):
w.horizontalHeader().resizeSection(j, 21)
w.show()
sys.exit(app.exec_())