前面讲解了有关Model/View的实现方式,下面将用一个例子,介绍如何构造model和使用Delegate渲染进行混合编程,主要显示学生信息,tableview里面有checkbox, combBox, lineEdit等操作控件,最终效果如下。
1.首先,我们建立2个学生的基本信息(姓名,性别,年龄,分数,是否毕业),并放在链表中。姓名不可编辑,性别combox下拉可选,年龄,分数双击可修改,是否毕业开关可编辑。
enum E_Gender
{
E_BOY,
E_GIRL,
};
typedef struct S_Student
{
bool m_check;
QString m_name;
int m_age;
E_Gender m_gender;
int m_score;
bool m_graduate;
}S_STU;
QList StuList;
StuList.clear();
S_STU *stu1 = new S_STU;
stu1->m_check = false;
stu1->m_name = QString("XiaoMing");
stu1->m_gender = E_BOY;
stu1->m_age = 12;
stu1->m_score = 98;
stu1->m_graduate = true;
S_STU *stu2 = new S_STU;
stu2->m_check = false;
stu2->m_name = QString("XiaoHong");
stu2->m_gender = E_GIRL;
stu2->m_age = 10;
stu2->m_score = 100;
stu2->m_graduate = false;
StuList.append(stu1);
StuList.append(stu2);
2.构造model模型,重载常用的虚函数。
int StudentModel::columnCount(const QModelIndex & parent) const
{
Q_UNUSED(parent);
return 6;
}
int StudentModel::rowCount(const QModelIndex & parent) const
{
Q_UNUSED(parent);
return StuList.count();
}
QVariant StudentModel::data(const QModelIndex & index, int role) const
{
if (!index.isValid())
return QVariant();
int nRow = index.row();
int nColumn = index.column();
S_STU *stu = StuList.at(nRow);
switch(role){
case Qt::TextColorRole:
return QColor(Qt::black);
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
case Qt::UserRole+COLUMN_CHECK:
{
if (nColumn == COLUMN_CHECK)
return stu->m_check;
return "";
}
case Qt::DisplayRole:
{
if (nColumn == COLUMN_NAME)
return stu->m_name;
if (nColumn == COLUMN_AGE)
return stu->m_age;
if (nColumn == COLUMN_SCORE)
return stu->m_score;
return "";
}
case Qt::UserRole+COLUMN_AGE:
{
if (nColumn == COLUMN_AGE)
return stu->m_age;
return "";
}
case Qt::UserRole+COLUMN_SCORE:
{
if (nColumn == COLUMN_SCORE)
return stu->m_score;
return "";
}
case Qt::UserRole+COLUMN_GENDER:
{
if (nColumn == COLUMN_GENDER)
return stu->m_gender;
return "";
}
case Qt::UserRole+COLUMN_GRADUATE:
{
if (nColumn == COLUMN_GRADUATE)
return stu->m_graduate;
return "";
}
default:
return QVariant();
}
return QVariant();
}
bool StudentModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if (!index.isValid())
return false;
int nRow = index.row();
S_STU *stu = StuList.at(nRow);
switch(role){
case Qt::UserRole+COLUMN_CHECK:
{
stu->m_check = value.toBool();
emit dataChanged(index, index);
return true;
}
case Qt::UserRole+COLUMN_GENDER:
{
int val = value.toInt();
if(val==0)
stu->m_gender = E_BOY;
else if(val==1)
stu->m_gender = E_GIRL;
emit dataChanged(index, index);
return true;
}
case Qt::UserRole+COLUMN_AGE:
{
stu->m_age = value.toInt();
emit dataChanged(index, index);
return true;
}
case Qt::UserRole+COLUMN_SCORE:
{
stu->m_score = value.toInt();
emit dataChanged(index, index);
return true;
}
case Qt::UserRole+COLUMN_GRADUATE:
{
stu->m_graduate = value.toBool();
emit dataChanged(index, index);
return true;
}
default:
return false;
}
return setData(index,value,role);
}
QVariant StudentModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(role)
{
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignHCenter | Qt::AlignVCenter);
case Qt::DisplayRole:
{
if(orientation == Qt::Horizontal){
if (section == COLUMN_CHECK)
return QStringLiteral("Operate");
if (section == COLUMN_NAME)
return QStringLiteral("Name");
if (section == COLUMN_GENDER)
return QStringLiteral("Gender");
if (section == COLUMN_AGE)
return QStringLiteral("Age");
if (section == COLUMN_SCORE)
return QStringLiteral("Score");
if (section == COLUMN_GRADUATE)
return QStringLiteral("Graduate");
}
}
default:
return QVariant();
}
return QVariant();
}
Qt::ItemFlags StudentModel::flags(const QModelIndex & index) const
{
if (!index.isValid())
return QAbstractItemModel::flags(index);
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if(index.column() == COLUMN_CHECK){
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}else if(index.column() == COLUMN_GENDER){
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}else if(index.column() == COLUMN_AGE || index.column() == COLUMN_SCORE){
flags |= Qt::ItemIsEditable;
}else if(index.column() == COLUMN_GRADUATE){
flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
}
return flags;
}
3.添加StudentDelegate类,继承于QStyledItemDelegate.同样重载需要使用的虚函数。在这里面,combox和lineEdit控件通过创建编辑器createEditor函数创建,checkbox和开关图标通过paint函数创建。editorEvent函数主要处理checkbox和开关图标的点击处理。
QWidget * StudentDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
if(index.column() == COLUMN_GENDER){
QComboBox *combox = new QComboBox(parent);
combox->addItem("Boy");
combox->addItem("Girl");
return combox;
}else if(index.column() == COLUMN_AGE || index.column() == COLUMN_SCORE ){
QLineEdit *lineEdit = new QLineEdit(parent);
return lineEdit;
}
return 0;
}
void StudentDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
{
if(index.column() == COLUMN_GENDER){
QComboBox *comboBox = static_cast(editor);
int value = index.model()->data(index, Qt::UserRole+COLUMN_GENDER).toUInt();
comboBox->setCurrentIndex(value);
}else if(index.column() == COLUMN_AGE || index.column() == COLUMN_SCORE){
QLineEdit *lineEdit = static_cast(editor);
QString value;
if(index.column() == COLUMN_AGE)
value = index.model()->data(index, Qt::DisplayRole).toString();
else if(index.column() == COLUMN_SCORE)
value = index.model()->data(index, Qt::DisplayRole).toString();
lineEdit->setText(value);
}
}
void StudentDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
if(index.column() == COLUMN_GENDER){
QComboBox *comboBox = static_cast(editor);
model->setData(index, comboBox->currentIndex(), Qt::UserRole+COLUMN_GENDER);
}else if(index.column() == COLUMN_AGE || index.column() == COLUMN_SCORE ){
QLineEdit *lineEdit = static_cast(editor);
if(index.column() == COLUMN_AGE)
model->setData(index, lineEdit->text(), Qt::UserRole+COLUMN_AGE);
else if(index.column() == COLUMN_SCORE)
model->setData(index, lineEdit->text(), Qt::UserRole+COLUMN_SCORE);
}
}
void StudentDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem viewOption(option);
initStyleOption(&viewOption, index);
if (option.state.testFlag(QStyle::State_HasFocus))
viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
QStyledItemDelegate::paint(painter, viewOption, index);
if (index.column() == COLUMN_CHECK)
{
bool data = index.model()->data(index, Qt::UserRole + COLUMN_CHECK).toBool();
QStyleOptionButton checkBoxStyle;
checkBoxStyle.state = data ? QStyle::State_On : QStyle::State_Off;
checkBoxStyle.state |= QStyle::State_Enabled;
checkBoxStyle.iconSize = QSize(20, 20);
checkBoxStyle.rect = option.rect;
QCheckBox checkBox;
QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkBoxStyle, painter, &checkBox);
}
else if(index.column() == COLUMN_GRADUATE)
{
bool data = index.model()->data(index, Qt::UserRole + COLUMN_GRADUATE).toBool();
QPixmap pixmap;
if(data)
pixmap = QPixmap(":/checked.png");
else
pixmap = QPixmap(":/unchecked.png");
int height = (viewOption.rect.height() - 22) / 2;
QRect decorationRect = QRect(viewOption.rect.left() + viewOption.rect.width() - 90, viewOption.rect.top() + height, 36, 22);
painter->drawPixmap(decorationRect, pixmap);
}
}
bool StudentDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
QRect decorationRect = option.rect;
QMouseEvent *mouseEvent = static_cast(event);
if (event->type() == QEvent::MouseButtonPress && decorationRect.contains(mouseEvent->pos()))
{
if (index.column() == COLUMN_CHECK)
{
bool data = model->data(index, Qt::UserRole+COLUMN_CHECK).toBool();
model->setData(index, !data, Qt::UserRole+COLUMN_CHECK);
}else if(index.column() == COLUMN_GRADUATE){
bool data = model->data(index, Qt::UserRole+COLUMN_GRADUATE).toBool();
model->setData(index, !data, Qt::UserRole+COLUMN_GRADUATE);
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
4.最后调整各个控件里的参数,然后获取一下数据,点击sure按钮测试,可以看到数据是否写入链表中。
打印内容顺序:姓名 是否选中 性别 年龄 分数 是否毕业