Коллеги,
столкнулся с неожиданной проблемой, когда потребовалось получить текстовое значение ячеек QTableView, в которых используются делегаты. Это мне потребовалось, чтобы организовать поиск в таблице. Понятно, что использовать модель, прицепленную к таблице, нельзя потому, что в ней содержится не то, что отображается в таблице.
Я не нашел внятного решения, поэтому, думаю, кому-нибудь это может показаться полезным. Не привожу полного "компилябельного" примера, но идея должна быть понятна.
Она проста - в самом делегате нужно предусмотреть открытый метод getItemText(const QModelIndex &index), который используется как для отрисовки делегата в таблице, так и при получении содержимого ячейки QTableView. Опуская детали, на примере делегата с использованием QComboBox:
C++ (Qt)
class ComboItem;
class CComboDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
QList<ComboItem> items;
//
public:
//
QString getItemText(const QModelIndex &index) const;
// QAbstractItemDelegate interface
public:
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
class ComboItem {
public:
QString item;
QVariant data;
ComboItem() = default;
ComboItem(QString text, QVariant value) {
item = text;
data = value;
}
bool operator == (const ComboItem& other) const {
return other.data == data;
}
};
Класс ComboItem содержит отображаемый текст и данные модели:
C++ (Qt)
QWidget *CComboDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox* combo = new QComboBox(parent);
combo->setEditable(false);
combo->setAutoFillBackground(true);
foreach (auto i, items) {
combo->addItem(i.item, i.data);
}
return combo;
}
QString CComboDelegate::getItemText(const QModelIndex &index) const
{
int i = items.indexOf(ComboItem("",index.data()));
return (i>=0)? items[i].item : index.data().toString();
}
void CComboDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Qt::ItemFlags flag = index.model()->flags(index);
painter->save();
if (option.state & QStyle::State_HasFocus || (option.state & QStyle::State_Selected && option.state & QStyle::State_Active)) {
painter->fillRect(option.rect, option.palette.highlight());
painter->setPen(option.palette.highlightedText().color());
} else if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.color(QPalette::Inactive, QPalette::Highlight));
} else if((flag & Qt::ItemIsEditable) == false) {
painter->fillRect(option.rect, QBrush(QColor(255, 245, 238)));
}
QRect rect = option.rect;
rect.setX(rect.x()+3);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, getItemText(index));
painter->restore();
}
Из всего того, что я привел выше, существенно только то, что то, что делается в getItemText, все равно используется в CComboDelegate::paint, я его только выполнил в виде метода и сделал открытым. Кстати, попутно тут я привел код отрисовки делегата для разных случаев (фокуса, выбранного, неактивного, только для чтения) - тоже может быть полезным.
Далее, как это используется при поиске по столбцу таблицы:
C++ (Qt)
void MainWindow::finderSlot(const QString &text)
{
if(!text.size())
return;
bool find = false;
QModelIndex index = tableViewData->currentIndex();
if(index.isValid()) {
QAbstractItemDelegate* delegate = tableViewData->itemDelegateForColumn(index.column());
CComboDelegate* comboDelegate = qobject_cast<CComboDelegate*>(delegate);
QAbstractItemModel* model = tableViewData->model();
for(int i=0; i<model->rowCount(); i++) {
index = index.siblingAtRow(i);
QString value = (comboDelegate)? comboDelegate->getItemText(index) : model->data(index).toString();
if(value.startsWith(text, Qt::CaseInsensitive)) {
tableViewData->setCurrentIndex(index);
find = true;
break;
}
}
} else {
QMessageBox (QMessageBox::Warning, "Search", "The search field is not selected", QMessageBox::Ok).exec();
lineEditFind->clear();
}
if(!find)
QApplication::beep();
}
Если текст найден, то курсор перемещается на соответствующую ячейку.