QTreeView+QStyledItemDelegate实现编辑名称功能
1.需求描述
点击编辑按钮,进入编辑状态,点击确认和取消按钮退出编辑状态
(1) 重写代理createEditor函数
这个函数是代理触发编辑信号后,自动创建编辑界面的widget对象,覆盖在item上;EmptyTreeItem就是我们自定义的编辑控件,包括输入框,确认和取消按钮;
QWidget * TreeTaskDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariantMap caseinfo = index.data(Qt::UserRole).toMap(); QString strType = caseinfo.value("type").toString(); EmptyTreeItem* pEdit = NULL; if (strType=="0")//案件 { pEdit = new EmptyTreeItem(); pEdit->setParent(parent); pEdit->SetAddType(TreeItemType::TopCase); connect(pEdit, &EmptyTreeItem::signalEditFinish, this, &TreeTaskDelegate::OnEditFinish); } else if (strType=="1")//文件夹 { pEdit = new EmptyTreeItem(); connect(pEdit, &EmptyTreeItem::signalEditFinish, this, &TreeTaskDelegate::OnEditFinish); pEdit->setParent(parent); pEdit->SetAddType(TreeItemType::ChildFolder); } else if (strType == "-1")//新增案件或者文件夹 { pEdit = new EmptyTreeItem(); connect(pEdit, &EmptyTreeItem::signalEditFinish, this, &TreeTaskDelegate::OnEditFinish); pEdit->setParent(parent); if (caseinfo.contains("parantItem")) { pEdit->SetAddType(TreeItemType::ChildFolder); } else { pEdit->SetAddType(TreeItemType::TopCase); } } return pEdit; }
(2) 重写代理的setEditorData函数
这个函数的作用是给自定义编辑窗口设置用户数据,例如将编辑前的名称设置给编辑窗口,显示在输入框内,作为初始值;
void TreeTaskDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (editor==NULL) { return; } QVariantMap caseinfo = index.data(Qt::UserRole).toMap(); QString strType = caseinfo.value("type").toString(); if (strType == "0" || strType=="1"|| strType=="-1") { EmptyTreeItem* pEdit = dynamic_cast<EmptyTreeItem*>(editor); if (pEdit == NULL) { return; } QVariantMap caseinfo = index.data(Qt::UserRole).toMap(); QString strType = caseinfo.value("type").toString(); pEdit->setInfo(caseinfo); //将数据设置给编辑窗口,例如显示修改前的名字 } }
(3)重写代理的updateEditorGeometry函数
这个函数的作用是设置编辑窗口在item上的坐标,我们采用是完全覆盖,所以直接以item的坐标设置给编辑窗口;
void TreeTaskDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { //QRect rect(option.rect.x() +20, option.rect.y(), option.rect.width() - 20, option.rect.height()); editor->setGeometry(option.rect); }
(4)重写代理的setModelData函数
这个函数的作用是编辑完成后将数据再从新更新到model中去,并刷新界面的显示,但是我们还需要更新到数据库,还需要判断有没有重名,所以没有在代理中实现这个函数,而是将通过编辑窗口的确认按钮和取消按钮传出编辑完成的信号出去,用槽函数实现编辑完成后的处理;
//void TreeTaskDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const //{ // if (editor == NULL||model==NULL) // { // return; // } // EmptyTreeItem* pEdit = dynamic_cast<EmptyTreeItem*>(editor); // if (pEdit==NULL) // { // return; // } // QVariantMap caseinfo = index.data(Qt::UserRole).toMap(); // QString strType = caseinfo.value("type").toString(); // caseinfo.insert("name", pEdit->GetName()); // model->setData(index, caseinfo, Qt::UserRole); //}
(5)在createEditor函数中创建编辑界面时就连接确认和取消按钮消息
connect(pEdit, &EmptyTreeItem::signalEditFinish, this, &TreeTaskDelegate::OnEditFinish);
(6)代理中实现OnEditFinish函数,传出信号给使用类
void TreeTaskDelegate::OnEditFinish(QString strType, QString strNewName)//strType confirm cancel { QWidget *editor = qobject_cast<QWidget *>(sender()); emit commitData(editor); emit closeEditor(editor); EmptyTreeItem* pEdit = (EmptyTreeItem*)(editor); if (pEdit!=NULL) { QVariantMap caseinfo = pEdit->getInfo(); emit signalEditFinish(caseinfo, strType, strNewName); } }
(7) 连接信号并实现编辑完成函数
void TreeTaskList::SlotEditFinish(QVariantMap caseinfo, QString buttontype, QString strNewName) { strNewName = strNewName.trimmed(); QStandardItem* pItem = caseinfo.value("treeItem").value<QStandardItem*>(); if (pItem==NULL) { return; } if (caseinfo.contains("type")) { QString strType = caseinfo.value("type").toString(); QString strOldName = caseinfo.value("name").toString(); QString strPath = caseinfo.value("path").toString(); if (strType == "0")//案件 { if (buttontype == "confirm") { if (strOldName != strNewName) { if (CheckSameName(NULL, strNewName) == 1) { emit signalError(-3, "同级目录下已有重名案件,请重新命名"); return; } //将名称更新到数据库 QString sql = QString("update CaseManage set name='%1' where path='%2'").arg(strNewName).arg(strPath); QString strMsg = ""; if (LocalDb::instance()->ExcuateSql(sql, strMsg) == 0) { caseinfo.insert("name", strNewName); } else { emit signalError(-3, "重命名保存到数据库失败"); LOG_ERROR("save name to db failed errormsg: %s", strMsg.toStdString().c_str()); return; } SlotResizeTreeWidget(); } } else if (buttontype == "cancel") { } caseinfo.remove("editing"); m_picPathDirInfo.insert(strPath, caseinfo); pItem->setData(caseinfo, Qt::UserRole); ui.treeView->closePersistentEditor(m_modelTree->indexFromItem(pItem)); } else if (strType == "1")//文件夹 { if (buttontype == "confirm") { if (strOldName != strNewName) { QStandardItem* pParatItem = pItem->parent(); if (pParatItem!=NULL) { if (CheckSameName(pParatItem, strNewName) == 1) { emit signalError(-3, "同级目录下已有重名文件夹,请重新命名"); return; } } //将名称更新到数据库 QString sql = QString("update CaseManage set name='%1' where path='%2'").arg(strNewName).arg(strPath); QString strMsg = ""; if (LocalDb::instance()->ExcuateSql(sql, strMsg) == 0) { caseinfo.insert("name", strNewName); } else { emit signalError(-3, "重命名保存到数据库失败"); LOG_ERROR("save name to db failed errormsg: %s", strMsg.toStdString().c_str()); return; } } } else if (buttontype == "cancel") { } caseinfo.remove("editing"); m_picPathDirInfo.insert(strPath, caseinfo); pItem->setData(caseinfo, Qt::UserRole); ui.treeView->closePersistentEditor(m_modelTree->indexFromItem(pItem)); } else if (strType == "-1")//新增案件或者文件夹 { QStandardItem* parantItem = NULL; if (caseinfo.contains("parantItem")) { parantItem = caseinfo.value("parantItem").value<QStandardItem*>(); } if (buttontype == "confirm") { if (strNewName.trimmed() == "") { signalError(-2, "案件名称不能为空或空格,请输入案件名称"); return; } if (AddFoldToList(strNewName.trimmed(), parantItem) == NULL)//添加案件 { //signalError(-2, "添加案件失败"); return; } } else if (buttontype == "cancel") { } ui.treeView->closePersistentEditor(pItem->index()); if (parantItem==NULL) { m_modelTree->removeRow(pItem->row()); } else { parantItem->removeRow(pItem->row()); } SlotResizeTreeWidget(); m_modelTree->sort(0, Qt::DescendingOrder); } } }
(8)触发编辑状态和关闭编辑状态
触发编辑状态,点击重命名时调用
ui.treeView->openPersistentEditor(index);//会一致显示,失交也不会退出
关闭编辑状态,编辑结束或者取消时调用
ui.treeView->closePersistentEditor(m_modelTree->indexFromItem(pItem));