Qt创建右键菜单的方法

Qt创建右键菜单的方法函数所有派生自QWidget的控件,若实现在其上右键单击弹出菜单,需要设置该控件的上下文菜单策略函数为setContextMenuPolicy(Qt::CustomContextMenu),设置

函数
所有派生自QWidget的控件,若实现在其上右键单击弹出菜单,需要设置该控件的上下文菜单策略函数为setContextMenuPolicy(Qt::CustomContextMenu),设置该策略后当用户右键点击控件时qt会发送一个信号customContextMenuRequested() ,给这个信号设置相应的槽函数,在槽函数中将菜单展示出来就行了。

涉及相关函数其原型如下:

void QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy)
void QWidget::customContextMenuRequested(const QPoint & pos)
释义
若要让控件支持右键菜单,必须设置此函数。参数中枚举类型ContextMenuPolicy用来设置与显示菜单相关的策略,其中枚举项ContextMenuPolicy::CustomContextMenu表示右键点击控件时qt会发出一个信号,该信号为customContextMenuRequested() 。
是一个信号函数,在右键点击控件时由qt自动发出。注意必须设置策略为setContextMenuPolicy(Qt::CustomContextMenu)才行。
示例
对需要支持右键弹出菜单的控件做如下设置:

setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &ProjectTreeView::customContextMenuRequested, this, &ProjectTreeView::open_menu);
open_menu即为自定义小部件类ProjectTreeView中创建的槽函数,该槽函数的实现为:

#include “QMenu”
void ProjectTreeView::open_menu(const QPoint & pos)
{

    QMenu menu;
    menu.addAction(“aaa”);
    menu.addAction(“bbb”);
    menu.addAction(“ccc”);
    menu.exec(QCursor::pos());//以阻塞方式显示菜单,参数可指示菜单显示位置,另外该函数可返回单击选中项
}
运行后,在小部件上右键单击后截图为:

Qt创建右键菜单的方法

 

/*********************************************

qt右键菜单的两种方式
重写事件处理接口 contextMenuEvent
绑定信号与槽
qt右键菜单的两种方式
重写事件处理接口 contextMenuEvent
第一步

准备菜单

//定义菜单
QMenu *RightButtonMenu; //右键菜单

QAction *saveSreenShot; //保存到粘贴板
QAction *saveCopyAs;    //另存为
QAction *quitSreenShot; //退出截图

初始化菜单

saveSreenShot = new QAction(tr(“保存截图”), this);
//connect(saveSreenShot, SIGNAL(triggered()), this, SLOT(SaveSrceenShot()));
quitSreenShot = new QAction(tr(“退出截图”), this);
//connect(quitSreenShot, SIGNAL(triggered()), this, SLOT(MenuClose()));
saveCopyAs = new QAction(tr(“另存为”),this);
//connect(saveCopyAs, SIGNAL(triggered()), this, SLOT(SaveCopyAs()));
RightButtonMenu = new QMenu(this);

第二步

添加右键处理事件

virtual void contextMenuEvent(QContextMenuEvent *event);    //右键菜单
第三步

菜单停靠指定位置等待选择

void Widget::contextMenuEvent(QContextMenuEvent *event)
{

    Q_UNUSED(event)
    RightButtonMenu->clear();
    RightButtonMenu->addAction(saveSreenShot);
    RightButtonMenu->addAction(saveCopyAs);
    RightButtonMenu->addSeparator();    //分割线
    RightButtonMenu->addAction(quitSreenShot);
    RightButtonMenu->exec(QCursor::pos());  //在当前鼠标处堵住
}

绑定信号与槽
第一步

定义响应槽函数

private slots:
    void _SlotPlayArgsMenu(const QPoint pos);

第二步
绑定信号与槽

this->setContextMenuPolicy(Qt::CustomContextMenu);//添加右键菜单策略
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(_SlotPlayArgsMenu(QPoint)));

第三步

菜单停靠指定位置等待选择

void Widget::_SlotPlayArgsMenu(const QPoint pos)
{

    qDebug()<<pos<<endl;
    RightButtonMenu->clear();
    RightButtonMenu->addAction(saveSreenShot);
    RightButtonMenu->addAction(saveCopyAs);
    RightButtonMenu->addSeparator();    //分割线
    RightButtonMenu->addAction(quitSreenShot);
    RightButtonMenu->exec(QCursor::pos());  //在当前鼠标处堵住
}
 

/********************************

Qt上下文菜单(右键菜单)的几种方式,setContextMenuPolicy(Qt::ContextMenuPolicy policy)函数5个参数的用法

设置右健菜单的方法原型:void setContextMenuPolicy(Qt::ContextMenuPolicy policy),参数有5个枚举值,说明如下:

序号    值    说明
1    Qt::NoContextMenu    小部件没有上下文菜单,上下文菜单的处理被推迟到小部件的父级
2    Qt::PreventContextMenu    该小部件没有上下文菜单,与NoContextMenu相反,处理不会被推迟到小部件的父级。所有鼠标右键事件都保证通过void QAbstractItemView::mousePressEvent(QMouseEvent *event) override和void QAbstractItemView::mouseReleaseEvent(QMouseEvent *event) override传递给小部件本身
3    Qt::DefaultContextMenu    小部件的QWidget::contextMenuEvent()处理程序被调用
4    Qt::ActionsContextMenu    小部件将其QWidget::actions()显示为上下文菜单
5    Qt::CustomContextMenu    小部件发出QWidget::customContextMenuRequested()信号
首先,新建Qt工程,主界面设计如下(其实界面不用设计的这么复杂只要主界面上放一个QTableView部件就可以,我是本来设计好的,直接拿来用了):

然后,用Qt的Action Editor设计几个QAction对象的指针,如下:

主界面类头文件filelistform.h如下:

#ifndef FILELISTFORM_H
#define FILELISTFORM_H

#include <QWidget>
#include “customtableview.h”

class FileListForm : public QWidget
{

    Q_OBJECT
public:
    explicit FileListForm(QWidget *parent = nullptr);
    ~FileListForm();
private:
    Ui::FileListForm *ui;
protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void contextMenuEvent(QContextMenuEvent *event) override;
};
#endif // FILELISTFORM_H

主界面类cpp文件filelistform.cpp如下:

#include “filelistform.h”
#include “ui_filelistform.h”
#include <QDebug>

FileListForm::FileListForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FileListForm)
{

    ui->setupUi(this);

    ui->tableView->addAction(ui->insertRowAction);
    ui->tableView->addAction(ui->deleteRowAction);
    ui->tableView->addAction(ui->submitAction);
    ui->tableView->addAction(ui->revertAction);
    ui->tableView->addAction(ui->fileToUnitAction);
    ui->tableView->addAction(ui->selectAction);
    ui->tableView->setContextMenuPolicy(Qt::PreventContextMenu);
}

void FileListForm::mousePressEvent(QMouseEvent *event)
{

    const QString str = QString(“(%1,%2)”).arg(event->x()).arg(event->y());
    if(event->button()==Qt::LeftButton)
    {

        QMessageBox::information(this,”鼠标操作提示”,”鼠标左键按下:”+tr(str.toLocal8Bit()),tr(“ok”));
    }else if(event->button()==Qt::RightButton)
    {

        // 在这里也可以定义右键菜单
         qDebug() << “父部件mousePressEvent函数调用,鼠标右键按下:”+tr(str.toLocal8Bit());
    }else if(event->button()==Qt::MidButton)
    {

        QMessageBox::information(this,”鼠标操作提示”,”鼠标中键按下:”+tr(str.toLocal8Bit()),tr(“ok”));
    }
}

void FileListForm::mouseReleaseEvent(QMouseEvent *event)
{

    const QString str = QString(“(%1,%2)”).arg(event->x()).arg(event->y());
    if(event->button()==Qt::LeftButton)
    {

        QMessageBox::information(this,”父部件鼠标操作提示”,”鼠标左键弹起:”+tr(str.toLocal8Bit()),tr(“ok”));
    }else if(event->button()==Qt::RightButton)
    {

        qDebug() << “父部件mouseReleaseEvent函数调用:鼠标右键弹起:”+tr(str.toLocal8Bit());
    }else if(event->button()==Qt::MidButton)
    {

        QMessageBox::information(this,”父部件鼠标操作提示”,”鼠标中键弹起:”+tr(str.toLocal8Bit()),tr(“ok”));
    }
    event->accept();
}

void FileListForm::contextMenuEvent(QContextMenuEvent *event)
{

    qDebug() << “父部件contextMenuEvent函数调用”;
    QAction *pact1 = new QAction(“parent_action1”);
    QAction *pact2 = new QAction(“parent_action2”);
    QAction *pact3 = new QAction(“parent_action3”);
    QAction *pact4 = new QAction(“parent_action4”);

    QMenu *pmenu =new QMenu();
    pmenu->addAction(pact1);
    pmenu->addAction(pact2);
    pmenu->addAction(pact3);
    pmenu->addAction(pact4);

    pmenu->exec(event->globalPos());

    event->accept();
}

FileListForm::~FileListForm()
{

    delete ui;
}

重写QTableView类,头文件customtableview.h如下

#ifndef CUSTOMTABLEVIEW_H
#define CUSTOMTABLEVIEW_H

#include <QtWidgets>
class CustomTableView : public QTableView
{

public:
    CustomTableView(QWidget *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void contextMenuEvent(QContextMenuEvent *event) override;

private slots:
    void ShowcustomContextMenu(const QPoint &pos);
};

#endif // CUSTOMTABLEVIEW_H

cpp源文件customtableview.cpp如下

#include “customtableview.h”

CustomTableView::CustomTableView(QWidget *parent):QTableView(parent)
{

    connect(this,&CustomTableView::customContextMenuRequested,this,&CustomTableView::ShowcustomContextMenu);
}

void CustomTableView::mousePressEvent(QMouseEvent *event)
{

    const QString str = QString(“(%1,%2)”).arg(event->x()).arg(event->y());
    if(event->button()==Qt::LeftButton)
    {

        QMessageBox::information(this,”Table鼠标操作提示”,”鼠标左键按下:”+tr(str.toLocal8Bit()),tr(“ok”));
    }else if(event->button()==Qt::RightButton)
    {

        qDebug() << “Table小部件mousePressEvent函数调用:鼠标右键按下:”+tr(str.toLocal8Bit());
    }else if(event->button()==Qt::MidButton)
    {

        QMessageBox::information(this,”Table鼠标操作提示”,”鼠标中键按下:”+tr(str.toLocal8Bit()),tr(“ok”));
    }
    event->accept();
}

void CustomTableView::mouseReleaseEvent(QMouseEvent *event)
{

    const QString str = QString(“(%1,%2)”).arg(event->x()).arg(event->y());
    if(event->button()==Qt::LeftButton)
    {

        QMessageBox::information(this,”Table鼠标操作提示”,”鼠标左键弹起:”+tr(str.toLocal8Bit()),tr(“ok”));
    }else if(event->button()==Qt::RightButton)
    {

        // 在这里也可以定义右键菜单
        /*
        QAction *pact1 = new QAction(“RightButton_action1”);
        QAction *pact2 = new QAction(“RightButton_action2”);
        QAction *pact3 = new QAction(“RightButton_action3”);
        QAction *pact4 = new QAction(“RightButton_action4”);

        QMenu *pmenu =new QMenu();
        pmenu->addAction(pact1);
        pmenu->addAction(pact2);
        pmenu->addAction(pact3);
        pmenu->addAction(pact4);

        pmenu->exec(event->globalPos());
        */
        qDebug() << “Table小部件mouseReleaseEvent函数调用:鼠标右键弹起:”+tr(str.toLocal8Bit());
    }else if(event->button()==Qt::MidButton)
    {

        QMessageBox::information(this,”Table鼠标操作提示”,”鼠标中键弹起:”+tr(str.toLocal8Bit()),tr(“ok”));
    }
    event->accept();
}

void CustomTableView::contextMenuEvent(QContextMenuEvent *event)
{

    qDebug() << “Table小部件contextMenuEvent函数调用”;
    QAction *pact1 = new QAction(“contextMenuEvent_action1”);
    QAction *pact2 = new QAction(“contextMenuEvent_action2”);
    QAction *pact3 = new QAction(“contextMenuEvent_action3”);
    QAction *pact4 = new QAction(“contextMenuEvent_action4”);

    QMenu *pmenu =new QMenu();
    pmenu->addAction(pact1);
    pmenu->addAction(pact2);
    pmenu->addAction(pact3);
    pmenu->addAction(pact4);

    pmenu->exec(event->globalPos());

    event->accept();
}

void CustomTableView::ShowcustomContextMenu(const QPoint &pos)
{

    qDebug() << “Table小部件ShowcustomContextMenu函数调用”;
    QAction *pact1 = new QAction(“custom_action1”);
    QAction *pact2 = new QAction(“custom_action2”);
    QAction *pact3 = new QAction(“custom_action3”);
    QAction *pact4 = new QAction(“custom_action4”);

    QMenu *pmenu =new QMenu();
    pmenu->addAction(pact1);
    pmenu->addAction(pact2);
    pmenu->addAction(pact3);
    pmenu->addAction(pact4);

    pmenu->exec(this->mapToGlobal(pos +
           QPoint(this->verticalHeader()->width(),this->horizontalHeader()->height())));
}

接下来很关键的一步,右键点击QTableView小部件“提升为…”,为QTableView提升为CustomTableView类的对象,提升完毕后,再次右键点击QTableView小部件,变成如下菜单,说明提升成功。

再接着,我们用上面表格中的五个参数分别进行实验

1、当参数和为Qt::NoContextMenu时:
ui->tableView->setContextMenuPolicy(Qt::NoContextMenu);
QTableView小部件不显示右键菜单,程序执行的顺序是:先调用CustomTableView表格小部件的mousePressEven()函数,再调用CustomTableView表格小部件的mouseReleaseEvent()函数,最后调用其父部件(FileListForm类,主界面)contextMenuEvent()函数,显示父部件的上下文菜单:

上下文菜单如下:

2、 当参数和为Qt::PreventContextMenu时:
ui->tableView->setContextMenuPolicy(Qt::PreventContextMenu);
1
QTableView小部件不显示右键菜单,程序执行的顺序是:先调用CustomTableView表格小部件的mousePressEven()函数,再调用CustomTableView表格小部件的mouseReleaseEvent()函数:

3、当参数和为Qt::DefaultContextMenu 时:

ui->tableView->setContextMenuPolicy(Qt::DefaultContextMenu);

程序执行的顺序是:先调用CustomTableView表格小部件的mousePressEven()函数,再调用CustomTableView表格小部件的mouseReleaseEvent()函数,再执行CustomTableView表格小部件contextMenuEvent()函数,显示右键菜单:

上下文菜单如下:

4、当参数和为Qt::ActionsContextMenu时:

ui->tableView->setContextMenuPolicy(Qt::ActionsContextMenu);
程序执行的顺序是:先调用CustomTableView表格小部件的mousePressEven()函数,再调用CustomTableView表格小部件的mouseReleaseEvent()函数,显示右键Actions菜单,即通过下面代码生成的菜单:

ui->tableView->addAction(ui->insertRowAction);
ui->tableView->addAction(ui->deleteRowAction);
ui->tableView->addAction(ui->submitAction);
ui->tableView->addAction(ui->revertAction);
ui->tableView->addAction(ui->fileToUnitAction);
ui->tableView->addAction(ui->selectAction);

执行顺序截图:

上下文菜单如下:

5、当参数和为Qt::CustomContextMenu时:

ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);

程序执行的顺序是:先调用CustomTableView表格小部件的mousePressEven()函数,再调用CustomTableView表格小部件的mouseReleaseEvent()函数,然后CustomTableView表格小部件再发出customContextMenuRequested信号,根据下面connect代码,调用ShowcustomContextMenu槽函数,显示右键菜单

connect(this,&CustomTableView::customContextMenuRequested,this,&CustomTableView::ShowcustomContextMenu);
1
代码调用顺序截图:

/******************************************************

QWidget及其子类都可有右键菜单,因为QWidget有以下两个与右键菜单有关的函数:

Qt::ContextMenuPolicy contextMenuPolicy () const

void setContextMenuPolicy ( Qt::ContextMenuPolicy policy )

Qt::ContextMenuPolicy枚举类型包括:Qt::DefaultContextMenu, Qt::NoContextMenu, Qt::PreventContextMenu, Qt::ActionsContextMenu, and Qt::CustomContextMenu。

使用方式如下:

1)默认是Qt::DefaultContextMenu。
它是利用右键菜单事件contextMenuEvent()来处理(which means the contextMenuEvent() handler is called)。就是要重写contextMenuEvent( QContextMenuEvent * event )函数。

2)使用Qt::CustomContextMenu。
它是发出QWidget::customContextMenuRequested信号,注意仅仅只是发信号,意味着要自己写显示右键菜单的slot。
这个信号是QWidget唯一与右键菜单有关的信号(也是自有的唯一信号),同时也是很容易被忽略的signal:

void customContextMenuRequested ( const QPoint & pos )

该信号的发出条件是:用户请求contextMenu(常规就是鼠标右击啦)且同时被击的widget其contextMenuPolicy又是Qt::CustomContextMenu。
注意:pos是该widget接收右键菜单事件的位置,一般是在该部件的坐标系中。但是对于QAbstratScrollArea及其子类例外,是对应着其视口viewport()的坐标系。如常用的QTableView、QHeaderView就是QAbstratScrollArea的子类。
因为仅发信号,所以需自己写显示右键菜单的slot来响应,例如一个表格(QTableView类型)表头的显示右键菜单槽:
datatable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(datatable->horizontalHeader(), SIGNAL(customContextMenuRequested(const QPoint&)),
        this, SLOT(show_contextmenu(const QPoint&)));//this是datatable所在窗口
QMenu *cmenu = NULL;
show_contextmenu(const QPoint& pos)
{

    if(cmenu)//保证同时只存在一个menu,及时释放内存
    {

        delete cmenu;
        cmenu = NULL;
    }
    QMenu cmenu = new QMenu(datatable->horizontalHeader());
   
    QAction *ascendSortAction = cmenu->addAction(“升序”);
    QAction *descendSortAction = cmenu->addAction(“降序”);
    QAction *filterAction = cmenu->addAction(“过滤”);
    QAction *reshowAction = cmenu->addAction(“重载”);
   
    connect(ascendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_ascend()));
    connect(descendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_descend()));
    connect(filterAction, SIGNAL(triggered(bool)), this, SLOT(show_filter_dlg()));
    connect(reshowAction, SIGNAL(triggered(bool)), this, SLOT(reshow_data()));
   
    cmenu->exec(QCursor::pos());//在当前鼠标位置显示
    //cmenu->exec(pos)是在viewport显示
}

也可先做好cmenu,好处是始终使用一个:
    QMenu cmenu = new QMenu(datatable->horizontalHeader());
   
    QAction *ascendSortAction = cmenu->addAction(“升序”);
    QAction *descendSortAction = cmenu->addAction(“降序”);
    QAction *filterAction = cmenu->addAction(“过滤”);
    QAction *reshowAction = cmenu->addAction(“重载”);
   
    connect(ascendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_ascend()));
    connect(descendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_descend()));
    connect(filterAction, SIGNAL(triggered(bool)), this, SLOT(show_filter_dlg()));
    connect(reshowAction, SIGNAL(triggered(bool)), this, SLOT(reshow_data()));
show_contextmenu(const QPoint& pos)
{

    if(cmenu)
    {

        cmenu->exec(QCursor::pos());
    }
}

3)使用Qt::ActionsContextMenu。
把部件的所有action即QWidget::actions()作为context menu显示出来。
还是上面的例子,要在表格(QTableView类型)表头显示右键菜单:
        QAction *ascendSortAction = new QAction(“升序”, this);
        QAction *descendSortAction = new QAction(“降序”, this);
        QAction *filterAction = new QAction(“过滤”, this);
        QAction *unfilterAction = new QAction(“取消过滤”, this);
   
        connect(ascendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_ascend()));
        connect(descendSortAction, SIGNAL(triggered(bool)), this, SLOT(sort_descend()));
        connect(filterAction, SIGNAL(triggered(bool)), this, SLOT(filter_table()));
        connect(unfilterAction, SIGNAL(triggered(bool)), this, SLOT(unfilter_table()));
   
        datatable->horizontalHeader()->addAction(ascendSortAction);
        datatable->horizontalHeader()->addAction(descendSortAction);
        datatable->horizontalHeader()->addAction(filterAction);
        datatable->horizontalHeader()->addAction(unfilterAction);
        
        datatable->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);

另外两个就是不显示context menu了:
Qt::NoContextMenu
    the widget does not feature a context menu, context menu handling is deferred to the widget’s parent.
   
Qt::PreventContextMenu
    the widget does not feature a context menu, and in contrast to NoContextMenu, the handling is not deferred to the widget’s parent. This means that all right mouse button events are guaranteed to be delivered to the widget itself through mousePressEvent(), and mouseReleaseEvent().

补充:
    使用Qt::ActionsContextMenu比较简洁,但是如果需要根据当前菜单弹出的位置来定义不同菜单,或者像上个例子,在表格(QTableView类型)表头显示右键菜单时,我需要知道是哪一列表头被点击,从而在后来调用sort_ascend()排序函数时能够根据不同列进行不同排序策略,那么Qt::ActionsContextMenu就做不到了。
    这种需要捕捉弹出位置的情况只好用Qt::ActionsContextMenu了,customContextMenuRequested ( const QPoint & pos )信号返回点击位置pos(在表头视口坐标系中位置),然后表头即可调用logicalIndexAt(pos)函数得到被点击section对应的index即被点击部分的列号,然后存下来可供后面action激活的排序槽使用。
show_contextmenu(const QPoint& pos)
{

    //get related column of headerview
    contextmenu_column = datatable->horizontalHeader()->logicalIndexAt(pos);

    //show contextmenu
    if(cmenu)
    {

        cmenu->exec(QCursor::pos());
    }
}

【相关问题】

1.问题:当把 QTreeWidget 放在别的 QWidget 中以后,发现右键总是捕捉不到最下面的一个Item 节点。

错误代码:

void ShowItems::contextMenuEvent ( QContextMenuEvent * event )

{

    QPoint p = tree->mapFromGlobal(QCursor::pos());
    if(tree->itemAt(p) != NULL)
    {

        QMenu* popMenu = new QMenu(this);
        popMenu->addAction(insertItemAction);
        popMenu->addAction(deleteItemAction);
        popMenu->exec(QCursor::pos());
    }
}
原因:我们认为的(0,0)点的坐标与实际的有差距

解决:使用viewport()返回的QWidget作为当前的坐标QWidget

QWidget * QAbstractScrollArea::viewport () const
修改后,代码:

void ShowItems::contextMenuEvent (QContextMenuEvent *event )

{

    QWidget *w = tree->viewport();
    QPoint p = w->mapFromGlobal(QCursor::pos());
    if(tree->itemAt(p) != NULL)
    {

        QMenu* popMenu = new QMenu(this);
        popMenu->addAction(insertItemAction);
        popMenu->addAction(deleteItemAction);
        popMenu->exec(QCursor::pos());
    }
}
/*****************************************

今天的文章Qt创建右键菜单的方法分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/68214.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注