Qt开发 | Qt模型视图代理(Model-View-Delegate)

文章目录

  • 一、Qt MVD概念讲解
  • 二、Qt模型视图代理之:QTableView的使用
  • 三、Qt模型视图代理之:QListView的使用

一、Qt MVD概念讲解

  Qt MVD(Model-View-Delegate)是Qt框架中的一种设计模式,是Qt中用界面组件显示与编辑数据的一种结构。视图(view)是显示和编辑数据的界面组件,模型(Model)是视图与原始数据之间的接口。Model/View 结构的典型应用是在数据库应用程序中,例如数据库中的一个数据表可以在一个QTableView组件中显示和编辑。

MVD三个组件的介绍:

  • 模型

    模型 (Model) 是视图与原始数据之间的接口。原始数据可以是数据库中的一个数据表、内存中的一个StringList、磁盘文件结构等。QAbstractItemModel 是所有模型的祖宗类,其它 model 类都派生于它

    image-20240710164113448

  • 视图

    视图(view)是显示和编辑数据的界面组件。它不直接与数据交互,而是通过模型提供的接口来获取数据。Qt提供了多种视图类,如QListViewQTableViewQTreeView等,QListWidget、QTreeWidget 和 QTableWidget 是视图类的简化版,它们不使用数据模型,而是将数据直接存储在组件的每个项里面。

    image-20240710164424375

  • 代理

    代理 (Delegate) 为视图组件提供数据编辑器,如在表格组件中,编辑一个单元格的数据时,缺省是使用一个 QLineEdit 编辑框,代理负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后,又将其保存到数据模型中

    • QAbstractItemDelegate Class是所有代理类的抽象基类
    • QStyledItemDelegate Class是视图组件使用的缺省的代理类

    如何实现自定义代理

      自定义代理组件,主要用于自定义编辑器组件的行为,都必须实现如下的4个函数:

    • createEditor函数用于创建编辑特定数据模型项的编辑器组件。,如一个QSpinBox组件或一个QComboBox组件。

      如果数据模型包含不同类型的数据,需要根据数据类型来创建不同类型的编辑器组件

    • setEditorData函数用于将数据模型中的数据显示在编辑器组件中。它从数据模型中检索数据,并将其设置到编辑器组件。

    • setModelData函数用于将编辑器组件中的更改提交回数据模型。它从编辑器组件获取数据,并更新数据模型。

    • updateEditorGeometry函数用于调整编辑器组件的大小和位置,以确保它适合其所在的视图项

    在视图组件中设置自定义代理,需要调用setItemDelegateForColumn函数

    ui->tableView->setItemDelegateForColumn(3, &m_floatSpinDelegate);
    ui->tableView->setItemDelegateForColumn(4, &m_intSpinDelegate);
    ui->tableView->setItemDelegateForColumn(5, &m_comboBoxDelegate);
    

二、Qt模型视图代理之:QTableView的使用

  QTableView用于显示和编辑表格数据。下面案例需要用到自定义代理组件

示例:

  自定义下拉列表代理组件

CComboBoxDelegate.h

#ifndef CCOMBOBOXDELEGATE_H
#define CCOMBOBOXDELEGATE_H#include    <QItemDelegate>class CComboBoxDelegate : public QItemDelegate
{Q_OBJECTpublic:CComboBoxDelegate(QObject *parent=0);//自定义代理组件必须继承以下4个函数QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;
};#endif // CCOMBOBOXDELEGATE_H

CComboBoxDelegate.cpp

#include "ccomboboxdelegate.h"
#include <QComboBox>CComboBoxDelegate::CComboBoxDelegate(QObject *parent):QItemDelegate(parent)
{}QWidget *CComboBoxDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option, const QModelIndex &index) const
{QComboBox *editor = new QComboBox(parent);editor->addItem(u8"优");editor->addItem(u8"良");editor->addItem(u8"一般");return editor;
}void CComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{QString str = index.model()->data(index, Qt::EditRole).toString();QComboBox *comboBox = static_cast<QComboBox*>(editor);comboBox->setCurrentText(str);
}void CComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{QComboBox *comboBox = static_cast<QComboBox*>(editor);QString str = comboBox->currentText();model->setData(index, str, Qt::EditRole);
}void CComboBoxDelegate::updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index) const
{editor->setGeometry(option.rect);
}

  自定义上下箭头输入框代理组件

CFloatSpinDelegate.h

#ifndef CFLOATSPINDELEGATE_H
#define CFLOATSPINDELEGATE_H#include    <QObject>
#include    <QWidget>
#include    <QStyledItemDelegate>class CFloatSpinDelegate : public QStyledItemDelegate
{Q_OBJECT
public:CFloatSpinDelegate(QObject *parent=0);//自定义代理组件必须继承以下4个函数//创建编辑组件QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;
};#endif // CFLOATSPINDELEGATE_H

CFloatSpinDelegate.cpp

#include "cfloatspindelegate.h"
#include  <QDoubleSpinBox>CFloatSpinDelegate::CFloatSpinDelegate(QObject *parent):QStyledItemDelegate(parent)
{}QWidget *CFloatSpinDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option, const QModelIndex &index) const
{Q_UNUSED(option);Q_UNUSED(index);QDoubleSpinBox *editor = new QDoubleSpinBox(parent);editor->setFrame(false);editor->setMinimum(0);editor->setDecimals(2);editor->setMaximum(100);return editor;
}void CFloatSpinDelegate::setEditorData(QWidget *editor,const QModelIndex &index) const
{float value = index.model()->data(index, Qt::EditRole).toFloat();QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);spinBox->setValue(value);
}void CFloatSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);spinBox->interpretText();float value = spinBox->value();QString str=QString::asprintf("%.2f",value);model->setData(index, str, Qt::EditRole);
}void CFloatSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{editor->setGeometry(option.rect);
}

CIntSpinDelegate.h

#ifndef CINTSPINDELEGATE_H
#define CINTSPINDELEGATE_H#include    <QStyledItemDelegate>class CIntSpinDelegate : public QStyledItemDelegate
{Q_OBJECT
public:CIntSpinDelegate(QObject *parent=0);//自定义代理组件必须继承以下4个函数//创建编辑组件QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;//从数据模型获取数据,显示到代理组件中void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;//将代理组件的数据,保存到数据模型中void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;//更新代理编辑组件的大小void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;
};#endif // CINTSPINDELEGATE_H

CIntSpinDelegate.cpp

#include "cintspindelegate.h"
#include    <QSpinBox>CIntSpinDelegate::CIntSpinDelegate(QObject *parent):QStyledItemDelegate(parent)
{}QWidget *CIntSpinDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option, const QModelIndex &index) const
{//创建代理编辑组件Q_UNUSED(option);Q_UNUSED(index);QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBoxeditor->setFrame(false); //设置为无边框editor->setMinimum(0);editor->setMaximum(120);return editor;  //返回此编辑器
}void CIntSpinDelegate::setEditorData(QWidget *editor,const QModelIndex &index) const
{//从数据模型获取数据,显示到代理组件中//获取数据模型的模型索引指向的单元的数据int value = index.model()->data(index, Qt::EditRole).toInt();QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  //强制类型转换spinBox->setValue(value); //设置编辑器的数值
}void CIntSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{//将代理组件的数据,保存到数据模型中QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号int value = spinBox->value(); //获取spinBox的值model->setData(index, value, Qt::EditRole); //更新到数据模型
}void CIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{//设置组件大小Q_UNUSED(index);editor->setGeometry(option.rect);
}

  设置数据模型、选择模型与设置自定义代理组件;使用了QAxObject打开Excel(windows上)来添加数据

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QStandardItemModel>
#include <QItemSelectionModel>
#include "cintspindelegate.h"
#include "cfloatspindelegate.h"
#include "ccomboboxdelegate.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_btnOpenExcel_clicked();void on_btnReshowData_clicked();void OnCurrentChanged(const QModelIndex &current, const QModelIndex &previous);void on_btnAppendLast_clicked();void on_btnAppend_clicked();void on_btnDeleteSelectedLine_clicked();private:Ui::Widget *ui;QStandardItemModel  *m_pItemModel;//数据模型QItemSelectionModel *m_pSelectionModel;//Item选择模型CIntSpinDelegate    m_intSpinDelegate;    //整型数spinbox代理CFloatSpinDelegate  m_floatSpinDelegate;  //浮点数spinbox代理CComboBoxDelegate   m_comboBoxDelegate;   //combobox代理
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QAxObject>
#include <QFileDialog>
#include <QStandardPaths>static const int COLUMN_COUNT = 7;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);showMaximized();m_pItemModel = new QStandardItemModel(1,COLUMN_COUNT,this);m_pSelectionModel = new QItemSelectionModel(m_pItemModel);  //Item选择模型//选择当前单元格变化时的信号与槽connect(m_pSelectionModel, &QItemSelectionModel::currentChanged, this, &Widget::OnCurrentChanged);ui->tableView->setModel(m_pItemModel);   //设置数据模型到表格视图ui->tableView->setSelectionModel(m_pSelectionModel);  //设置选择模型到表格视图ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection); //设置选择模式为扩展选择,允许用户进行多选ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);	//设置选择行为为选择条目//给第3,4,5列设置自定义代理组件ui->tableView->setItemDelegateForColumn(3, &m_floatSpinDelegate);ui->tableView->setItemDelegateForColumn(4, &m_intSpinDelegate);ui->tableView->setItemDelegateForColumn(5, &m_comboBoxDelegate);
}Widget::~Widget()
{delete ui;
}//打开excel
void Widget::on_btnOpenExcel_clicked()
{QAxObject *excel = new QAxObject(this);excel->setControl("Excel.Application");excel->setProperty("Visible", false);    //显示窗体看效果,选择ture将会看到excel表格被打开excel->setProperty("DisplayAlerts", true);QAxObject *workbooks = excel->querySubObject("WorkBooks");   //获取工作簿(excel文件)集合QString str = QFileDialog::getOpenFileName(this, u8"打开excel","D:/MyQtCreatorProject/9_2_tableView",u8"Excel 文件(*.xls *.xlsx)");//打开刚才选定的excelworkbooks->dynamicCall("Open(const QString&)", str);QAxObject *workbook = excel->querySubObject("ActiveWorkBook");QAxObject *worksheet = workbook->querySubObject("WorkSheets(int)",1);QAxObject *usedRange = worksheet->querySubObject("UsedRange");   //获取表格中的数据范围QVariant var = usedRange->dynamicCall("Value");  //将所有的数据读取刀QVariant容器中保存QList<QList<QVariant>> excel_list;  //用于将QVariant转换为Qlist的二维数组QVariantList varRows=var.toList();if(varRows.isEmpty()){return;}const int row_count = varRows.size();QVariantList rowData;for(int i=0;i<row_count;++i){rowData = varRows[i].toList();excel_list.push_back(rowData);}//将每一行的内容放到contentListQList<QStringList> contentList;for(int i = 0; i<row_count; i++){QList<QVariant> curList = excel_list.at(i);int curRowCount = curList.size();QStringList oneLineStrlist;for(int j = 0; j < curRowCount; j++){QString content = curList.at(j).toString();oneLineStrlist << content;}contentList << oneLineStrlist;}workbook->dynamicCall( "Close(Boolean)", false );excel->dynamicCall( "Quit(void)" );delete excel;//解析contentList,填充tableViewint rowCounts = contentList.size();QStandardItem   *aItem;//遍历行for(int i = 0; i < rowCounts; i++){QStringList tmpList=contentList[i];if(i == 0){//设置表头m_pItemModel->setHorizontalHeaderLabels(tmpList);}else{int j;for (j = 0; j < COLUMN_COUNT-1; j++){//不包含最后一列aItem=new QStandardItem(tmpList.at(j));m_pItemModel->setItem(i-1,j,aItem);  //为模型的某个行列位置设置Item}//设置最后一列aItem=new QStandardItem(contentList[0].at(j));  //获取最后一列的指针aItem->setCheckable(true); //设置可以使用check控件if (tmpList.at(j)=="0")aItem->setCheckState(Qt::Unchecked); //根据数据设置check状态elseaItem->setCheckState(Qt::Checked);m_pItemModel->setItem(i-1 , j, aItem); //设置最后一列}}
}//选择单元格变化时的响应
void Widget::OnCurrentChanged(const QModelIndex &current, const QModelIndex &previous)
{Q_UNUSED(previous);if (current.isValid()) //当前模型索引有效{ui->textEdit->clear();ui->textEdit->append(QString::asprintf(u8"当前单元格:%d行,%d列",current.row(),current.column())); //显示模型索引的行和列号QStandardItem   *aItem;aItem = m_pItemModel->itemFromIndex(current); //从模型索引获得Itemui->textEdit->append(u8"单元格内容:"+aItem->text()); //显示item的文字内容}
}//在表格最后一行添加
void Widget::on_btnAppendLast_clicked()
{QList<QStandardItem*> aItemList;QStandardItem   *aItem;for(int i=0; i < COLUMN_COUNT - 1;i++)  //不包含最后1列{aItem = new QStandardItem(u8"自定义");aItemList<<aItem;}//获取最后一列的表头文字QString str=m_pItemModel->headerData(m_pItemModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();aItem=new QStandardItem(str);aItem->setCheckable(true);aItemList<<aItem;   //添加到容器m_pItemModel->insertRow(m_pItemModel->rowCount(), aItemList); //插入一行,需要每个Cell的ItemQModelIndex curIndex = m_pItemModel->index(m_pItemModel->rowCount()-1, 0); //创建最后一行的ModelIndex//如果之前点击了表格,清空选择项m_pSelectionModel->clearSelection();//设置刚插入的行为当前选择行m_pSelectionModel->setCurrentIndex(curIndex, QItemSelectionModel::Select);
}void Widget::on_btnAppend_clicked()
{QList<QStandardItem*> aItemList;QStandardItem   *aItem;for(int i=0; i<COLUMN_COUNT-1; i++){aItem=new QStandardItem(u8"自定义");aItemList<<aItem;}//获取表头文字QString str = m_pItemModel->headerData(m_pItemModel->columnCount()-1,Qt::Horizontal,Qt::DisplayRole).toString();aItem = new QStandardItem(str);aItem->setCheckable(true);aItemList<<aItem;QModelIndex curIndex = m_pSelectionModel->currentIndex(); //获取当前选中项的模型索引m_pItemModel->insertRow(curIndex.row(),aItemList);  //在当前行的前面插入一行m_pSelectionModel->clearSelection();  //清除已有选择m_pSelectionModel->setCurrentIndex(curIndex,QItemSelectionModel::Select);
}//删除选择的行
void Widget::on_btnDeleteSelectedLine_clicked()
{QModelIndex curIndex=m_pSelectionModel->currentIndex();//获取当前选择单元格的模型索引if (curIndex.row() == m_pItemModel->rowCount()-1) //如果是最后一行{m_pItemModel->removeRow(curIndex.row()); //删除最后一行}else{m_pItemModel->removeRow(curIndex.row());  //删除一行,并重新设置当前选择行m_pSelectionModel->setCurrentIndex(curIndex, QItemSelectionModel::Select);}
}//将tableView的数据显示在textEdit
void Widget::on_btnReshowData_clicked()
{ui->textEdit->clear(); //清空QStandardItem   *aItem;QString str;//获取表头文字int i,j;for (i=0; i < m_pItemModel->columnCount(); i++){aItem = m_pItemModel->horizontalHeaderItem(i); //获取表头的一个项数据str=str+aItem->text()+"\t"; //用TAB间隔文字}ui->textEdit->append(str); //添加为文本框的一行//获取数据区的每行for (i=0; i < m_pItemModel->rowCount();i++){str="";for(j=0; j<m_pItemModel->columnCount()-1; j++){aItem = m_pItemModel->item(i,j);str = str + aItem->text()+QString::asprintf("\t"); //以 TAB分隔}aItem = m_pItemModel->item(i,j); //最后一行if (aItem->checkState()==Qt::Checked)str = str+"1";elsestr = str+"0";ui->textEdit->append(str);}
}

main.cpp

#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

运行结果

image-20240710195531422

三、Qt模型视图代理之:QListView的使用

  • QListView初始化

    QStringList strList;
    strList << u8"北京" << u8"上海" << u8"深圳" << u8"广东"<< u8"南京" << u8"苏州" << u8"西安";//创建数据模型
    m_pStringListModel = new QStringListModel(this);//为模型设置StringList,会导入StringList的内容
    m_pStringListModel->setStringList(strList);//为listView设置数据模型
    ui->listView->setModel(m_pStringListModel);
    
  • QListView末尾追加行

    //在尾部插入一空行, 不添加就把最后一行给替换了
    m_pStringListModel->insertRow(m_pStringListModel->rowCount());//获取最后一行
    QModelIndex index = m_pStringListModel->index(m_pStringListModel->rowCount()-1, 0);
    m_pStringListModel->setData(index,"new item", Qt::DisplayRole);  //设置显示文字//设置新添加的行选中
    ui->listView->setCurrentIndex(index);
    
  • QListView插入一行

    //获取选中 model Index
    QModelIndex index=ui->listView->currentIndex();//在当前行的前面插入一行
    m_pStringListModel->insertRow(index.row());
    m_pStringListModel->setData(index,"inserted item",Qt::DisplayRole);
    ui->listView->setCurrentIndex(index);
    
  • QListView删除选中的行

    //获取当前选中的modelIndex
    QModelIndex index = ui->listView->currentIndex();//删除当前行
    m_pStringListModel->removeRow(index.row());
    
  • QListView删除所有数据

    m_pStringListModel->removeRows(0, m_pStringListModel->rowCount());
    
  • QListView添加右键菜单

    //listview右键菜单
    ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->listView, &QListView::customContextMenuRequested, this, &Widget::on_showRightMenu);
    

示例:

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QStringListModel>
#include <QMenu>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:void initMenu();private slots:void on_btnAddItem_clicked();void on_btnDeleteItem_clicked();void on_btnInsert_clicked();void on_btnClearAllData_clicked();void on_btnReshow_clicked();void on_showRightMenu(const QPoint& pos);void OnActionDelete();//链接listview的clicked信号void on_listView_clicked(const QModelIndex &index);private:Ui::Widget *ui;QStringListModel*   m_pStringListModel;QMenu *m_pMenu;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QMenu>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle(u8"QListView使用教程");QStringList strList;strList << u8"北京" << u8"上海" << u8"深圳" << u8"广东"<< u8"南京" << u8"苏州" << u8"西安";//创建数据模型m_pStringListModel = new QStringListModel(this);//为模型设置StringList,会导入StringList的内容m_pStringListModel->setStringList(strList);//为listView设置数据模型ui->listView->setModel(m_pStringListModel);//设置listview编辑属性//双击与选择//ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);initMenu();//listview右键菜单ui->listView->setContextMenuPolicy(Qt::CustomContextMenu);connect(ui->listView, &QListView::customContextMenuRequested, this, &Widget::on_showRightMenu);
}Widget::~Widget()
{delete ui;
}//添加item
void Widget::on_btnAddItem_clicked()
{//在尾部插入一空行, 不添加就把最后一行给替换了m_pStringListModel->insertRow(m_pStringListModel->rowCount());//获取最后一行QModelIndex index = m_pStringListModel->index(m_pStringListModel->rowCount()-1, 0);m_pStringListModel->setData(index,"new item", Qt::DisplayRole);  //设置显示文字//设置新添加的行选中ui->listView->setCurrentIndex(index);
}//删除选中的项
void Widget::on_btnDeleteItem_clicked()
{//获取当前选中的modelIndexQModelIndex index = ui->listView->currentIndex();//删除当前行m_pStringListModel->removeRow(index.row());
}//插入一项
void Widget::on_btnInsert_clicked()
{//获取选中 model IndexQModelIndex index=ui->listView->currentIndex();//在当前行的前面插入一行m_pStringListModel->insertRow(index.row());m_pStringListModel->setData(index,"inserted item",Qt::DisplayRole);ui->listView->setCurrentIndex(index);
}//回显listview数据
void Widget::on_btnReshow_clicked()
{//获取数据模型的StringListQStringList tmpList = m_pStringListModel->stringList();ui->textEdit->clear(); //文本框清空for (int i=0; i<tmpList.count();i++){//显示数据模型的StringList()返回的内容ui->textEdit->append(tmpList.at(i));}
}//清除所有数据
void Widget::on_btnClearAllData_clicked()
{m_pStringListModel->removeRows(0, m_pStringListModel->rowCount());
}void Widget::initMenu()
{m_pMenu = new QMenu(ui->listView);QAction *pAc1 = new QAction(u8"删除", ui->listView);QAction *pAc2 = new QAction(u8"插入", ui->listView);QAction *pAc3 = new QAction(u8"置顶", ui->listView);QAction *pAc4 = new QAction(u8"排到最后", ui->listView);m_pMenu->addAction(pAc1);m_pMenu->addAction(pAc2);m_pMenu->addAction(pAc3);m_pMenu->addAction(pAc4);//注意在exec前链接信号槽,因为exec会阻塞主线程,//如果connect写在exec代码之后,信号槽将无法链接connect(pAc1, &QAction::triggered, this, &Widget::OnActionDelete);
}void Widget::on_showRightMenu(const QPoint& pos)
{if(!((ui->listView->selectionModel()->selectedIndexes()).empty())){m_pMenu->exec(QCursor::pos());//在当前鼠标位置显示}
}void Widget::OnActionDelete()
{//获取当前 modelIndexQModelIndex index = ui->listView->currentIndex();//删除当前行m_pStringListModel->removeRow(index.row());
}void Widget::on_listView_clicked(const QModelIndex &index)
{ui->textEdit->clear(); //文本框清空//显示QModelIndex的行、列号ui->textEdit->append(QString::asprintf(u8"当前项:row=%d, column=%d",index.row(),index.column()));
}

main.cpp

#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

运行结果

image-20240710202833881

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/44371.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析C++11:现代特性和应用

目录 一.c11.简介二.列表初始化和initializer_list1.列表初始化2.initializer_list 三.简化声明1.auto2.decltype 四.新增容器1.array2.forward_list3.unordered_map/set 五.右值引用与移动语义1.左值和右值2.左值引用3.右值引用4.移动构造和移动赋值5.万能引用和引用折叠6.完美…

git-工作场景

1. 远程分支为准 强制切换到远程分支并忽略本地未提交的修改 git fetch origin # 获取最新的远程分支信息 git reset --hard origin/feature_server_env_debug_20240604 # 强制切换到远程分支&#xff0c;并忽略本地修改 2. 切换分支 1. **查看所有分支&#xff1a;**…

mount卡住(失败)解决方案

mount -a卡主 第一步确保两边都打开了NFS服务&#xff01;&#xff01;&#xff01;&#xff01; 客户端执行mount -av 查看信息是拒绝服务 查看服务端&#xff1a;showmount -e 192.168.25.168 看提示信息处理&#xff0c;关闭两端的防火钱 遇到这个错误就是服务端不让客户端…

JAVA--SpringCloud

SpringCloud基础 为什么需要spring cloud 单体结构--Monolith 首先请回想一下我们所开发的服务是什么样子的。通常情况下&#xff0c;这个服务所对应的代码由多个项目&#xff08;模块&#xff09;所组成&#xff0c;各个项目会根据自身所提供功能的不同具有一个明确的边界。…

C++类与对象-基础篇

目录 一、什么是类 1.1 语法定义 1.2 访问限定符 1.3 类域 二、类的实例化 2.1 什么是实例化 2.2 类的大小 三、this指针 3.1 引入 3.2 this指针的使用 一、什么是类 1.1 语法定义 class 类名 {}; 说明 类似于C语言中的结构体&#xff0c;括号后分号不能丢类内成员可…

算术运算符用途解析及应用案例

文章目录 常用的算术运算符及其用途&#xff1a;运算符优先级类型转换高级用法 应用案例1. 计算器程序2. 平方根计算3. 计算平均数和标准差4. 货币兑换5. 计算几何6. 动力学模拟7. 数字图像处理8. 金融计算&#xff1a;复利计算 常用的算术运算符及其用途&#xff1a; 算术运算…

前端面试题40(浅谈MVVM双向数据绑定)

MVVM&#xff08;Model-View-ViewModel&#xff09;架构模式是一种用于简化用户界面&#xff08;UI&#xff09;开发的软件架构设计模式&#xff0c;尤其在现代前端开发中非常流行&#xff0c;例如在使用Angular、React、Vue.js等框架时。MVVM模式源于经典的MVC&#xff08;Mod…

软件架构之软件架构概述及质量属性

软件架构之软件架构概述及质量属性 第 9 章&#xff1a;软件架构设计9.1 软件架构概述9.1.1 软件架构的定义9.1.2 软件架构的重要性9.1.3 架构的模型 9.2 架构需求与软件质量属性9.2.1 软件质量属性9.2.2 6 个质量属性及实现 第 9 章&#xff1a;软件架构设计 像学写文章一样&…

前后端如何实现非对称加解密-使用RSA为例讲解!

写在最前面&#xff0c;RSA是一种非对称加密算法&#xff0c;使用不同的公钥和私钥进行加密和解密。 下面是使用RSA进行加密和解密的代码示例&#xff1a; 前端&#xff1a;使用CryptoJS进行RSA加密 在前端JavaScript中&#xff0c;使用jsencrypt库来进行RSA加密&#xff1a…

领夹麦克风哪个品牌好,哪个麦克风好,热门无线麦克风品牌推荐

​无线领夹麦克风是现代沟通的重要工具&#xff0c;它不仅提高了语音交流的清晰度&#xff0c;还展现了使用者的专业形象。随着技术发展&#xff0c;这些麦克风已经变得更加轻便、时尚&#xff0c;易于使用。在各种场合&#xff0c;如演讲、教育和网络直播中&#xff0c;当然&a…

Matplotlib入门

#折线图用来表示数据的变化 plt.plot(x,y) #直方图用来统计连续性数据 无间隔 plt.hist(data数组,组数) #条形图用来统计离散的数组 并且反映其变化 有间隔 plt.bar(x,y,width 0.3) plt.barh(y,x,height 0.3) #散点图用来xy轴之间的联系 趋势 plt.scatter(x,y) #导入p…

重命名文件的方法有哪些?重命名文件的工具有哪些?

在日常的计算机使用过程中&#xff0c;重命名文件是一项常见但至关重要的任务。无论是为了更好地组织文件、修复命名错误&#xff0c;还是简化文件管理流程&#xff0c;掌握正确的重命名方法和工具都能显著提升效率。 本文将探讨多种重命名文件的方法&#xff0c;同时介绍几款高…

解决Invalid or unsupported by client SCRAM mechanisms(dbeaver)

在用工具&#xff08;dbeaver&#xff09;链接Opengauss数据库的时候&#xff0c;报出标题的错误。原因为驱动不正确。 驱动下载地址&#xff1a;https://opengauss.org/zh/download/ 下载完的包 &#xff0c;解压后&#xff0c;里面应该有两个jar 包,使用postgresql.jar dbe…

国产大模型第一梯队玩家,为什么pick了CPU?

AI一天&#xff0c;人间一年。 现在不论是大模型本身&#xff0c;亦或是AI应用的更新速度简直令人直呼跟不上—— Sora、Suno、Udio、Luma……重磅应用一个接一个问世。 也正如来自InfoQ的调查数据显示的那般&#xff0c;虽然AIGC目前还处于起步阶段&#xff0c;但市场规模已…

qmt量化交易策略小白学习笔记第55期【qmt编程之期权数据--获取历史期权列表】

qmt编程之获取期权数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;咨询免费开通量化回测与获取实盘权限&#xff0c;欢迎和博主联系&#xff01; 获取历史期权列表 …

教师管理小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;教师管理&#xff0c;个人认证管理&#xff0c;课程信息管理&#xff0c;课堂记录管理&#xff0c;课堂统计管理&#xff0c;留言板管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;课程信息…

Postman使用教程【项目实战】

目录 引言软件下载及安装项目开发流程1. 创建项目2. 创建集合(理解为&#xff1a;功能模块)3. 设置环境变量&#xff0c;4. 创建请求5. 测试脚本6. 响应分析7. 共享与协作 结语 引言 Postman 是一款功能强大的 API 开发工具&#xff0c;它可以帮助开发者测试、开发和调试 API。…

java项目总结数据库

1.什么是数据库 用于存储和管理数据的仓库 2.数据库的特点 1.持久化存储数据。确实数据库就是一个文件系统。 2.便于存储和管理数据 3.使用统一的方式操作数据库 --SQL 3.MqSql服务启动 4.登录和退出 这里的ip值IP地址 5.客户端与服务器关系 6.目录结构 7.SQL 1.什么是SQL&…

节点流与处理流:深入解析Java中的IO流

节点流与处理流&#xff1a;深入解析Java中的IO流 1、节点流&#xff08;Node Stream&#xff09;1.1 定义1.2 好处1.3 示例 2、处理流&#xff08;Processing Stream&#xff09;2.1 定义2.2 好处2.3 创建特征2.4 示例 3、总结 &#x1f496;The Begin&#x1f496;点点关注&…

JavaWeb__正则表达式

目录 1. 正则表达式简介2. 正则表达式体验2.1 验证2.2 匹配2.3 替换2.4 全文查找2.5 忽略大小写2.6 元字符使用2.7 字符集合的使用2.8 常用正则表达式 1. 正则表达式简介 正则表达式是描述字符模式的对象。正则表达式用于对字符串模式匹配及检索替换&#xff0c;是对字符串执行…