I have a QTableView
defines in UI file. Here is the figure:
I'd like to make month change (where the red array points) with QComboBox
widget, dealing with delegates, but for me, for my custom delegate and model it's a too complex problem, and I can't figure out what is wrong?!
Problem: on my opinion, QAbstractTableModel
can't work with QItemDelegate
, because I'm unable to combine my custom simple ComboBoxDelegate
widget with QTableView. Wtf?
Here is what I have:
My custom delegate header/source data:
class ComboBoxDelegate : public QItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject *parent = 0);
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const;
void setEditorData(QWidget *editor, const QModelIndex &index) const;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const;
void updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
ComboBoxDelegate::ComboBoxDelegate(QObject *parent)
: QItemDelegate(parent)
{}
QWidget *ComboBoxDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &index) const
{
QComboBox* editor = new QComboBox(parent);
editor->addItem(index.data(Qt::DisplayRole).toString());
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString value = index.model()->data(index, Qt::EditRole).toString();
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentIndex(comboBox->findText(value));
}
void ComboBoxDelegate::setModelData(QWidget *editor,
QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
QString value = comboBox->currentText();
model->setData(index, value, Qt::EditRole);
}
Model data:
class PortfolioModel : public QAbstractTableModel
{
Q_OBJECT;
// types
enum Position
{
ePosValue = 0
, eColumnCount
};
enum Constants
{
eLocalCcy = 0
, eCurrentTime
, eCurrentMonthName
, eRowCount
};
// data
static QFont font_;
static QBrush foreground_;
static QBrush background_;
// methods
QVariant _valueName(int index) const;
QVariant _valueNameTooltip(int index) const;
QVariant _data(int index, int col, bool tooltip) const;
public:
PortfolioModel(QObject* parent = 0);
~PortfolioModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
signals:
void resizeToContents(void);
public slots:
void refreshData(void);
};
QFont PortfolioModel::font_ = QFont("Tahoma", 8, QFont::Normal);
QBrush PortfolioModel::foreground_ = QBrush(QColor("#000000")); // Black
QBrush PortfolioModel::background_ = QBrush(QColor("#C3FDB8")); // Dark Sea Green1
PortfolioModel::PortfolioModel(QObject* parent)
: QAbstractTableModel(parent)
{}
PortfolioModel::~PortfolioModel()
{}
void PortfolioModel::refreshData(void)
{
emit dataChanged(QModelIndex(), QModelIndex());
emit resizeToContents();
}
int PortfolioModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
return eRowCount;
}
int PortfolioModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
return eColumnCount;
}
QVariant PortfolioModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const
{
if (!index.isValid())
return QVariant();
switch (role)
{
case Qt::DisplayRole:
return _data(index.row(), index.column(), false);
case Qt::FontRole:
break;
case Qt::BackgroundRole:
return background_;
case Qt::ForegroundRole:
return foreground_;
case Qt::TextAlignmentRole:
case Qt::DecorationRole:
case Qt::EditRole:
break;
case Qt::ToolTipRole:
return _data(index.row(), index.column(), true);
case Qt::StatusTipRole:
case Qt::WhatsThisRole:
case Qt::SizeHintRole:
break;
}
return QVariant();
}
QVariant PortfolioModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch (orientation)
{
case Qt::Horizontal:
switch (role)
{
case Qt::DisplayRole:
if (section == ePosValue)
{
return QVariant("Value");
}
case Qt::ToolTipRole:
if (section == ePosValue)
{
return QVariant("Fund values");
}
break;
}
case Qt::Vertical:
switch (role)
{
case Qt::DisplayRole:
return _valueName(section);
case Qt::ToolTipRole:
return _valueNameTooltip(section);
}
break;
}
return QVariant();
}
QVariant PortfolioModel::_valueNameTooltip(int index) const
{
switch (index)
{
case eLocalCcy:
return QObject::tr("Local currency");
case eCurrentTime:
return QObject::tr("Current time");
case eCurrentMonthName:
return QObject::tr("Current Month");
}
return QVariant();
}
QVariant PortfolioModel::_valueName(int index) const
{
switch (index)
{
case eLocalCcy:
return QObject::tr("Local Currency");
case eCurrentTime:
return QObject::tr("Current Time");
case eCurrentMonthName:
return QObject::tr("Month");
}
return QVariant();
}
QVariant PortfolioModel::_data(int index, int col, bool tooltip) const
{
switch (index)
{
case eLocalCcy:
if (col == ePosValue)
{
return QString(Nav::bk()->currentFund().currency(Nav::bk()->currentFund().localCcy()).code());
}
break;
case eCurrentTime:
if (col == ePosValue)
{
return Nav::bk()->currentFund().currentTime();
}
break;
case eCurrentMonthName:
if (col == ePosValue)
{
return QDate::longMonthName(Nav::bk()->currentFund().currentTime().date().month());
}
break;
}
return QVariant();
}
And after that I made init delegate like that:
ComboBoxDelegate *delegate_ = new ComboBoxDelegate(this);
this->ui.tableView->setItemDelegate(delegate_);
PS: sorry for the long output, but I think it's will be better If all sources will be presented here.
Thanks!
From the overview of QAbstractItemModel (see the Subclassing heading):
To enable editing in your model, you must also implement setData(), and reimplement flags() to ensure that ItemIsEditable is returned.
The same information is given in the overview of QAbstractTableModel. Your PortfolioModel
class does not reimplement either of these functions. In order to use the delegate's editor, you need to use do so.
Alternately, you may only want it to appear as if a combobox is there (not actually allow editing). If that is the case, you will probably need to do some drawing hacks yourself to make it appear that way... or mark it as editable but disable the widget, or something like that.