QMenu第一次未在正确位置执行

时间:2018-10-15 20:17:31

标签: c++ qt qmenu

关于QMenu及其执行时的位置,我遇到了一个非常奇怪的问题。

这是我的子类QMenu的代码:

DockItemContextMenu::DockItemContextMenu(QWidget *parent) : QMenu(parent){
    style = qApp->style();
    QPointer<QAction> restoreAction = new QAction(QIcon(style->standardIcon(QStyle::SP_TitleBarMaxButton)), "Restore", this);
    QPointer<QAction> minimizeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarMinButton), "Minimize", this);
    QPointer<QAction> maximizeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarMaxButton), "Maximize", this);
    QPointer<QAction> stayOnTopAction = new QAction("Stay On Top", this);
    stayOnTopAction->setCheckable(true);
    QPointer<QAction> closeAction = new QAction(style->standardIcon(QStyle::SP_TitleBarCloseButton), "Close", this);

    this->addActions({restoreAction, minimizeAction, maximizeAction, stayOnTopAction, closeAction});

    connect(restoreAction, &QAction::triggered, parent, [this](){ emit restoreTriggered();}, Qt::QueuedConnection);
    connect(minimizeAction, &QAction::triggered, parent, [this](){ emit minimizeTriggered();}, Qt::QueuedConnection);
    connect(maximizeAction, &QAction::triggered, parent, [this](){ emit maximizeTriggered();}, Qt::QueuedConnection);
    connect(stayOnTopAction, &QAction::triggered, parent, [this](){ emit stayOnTopTriggered();}, Qt::QueuedConnection);
    connect(closeAction, &QAction::triggered, parent, [this](){ emit closeTriggered();}, Qt::QueuedConnection);
}

好的,所以本质上我有另一个小部件,它将此DockItemContextMenu的实例作为字段保存。在这个名为Titlebar的拥有类中,我做到了这样,右键单击将发出customContextMenuRequested(QPoint)信号。

TitleBar::TitleBar(QString title, QWidget *parent){
        ...
        this->setContextMenuPolicy(Qt::CustomContextMenu);
        contextMenu = new DockItemContextMenu(this);
        connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)), Qt::QueuedConnection);
        ...
}

此后,此窗口小部件实际上已插入QGraphicsScene中,并隐式转换为QGraphicsItem。当我在Titlebar上执行FIRST右键单击事件时,如果将整个MainWindow的{​​{1}}拖动到屏幕上的起始位置以外的任何位置,它将不会在正确的屏幕位置执行。除了位于QApplication中之外,此场景本身始终存储在QGraphicsScene中。现在,我将了解这是否总是存在某种问题,但事实证明,每次我为该信号调用插槽时,只有第一次它会在QSplitter中的错误位置执行。无论我如何操作QGraphicsScene小部件本身的大小,将命令移动或最大化命令到Titlebar,甚至编辑MainWindow的拆分器大小,都会影响{ {1}},之后它将始终位于正确的位置。这是执行功能:

QGraphicsView

我打印出它正在调用exec的地方。最奇怪的部分是,两次我都在同一位置上单击鼠标右键,都会同时为第一个执行程序和第二个执行程序打印插槽位置参数的SAME值,但每次都在正确的位置,而不是第一个。将上下文菜单添加到QGraphicsScene类时,是否忘记设置其他标志了吗?将void TitleBar::showContextMenu(QPoint point){ qDebug() << point; contextMenu->exec(point); emit _parent->focusChangedIn(); } 的父级设置为Titlebar有什么关系吗?我只是傻眼了,在给定相同值的情况下,在两个不同的屏幕位置使用相同的QMenu'sTitlebar。是否有人知道QPoint exec的{​​{1}}插槽的第一次呼叫中可能发生或未发生的事情?

编辑:问题源于在Titlebar's构造函数中执行以下代码行:

execing

更改为:

QMenu

解决了该问题。有谁知道为什么,或者这可能是一个错误?我宁愿不接受这作为答案,因为它并没有解释为什么它首先发生。

这是一个效果相同的最小示例。

MainWindow.h:

Titlebar

MainWindow.cpp:

contextMenu = new DockItemContextMenu(this);

Titlebar.h:

contextMenu = new DockItemContextMenu;

Titlebar.cpp:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QWidget>
#include <QGraphicsView>
#include <QSplitter>
#include <QHBoxLayout>
#include <QGraphicsScene>
#include <QPointer>
#include <QTreeWidget>
#include "titlebar.h"

class MainWindow : public QMainWindow{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:

};

#endif // MAINWINDOW_H

main.cpp:

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){
    QPointer<QWidget> widgetArea = new QWidget;
    QPointer<QHBoxLayout> hLayout = new QHBoxLayout;

    widgetArea->setLayout(hLayout);

    QPointer<QSplitter> splitter = new QSplitter;
    hLayout->addWidget(splitter);

    QPointer<QTreeView> tree = new QTreeView;
    splitter->addWidget(tree);

    QPointer<QGraphicsView> view = new QGraphicsView;
    splitter->addWidget(view);
    splitter->setStretchFactor(0, 1);
    splitter->setStretchFactor(1, 4);

    QPointer<QGraphicsScene> scene = new QGraphicsScene;
    view->setScene(scene);

    QPointer<Titlebar> blue = new Titlebar;
    blue->setObjectName("blue");
    blue->setStyleSheet(QString("#blue{background-color: rgb(0,0,255)}"));
    blue->resize(250,250);
    scene->addWidget(blue);

    this->setCentralWidget(widgetArea);
    this->resize(1000,750);
}

MainWindow::~MainWindow(){

}

因此,它将运行并相应地重现错误。如果您从

更改Titlebar.cpp中的行
#ifndef TITLEBAR_H
#define TITLEBAR_H

#include <QMenu>
#include <QWidget>
#include <QPointer>
#include <QDebug>
#include <QMouseEvent>

class Titlebar : public QWidget{
    Q_OBJECT
public:
    explicit Titlebar(QWidget *parent = nullptr);
    QPointer<QMenu> menu;
    QPoint currentPos;
protected slots:
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void showContextMenu(QPoint point);
};

#endif // TITLEBAR_H

收件人:

#include "titlebar.h"

Titlebar::Titlebar(QWidget *parent) : QWidget(parent){
    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)), Qt::QueuedConnection);
    menu = new QMenu(this);
    menu->addAction("Test");
}

void Titlebar::showContextMenu(QPoint point){
    qDebug() << point;
    menu->exec(mapToGlobal(point));
}

void Titlebar::mouseMoveEvent(QMouseEvent *event){
    if (event->buttons() && Qt::LeftButton){
        QPoint diff = event->pos() - currentPos;
        move(pos() + diff);
    }
}

void Titlebar::mousePressEvent(QMouseEvent * event){
    currentPos = event->pos();
}

然后它可以正常工作。只有第一次右键单击打开上下文菜单时,才会在屏幕上错误的位置生成。现在,所有后续的右键单击都将跟随小部件/窗口/拆分器的任意组合。我不明白,有人可以告诉我这是否是一个错误。

1 个答案:

答案 0 :(得分:0)

您需要添加一行代码,因为您使用的是QGraphicsScene的一部分QGraphicsProxyWidget。该场景由继承QAbstractScrollArea的QGraphicsView表示。这导致上下文菜单通过视口而不是窗口小部件本身显示。因此,添加这一行代码将覆盖标题栏到not be embedded in the scene when it's parent was already embedded in the scene。有效地使其再次引用窗口小部件而不是视口。

在MainWindow.cpp中,在第26行之后添加

blue->setWindowFlags(Qt::BypassGraphicsProxyWidget);