关于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's
会Titlebar
。是否有人知道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();
}
然后它可以正常工作。只有第一次右键单击打开上下文菜单时,才会在屏幕上错误的位置生成。现在,所有后续的右键单击都将跟随小部件/窗口/拆分器的任意组合。我不明白,有人可以告诉我这是否是一个错误。
答案 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);