Qt4.8.2 右键弹出菜单及子窗口弹出位置不正确

一、关联菜单策略     QWidget:: contextMenuPolicy

帮助文档:

This property holds how the widget shows a context menu.

The default value of this property is Qt::DefaultContextMenu, which means the contextMenuEvent() handler is called. Other values are Qt::NoContextMenu,Qt::PreventContextMenuQt::ActionsContextMenu, and Qt::CustomContextMenu. With Qt::CustomContextMenu, the signal customContextMenuRequested() is emitted.

中文大概是说:

这个属性(contextMenuPolicy)决定了如何显示上下文菜单(通常是右键弹出菜单)。默认的属性值是Qt::DefaultContextMenu,这意味着contextMenuEvent()函数将会被调用。

其他属性值值:

Qt::NoContextMenu,Qt::PreventContextMenu, Qt::ActionsContextMenu, Qt::CustomContextMenu. Qt::CustomContextMenu,将会发出信号customContextMenuRequested()。

方法一、void QWidget::contextMenuEvent ( QContextMenuEvent * event ) [virtual protected]

This event handler, for event event, can be reimplemented in a subclass to receive widget context menu events.

The handler is called when the widget's contextMenuPolicy is Qt::DefaultContextMenu.

The default implementation ignores the context event. See the QContextMenuEvent documentation for more details.

中文意思:这个事件处理函数,可以被QWidget的子类重新实现去接收上下文菜单事件。当属性contextMenuPolicy 的值是Qt::DefaultContextMenu的时候,这个函数才会被调用。默认的函数实现忽略了上下文菜单事件。

所以,我们重载contextMenuEvent函数实现上下文菜单

mainWidget.h

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QtGui/QWidget>

class QMenu;

class mainWidget : public QWidget
{
    Q_OBJECT
    
public:
    mainWidget(QWidget *parent = 0);
    ~mainWidget();

private:
    QMenu *menuTest;


protected:
    virtual void contextMenuEvent(QContextMenuEvent *);
};

#endif // MAINWIDGET_H


mainWidget.cpp

 

#include "mainwidget.h"
#include <QMenu>
#include <QAction>
#include <QContextMenuEvent>
mainWidget::mainWidget(QWidget *parent)
    : QWidget(parent)
{
    menuTest=new QMenu(this);
    QAction *actionTest=new QAction("test",this);
    menuTest->addAction(actionTest);
}

mainWidget::~mainWidget()
{
    
}

void mainWidget::contextMenuEvent(QContextMenuEvent *event)
{
    menuTest->exec(event->globalPos());
}


Qt4.8.2 右键弹出菜单及子窗口弹出位置不正确_第1张图片

这是可以实现的。

方法二、信号customContextMenuRequested

为了发出这个信号,我们设置setContextMenuPolicy(Qt::CustomContextMenu);

并且关联QObject::connect(this,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(showMenu(QPoint)));

showMenu(QPoint)是自定义的一个信号槽,处理菜单的显示。

mainWidget.h

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QtGui/QWidget>

class QMenu;

class mainWidget : public QWidget
{
    Q_OBJECT
    
public:
    mainWidget(QWidget *parent = 0);
    ~mainWidget();

private:
    QMenu *menuTest;

private slots:
    void showMenu(QPoint);
};

#endif // MAINWIDGET_H


mainWidget.cpp

#include "mainwidget.h"
#include <QMenu>
#include <QAction>
#include <QContextMenuEvent>
mainWidget::mainWidget(QWidget *parent)
    : QWidget(parent)
{
    menuTest=new QMenu(this);
    QAction *actionTest=new QAction("test",this);
    menuTest->addAction(actionTest);

    //必须设置此属性,否则不会发出信号customContextMenuRequested
    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(showMenu(QPoint)));
}

mainWidget::~mainWidget()
{
    
}

//private slot
void mainWidget::showMenu(QPoint p)
{
    /*
      p是相对于窗体坐标系的,即p的坐标相对与窗体的左上角坐标(0,0)
      如果窗体左上角为(100,100),p为(10,20)
      则mapToGlobal(p)为(110,120)
      经过坐标转化,才能在正确的位置显示菜单
    */
    menuTest->exec(mapToGlobal(p));
}

 

在窗体中创建弹出菜单位置不正确解决:

mainWidget.h

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include <QtGui/QWidget>

class QMenu;
class QListWidget;

class mainWidget : public QWidget
{
    Q_OBJECT
    
public:
    mainWidget(QWidget *parent = 0);
    ~mainWidget();

private:
    QMenu *menuTest;
    QListWidget *listTest;//添加一个窗体,弹出菜单

private slots:
    void showMenu(QPoint);
};

#endif // MAINWIDGET_H

mainWidget.cpp

#include "mainwidget.h"
#include <QMenu>
#include <QAction>
#include <QListWidget>
#include <QVBoxLayout>

mainWidget::mainWidget(QWidget *parent)
    : QWidget(parent)
{
    menuTest=new QMenu(this);
    QAction *actionTest=new QAction("test",this);
    menuTest->addAction(actionTest);

    listTest=new QListWidget;
    //必须设置此属性,否则不会发出信号customContextMenuRequested
    listTest->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(listTest,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(showMenu(QPoint)));

    QVBoxLayout *mainLayout=new QVBoxLayout;
    mainLayout->addWidget(listTest);
    setLayout(mainLayout);
}

mainWidget::~mainWidget()
{
    
}

//private slot
void mainWidget::showMenu(QPoint p)
{
    /*
      p是相对于窗体坐标系的,即p的坐标相对与窗体的左上角坐标(0,0)
      如果窗体左上角为(100,100),p为(10,20)
      则mapToGlobal(p)为(110,120)
      经过坐标转化,才能在正确的位置显示菜单
    */
    menuTest->exec(mapToGlobal(p));
}

 

但是,问题出现了,总是不能在正确的位置弹出菜单。

看帮助文档发现,customContextMenuRequested发出的QPoint p是相对于发出这个信号的窗体的左上角(0,0)的。所以,必须先转化为相对于父窗口的坐标,然后再转化为相对于全局的坐标。

menuTest->exec(mapToGlobal(p));修改为menuTest->exec(mapToGlobal(mapToParent(p)));

结果还是不能正确显示,这就是我对类的概念模糊了。

menuTest->exec(mapToGlobal(mapToParent(p)));等价于menuTest->exec(this->mapToGlobal(this->mapToParent(p)));

而本意是把子窗体坐标转化为父窗体坐标,所以,应该改为:menuTest->exec(this->mapToGlobal(listTest->mapToParent(p)));

这样就显示正常了。

经过这么一想,其实改成menuTest->exec(listTest->mapToGlobal(p));也是正确的。

当然,最简便的还是menuTest->exec(QCursor::pos());

 

感想:Qt接触将近一个月了。感觉对类这个概念理解的清楚了一点。但还是不好。以至于连this指针都忘了。

你可能感兴趣的:(object,文档,qt,menu,Signal)