Here is what I was finally able to come up with. It puts the focus rectangle on the border of each cell, regardless of "padding" or "margin". It also preserves the stylesheet. At least it preserves background color and padding. I didn't test all stylesheet options. However, it does not preserve the text color in the cell with focus. (P.S. This is working with PySide 1.1.1)
class CellDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent):
super(CellDelegate, self).__init__(parent)
self._parent = parent
def paint(self, qPainter, option, qModelIndex):
v4Option = QtGui.QStyleOptionViewItemV4(option)
v4Option.index = qModelIndex
value = qModelIndex.data()
v4Option.text = str(value)
style = self._parent.style()
if (v4Option.state & QtGui.QStyle.State_HasFocus):
# --- The table cell with focus
# Draw the background
style.drawPrimitive(style.PE_PanelItemViewItem, v4Option, qPainter, self._parent)
# Draw the text
subRect = style.subElementRect(style.SE_ItemViewItemText, v4Option, self._parent)
alignment = qModelIndex.data(QtCore.Qt.TextAlignmentRole)
if not alignment:
alignment = int(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
if (v4Option.state & QtGui.QStyle.State_Enabled):
itemEnabled = True
else:
itemEnabled = False
textRect = style.itemTextRect(v4Option.fontMetrics, subRect, alignment, itemEnabled, value)
style.drawItemText(qPainter, textRect, alignment, v4Option.palette, v4Option.state, value)
# Draw the focus rectangle
focusOption = QtGui.QStyleOptionFocusRect()
focusOption.rect = v4Option.rect
style.drawPrimitive(style.PE_FrameFocusRect, focusOption, qPainter, self._parent)
else:
# --- All other table cells
style.drawControl(style.CE_ItemViewItem, v4Option, qPainter, self._parent)
Here is some example code showing how to use it. The goal is that the stylesheet would be set in a .ui file. This is just a self-contained example:
class TestTableModel(QtCore.QAbstractTableModel):
headerNames = ('Column 1', 'Column 2')
def __init__(self):
super(TestTableModel, self).__init__()
self._data = [['test', 'text'], ['yyy', 'zzz']]
#----- Overridden Functions ------------------------------------------------
def columnCount(self, parentIndex):
return len(self.headerNames)
def data(self, qModelIndex, role=QtCore.Qt.DisplayRole):
if qModelIndex.isValid():
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
dataItem = self._data[qModelIndex.row()][qModelIndex.column()]
return dataItem
return None
def headerData(self, colNum, orientation, role):
if (orientation == QtCore.Qt.Horizontal) and (role == QtCore.Qt.DisplayRole):
return self.headerNames[colNum]
return None
def rowCount(self, parentIndex=QtCore.QModelIndex()):
return len(self._data)
#------------------------------------------------------------------------------
class TestTableViewSpacing(QtGui.QMainWindow):
def __init__(self, parent=None):
super(TestTableViewSpacing, self).__init__(parent)
self.tableView = QtGui.QTableView()
self.setCentralWidget(self.tableView)
tableModel = TestTableModel()
self.tableView.setModel(tableModel)
self.tableView.setStyleSheet('QTableView::item {border: 0px; padding: 5px; margin: 5px; color: yellow; '
'background-color: QLinearGradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #330055, stop: 1 #000000);} '
'QTableView::item:focus {border: 0px; background-color: darkred; color: yellow;}')
# VERY IMPORTANT!! Must pass the table view to the delegate, or it will not work!
newDelegate = CellDelegate(self.tableView)
self.tableView.setItemDelegate(newDelegate)