PDA

View Full Version : Tableview column size update



drmacro
31st August 2017, 18:37
I'm using a delegate to display the data in a table.

The goal is to display an indicator with some text.

And that part works. But, I can't seem to figure out how to get the column width to resize to fit the painted cell width. (If I expand the width of the column, the complete string is there)

It appears that it is sizing the columns based on the with of the header data, not the cell data.

(I've "translated" this from an example in C++, and between my level of QT expertise and the translating...I probably have missed something obvious. :p )

The main window starts:



class TDDlg(QtWidgets.QMainWindow):

#CueFileUpdate_sig = pyqtSignal()
def __init__(self, parent=None):
super(TDDlg, self).__init__(parent)
self.setupUi(self)
self.tableheader_horz = self.get_header_horz()
self.tableheader_vert = []
#self.tableView.doubleClicked.connect(self.on_tabl e_dblclick)
#self.tableView.clicked.connect(self.on_table_clic k)
self.tabledata = []
self.get_table_data()
self.leveldata = []
self.get_level_data()
self.tablemodel = MyTableModel(self.tabledata, self.leveldata, self.tableheader_horz, self.tableheader_vert, self)
self.tableView.setModel(self.tablemodel)
#self.tableView.setItemDelegateForColumn(3, CellDelegate())
self.tableView.setItemDelegate(CellDelegate())
self.tableView.setSelectionMode(QAbstractItemView. SingleSelection)
self.tableView.setSelectionBehavior(QAbstractItemV iew.SelectItems)
self.tableView.resizeColumnsToContents()
i = self.tableView.model().index(0, 0)

def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_2.setObjectName("gridLayout_2")
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.tableView = QtWidgets.QTableView(self.centralwidget)
self.tableView.setObjectName("tableView")
self.gridLayout.addWidget(self.tableView, 0, 0, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 28))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.toolBar = QtWidgets.QToolBar(MainWindow)
self.toolBar.setObjectName("toolBar")
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)

self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)


And the model and the delegate:



class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, datain, leveldata, headerdata_horz, headerdata_vert, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.arraydata = datain
self.leveldata = leveldata
self.headerdata_horz = headerdata_horz
self.headerdata_vert = headerdata_vert

def rowCount(self, parent):
if parent.isValid():
return 0
return len(self.arraydata)

def columnCount(self, parent):
if parent.isValid():
return 0
else:
if self.arraydata:
return len(self.arraydata[0])
else:
return 0

def data(self, index, role): # Return data from the model
if not index.isValid():
logging.info('Invalid index in MyModel>data')
retval = QtCore.QVariant()
elif role == QtCore.Qt.BackgroundRole:
cell_contents = self.arraydata[index.row()][index.column()]
if cell_contents[-1] == '0':
retval = QtCore.QVariant()
else:
retval = QBrush(Qt.red)
elif role == QtCore.Qt.DisplayRole:
retval = QtCore.QVariant(self.arraydata[index.row()][index.column()])
else:
retval = QtCore.QVariant()

return retval

def setData(self, index, value, role): # Set data in the model
if role == QtCore.Qt.EditRole and index.isValid():
print(index.row())
self.arraydata[index.row()][index.column()] = value
print('Return from rowCount: {0}'.format(self.rowCount(index)))
self.dataChanged.emit(index, index, [QtCore.Qt.DisplayRole])
return True
return False

def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
if col < len(self.headerdata_horz):
return QtCore.QVariant(self.headerdata_horz[col])
else:
return QtCore.QVariant('')
elif orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
if col < len(self.headerdata_vert):
return QtCore.QVariant(self.headerdata_vert[col])
else:
return QtCore.QVariant('')

return QtCore.QVariant()

class CellDelegate(QStyledItemDelegate):
def __init__(self):
QStyledItemDelegate.__init__(self)
self.diameter = 10
def paint(self, painter, item, modelindex): #QStyleOptionViewItem
diameter = min(item.rect.width(),item.rect.height())
self.diameter = diameter
adjustedRect = copy.copy(item.rect)
adjustedRect.setWidth(diameter)
adjustedRect.setHeight(diameter)
adjustedRect = adjustedRect.adjusted(5,5,-5,-5)
shade = 120
background = QColor(shade, shade, 128)
painter.setPen(background)
painter.setBrush(background)
painter.drawEllipse(adjustedRect)
val = modelindex.data()
level = '>' + val.split(':')[0] + ':' + str(int_to_db(int(val.split(':')[1])))
painter.setPen(Qt.red)
painter.drawText(item.rect.adjusted(round(diameter * 1.1),0,0,0),
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter,
level)
return

d_stranz
31st August 2017, 19:51
The call to resizeColumnsToContents() in the __init__ function has no effect, since the table is not yet visible and therefore the geometry has not yet been calculated. This happens only just prior to the showEvent(). So the place to put this is to override showEvent() for your TDDlg and call it there. You'll also need to call it again each time the table contents changes. For that, you'll need to connect a slot to one of the model's signals that indicates a change (such as dataChanged()).

drmacro
31st August 2017, 23:18
I added this to TDDLG:



def showEvent(self, event):
self.tableView.resizeColumnsToContents()



It never gets called.

I added this to TDDLG __init__:



self.tablemodel.dataChanged.connect(self.model_cha nged)



and this to TDDLG:


def model_changed(self, event):
self.tableView.resizeColumnsToContents()


It never gets called either, but I have no way to change data in the table at this point, so that is no surprise...I think. :confused:


Update: I don't know what I did but showEvent in TDDlg is now being called...but it still doesn't change the columns to fit the data. :o

Added after 15 minutes:

If I understand...which always questionable, the sizeHint in the delegate is called to determine the cell size.

Maybe I'm not calculating that correctly:



def sizeHint(self, item, modelindex):
textwidth = item.fontMetrics.width(modelindex.data())
newwidth = textwidth + self.diameter
return QSize(newwidth, item.fontMetrics.height())

The intent the diameter is the additional width added by the drawEllipse in the delegate paint routine.

drmacro
11th September 2017, 14:30
Ok, back at this after a trip.

I have implemented it as discussed previously and re-implemented with decorator role.

I am not able to get the custom delegate code to update the width of the cell to the data.
(If I put a fudge factor in the cell delegate size hint as shown below, the cell expands and shows the text of the data.
Of course, the fudge only works for the test data and won't be big enough if the data changes.)



def sizeHint(self, item, modelindex):
val = modelindex.data()
textwidth = item.fontMetrics.width(val)
textheight = item.fontMetrics.height()
bbox = item.fontMetrics.boundingRect(val)
diameter = round(min(textwidth,textheight) * 1.6) #<----fudge factor
newwidth = textwidth + diameter
print('In sizeHint, textwidth={}, newwidth={}, diameter={}'.format(textwidth, newwidth, diameter))
print('model_index.data={}, level ={}'.format(modelindex.data(), val))
print('bbox width={}'.format(bbox.width()))
return QSize(newwidth, item.fontMetrics.height())


Implemented with the decorator role, works fine. But, (and I assume I'm doing something wrong ) with the decorator role
the app takes twice as long to start up.

I assume this is because in the decorator implementation, it loads an icon rater than drawing an ellipse for each cell.

I'd like to figure out what I'm doing wrong in the custom delegate, but, I have no particular need to have the custom delegate and the decorator way expands the cells fine.
Having implemented the decorator I'd now like to know if
there are ways to improve the icon loading (currently I load two different icons in the table model and return them as decorators, see below).
(Or, maybe use something other than an icon as the decorator??)



class MyTableModel(QtCore.QAbstractTableModel):
def __init__(self, datain, leveldata, headerdata_horz, headerdata_vert, parent=None):
"""
Args:
datain: a list of lists\n
headerdata: a list of strings
"""
QtCore.QAbstractTableModel.__init__(self, parent)
self.arraydata = datain
self.leveldata = leveldata
self.headerdata_horz = headerdata_horz
self.headerdata_vert = headerdata_vert
self.iconmute_on = QtGui.QIcon()
self.iconmute_on.addPixmap(QtGui.QPixmap(":/icon/Mute_illum_64.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.iconmute_off = QtGui.QIcon()
self.iconmute_off.addPixmap(QtGui.QPixmap(":/icon/Mute_dark_64.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)

def data(self, index, role): # Return data from the model
if index.isValid():
#print('Invalid index in MyModel>data')
logging.info('Invalid index in MyModel>data')
retval = QtCore.QVariant()
if role == QtCore.Qt.BackgroundRole:
cell_contents = self.arraydata[index.row()][index.column()]
retval = QtCore.QVariant()
# if cell_contents[-1] == '0':
# #retval = QBrush(Qt.lightGray)
# retval = QtCore.QVariant()
# else:
# retval = QBrush(Qt.red)
elif role == QtCore.Qt.DisplayRole:
retval = QtCore.QVariant(self.leveldata[index.row()][index.column()])
# retval = QtCore.QVariant(self.leveldata[index.row()][index.column()])
elif role == QtCore.Qt.DecorationRole:
#retval = QtGui.QIcon("Mute_illum_64.png")
#if index.column() != 0:
try:
if self.arraydata[index.row()][index.column()][-1] == '0':# or index.column() == 0:
#return mute_btn_dark# QtGui.QIcon('Mute_dark_64.png')
return self.iconmute_off
else:
#return mute_btn_illum #QtGui.QIcon('Mute_illum_64.png')
return self.iconmute_on
except IndexError:
print('Bad index, Row: {}, Column:{}'.format(index.row(),index.column()))

else:
retval = QtCore.QVariant()

return retval