I display data in a QTableWidget in various number formats, depending on the setting of a combobox (outside QTableWidget). This works nicely by redefining the displayText()
method of an ItemDelegate.
I would also like to style / modify the displayed text depending on its index, however, only the text and the locale are passed as arguments to displayText()
.
As a workaround I also redefined the paint()
method because here the index is passed as well. This seems very clumsy to me and I'm also at the end of the rope when I start styling the text - pure Cargo Cult programming ...
So, am I barking up the wrong tree? Is there a simpler way to format e.g. only the element (0,1) (see code below), maybe within displayText()
?
The following code is just a minimum working example, full code under
https://github.com/chipmuenk/pyFDA/blob/coeff_table/pyfda/input_widgets/filter_coeffs.py
# -*- coding: utf-8 -*-
from __future__ import print_function, division, unicode_literals, absolute_import
#from PyQt4.QtGui import ...
from PyQt5.QtWidgets import (QWidget, QApplication, QTableWidget,
QTableWidgetItem, QVBoxLayout, QStyledItemDelegate)
from PyQt5.QtWidgets import QStyle
from PyQt5.QtGui import QFont
#from PyQt4.QtGui import QStyle, QFont
import numpy as np
class ItemDelegate(QStyledItemDelegate):
"""
The following methods are subclassed to replace display and editor of the
QTableWidget.
"""
def __init__(self, parent):
"""
Pass instance `parent` of parent class (TestTable)
"""
super(ItemDelegate, self).__init__(parent)
self.parent = parent # instance of the parent (not the base) class
def paint(self, painter, option, index):
"""
painter: instance of QPainter
option: instance of QStyleOptionViewItem(V4?)
index: instance of QModelIndex
"""
style_option = option
# read text to be shown:
if index.row() == 0 and index.column() == 1: # always display "1!" at index (0,1)
style_option.text = "1!"
style_option.font.setBold(True)
# now paint the cell
self.parent.style().drawControl(QStyle.CE_ItemViewItem, style_option, painter)
else:
super(ItemDelegate, self).paint(painter, option, index) # default painter
def displayText(self, text, locale):
"""
Display `text` in the selected with the selected number
of digits
text: string / QVariant from QTableWidget to be rendered
locale: locale for the text
"""
data = text # .toString() # Python 2: need to convert to "normal" string
return "{0:>{1}}".format(data, 4)
class TestTable(QWidget):
""" Create widget for viewing / editing / entering data """
def __init__(self, parent):
super(TestTable, self).__init__(parent)
self.bfont = QFont()
self.bfont.setBold(True)
self.tblCoeff = QTableWidget(self)
self.tblCoeff.setItemDelegate(ItemDelegate(self))
layVMain = QVBoxLayout()
layVMain.addWidget(self.tblCoeff)
self.setLayout(layVMain)
self.ba = np.random.randn(3,4) # test data
self._refresh_table()
def _refresh_table(self):
""" (Re-)Create the displayed table from self.ba """
num_cols = 3
num_rows = 4
self.tblCoeff.setRowCount(num_rows)
self.tblCoeff.setColumnCount(num_cols)
for col in range(num_cols):
for row in range(num_rows):
# set table item from self.ba
item = self.tblCoeff.item(row, col)
if item: # does item exist?
item.setText(str(self.ba[col][row]))
else: # no, construct it:
self.tblCoeff.setItem(row,col,QTableWidgetItem(
str(self.ba[col][row])))
self.tblCoeff.resizeColumnsToContents()
self.tblCoeff.resizeRowsToContents()
#------------------------------------------------------------------------------
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainw = TestTable(None)
app.setActiveWindow(mainw)
mainw.show()
sys.exit(app.exec_())
EDIT: =================================================================
@m7913d's suggestion works for me, I'm overriding the initStyleOption() method instead of the paint() method which looks more concise to me. This is the updated code (so I've partially answered my own question):
# -*- coding: utf-8 -*-
from __future__ import print_function, division, unicode_literals, absolute_import
#from PyQt4.QtGui import ...
from PyQt5.QtWidgets import (QWidget, QApplication, QTableWidget,
QTableWidgetItem, QVBoxLayout, QStyledItemDelegate)
from PyQt5.QtWidgets import QStyle
from PyQt5.QtGui import QFont
#from PyQt4.QtGui import QStyle, QFont
import numpy as np
class ItemDelegate(QStyledItemDelegate):
"""
The following methods are subclassed to replace display and editor of the
QTableWidget.
"""
def __init__(self, parent):
"""
Pass instance `parent` of parent class (TestTable)
"""
super(ItemDelegate, self).__init__(parent)
self.parent = parent # instance of the parent (not the base) class
def initStyleOption(self, option, index):
"""
Initialize `option` with the values using the `index` index. When the
item (0,1) is processed, it is styled especially. All other items are
passed to the original `initStyleOption()` which then calls `displayText()`.
"""
if index.row() == 0 and index.column() == 1: # a[0]: always 1
option.text = "1!" # QString object
option.font.setBold(True)
option.displayAlignment = Qt.AlignRight
#option.backgroundBrush ...
else:
# continue with the original `initStyleOption()`
super(ItemDelegate, self).initStyleOption(option, index)
def displayText(self, text, locale):
"""
Display `text` in the selected with the selected number
of digits
text: string / QVariant from QTableWidget to be rendered
locale: locale for the text
"""
data = text # .toString() # Python 2: need to convert to "normal" string
return "{0:>{1}}".format(data, 4)
class TestTable(QWidget):
""" Create widget for viewing / editing / entering data """
def __init__(self, parent):
super(TestTable, self).__init__(parent)
self.bfont = QFont()
self.bfont.setBold(True)
self.tblCoeff = QTableWidget(self)
self.tblCoeff.setItemDelegate(ItemDelegate(self))
layVMain = QVBoxLayout()
layVMain.addWidget(self.tblCoeff)
self.setLayout(layVMain)
self.ba = np.random.randn(3,4) # test data
self._refresh_table()
def _refresh_table(self):
""" (Re-)Create the displayed table from self.ba """
num_cols = 3
num_rows = 4
self.tblCoeff.setRowCount(num_rows)
self.tblCoeff.setColumnCount(num_cols)
for col in range(num_cols):
for row in range(num_rows):
# set table item from self.ba
item = self.tblCoeff.item(row, col)
if item: # does item exist?
item.setText(str(self.ba[col][row]))
else: # no, construct it:
self.tblCoeff.setItem(row,col,QTableWidgetItem(
str(self.ba[col][row])))
self.tblCoeff.resizeColumnsToContents()
self.tblCoeff.resizeRowsToContents()
#------------------------------------------------------------------------------
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainw = TestTable(None)
app.setActiveWindow(mainw)
mainw.show()
sys.exit(app.exec_())
replaces the overridden paint routine and is more concise.
You can try to override QStyledItemDelegate::initStyleOption
and set the text
yourself as this is now done by qt:
option->text = displayText(value, option->locale);