ChrisW67
17th March 2010, 23:56
I have a series of table models displayed either in a QTableView or QDataWidgetMapper. Each display widget has an attached QStyledItemDelegate derivative. The delegate's paint method is responsible for displaying date and duration columns in a format that is dynamically changeable by the user, e.g. a duration column can be displayed in whole minutes, decimal hours, or HH:MM.
I would like the related views to update when this format changes but haven't found the "correct" way to do this. I can have the delegates emit a signal when the format changes but there is nothing obvious to connect it to that would cause the view to refresh. For the QDataWidgetMapper, which displays a single row output in my application, I can force a refresh using toFirst(). I haven't found the equivalent for QTableView (other than reset() which loses selections etc.). The example code below shows the sort of thing I'm after. The table view only redraws when scrolled or resized.
I've also considered pushing the determination of the display text back into the models. The Qt::DisplayRole could return the variably formatted date/duration and emit layoutChanged() to force refresh. This doesn't work with the mapper though.
Any suggestions?
#include <QtGui>
class TestDelegate: public QStyledItemDelegate {
Q_OBJECT
public:
TestDelegate(QObject * parent = 0 )
: QStyledItemDelegate(parent)
{
m_format = true;
}
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
qDebug() << "Paint called for ("<< index.row() << "," << index.column() << ")";
// Default setup for this index
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
const QWidget *widget = opt.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
// Change the display the columns
int mins = index.data(Qt::EditRole).toInt();
if (m_format) {
// H:MM
int hours = mins / 60;
mins = mins % 60;
opt.text = QString("%1:%2").arg(hours).arg(mins, 2, 10, QLatin1Char('0'));
}
else {
// raw minutes
opt.text = opt.locale.toString(mins);
}
// Draw it
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}
public slots:
void toggleFormat() {
m_format = !m_format;
qDebug() << "Toggled to : " << m_format;
}
private:
bool m_format;
};
class MainWindow: public QWidget {
Q_OBJECT
public:
MainWindow(QWidget *parent=0):
QWidget(parent)
{
m_button = new QPushButton(this);
m_button->setText("Toggle format");
m_view = new QTableView(this);
TestDelegate *del = new TestDelegate(this);
m_view->setItemDelegate(del);
connect(m_button, SIGNAL(clicked()), del, SLOT(toggleFormat()));
QVBoxLayout *layout= new QVBoxLayout(this);
layout->addWidget(m_view);
layout->addWidget(m_button);
}
void setModel(QAbstractItemModel *model) {
m_view->setModel(model);
}
public slots:
private:
QTableView *m_view;
QPushButton *m_button;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStandardItemModel *model = new QStandardItemModel();
for (int row=0; row<10; row++) {
for (int col=0; col<5; col++) {
QStandardItem *newItem = new QStandardItem();
newItem->setData(row*10+col, Qt::EditRole);
model->setItem(row ,col, newItem);
}
}
MainWindow main;
main.setModel(model);
main.show();
return a.exec();
delete model;
}
#include "main.moc"
I would like the related views to update when this format changes but haven't found the "correct" way to do this. I can have the delegates emit a signal when the format changes but there is nothing obvious to connect it to that would cause the view to refresh. For the QDataWidgetMapper, which displays a single row output in my application, I can force a refresh using toFirst(). I haven't found the equivalent for QTableView (other than reset() which loses selections etc.). The example code below shows the sort of thing I'm after. The table view only redraws when scrolled or resized.
I've also considered pushing the determination of the display text back into the models. The Qt::DisplayRole could return the variably formatted date/duration and emit layoutChanged() to force refresh. This doesn't work with the mapper though.
Any suggestions?
#include <QtGui>
class TestDelegate: public QStyledItemDelegate {
Q_OBJECT
public:
TestDelegate(QObject * parent = 0 )
: QStyledItemDelegate(parent)
{
m_format = true;
}
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
qDebug() << "Paint called for ("<< index.row() << "," << index.column() << ")";
// Default setup for this index
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
const QWidget *widget = opt.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
// Change the display the columns
int mins = index.data(Qt::EditRole).toInt();
if (m_format) {
// H:MM
int hours = mins / 60;
mins = mins % 60;
opt.text = QString("%1:%2").arg(hours).arg(mins, 2, 10, QLatin1Char('0'));
}
else {
// raw minutes
opt.text = opt.locale.toString(mins);
}
// Draw it
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}
public slots:
void toggleFormat() {
m_format = !m_format;
qDebug() << "Toggled to : " << m_format;
}
private:
bool m_format;
};
class MainWindow: public QWidget {
Q_OBJECT
public:
MainWindow(QWidget *parent=0):
QWidget(parent)
{
m_button = new QPushButton(this);
m_button->setText("Toggle format");
m_view = new QTableView(this);
TestDelegate *del = new TestDelegate(this);
m_view->setItemDelegate(del);
connect(m_button, SIGNAL(clicked()), del, SLOT(toggleFormat()));
QVBoxLayout *layout= new QVBoxLayout(this);
layout->addWidget(m_view);
layout->addWidget(m_button);
}
void setModel(QAbstractItemModel *model) {
m_view->setModel(model);
}
public slots:
private:
QTableView *m_view;
QPushButton *m_button;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QStandardItemModel *model = new QStandardItemModel();
for (int row=0; row<10; row++) {
for (int col=0; col<5; col++) {
QStandardItem *newItem = new QStandardItem();
newItem->setData(row*10+col, Qt::EditRole);
model->setItem(row ,col, newItem);
}
}
MainWindow main;
main.setModel(model);
main.show();
return a.exec();
delete model;
}
#include "main.moc"