У меня есть очень странная проблема, связанная с 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
, оно не будет выполняться в правильном положении экрана, если я перетащу MainWindow
всего QApplication
в любое место, кроме его начальной позиции на экране.В дополнение к QGraphicsScene
, сама эта сцена всегда хранится в QSplitter
.Теперь я бы понял, если это всегда было какой-то проблемой, но, оказывается, каждый раз, когда я вызываю слот для этого сигнала, ТОЛЬКО в первый раз он будет исполнен в неправильной позиции в QGraphicsScene
.Независимо от того, как я манипулирую размером самого виджета Titlebar
, перемещаю команды или максимизирую команды в MainWindow
или даже редактирую размер разделителя для QGraphicsView
, который влияет на размер QGraphicsScene
, он всегда будетбыть в правильном положении впоследствии.вот функция для выполнения:
void TitleBar::showContextMenu(QPoint point){
qDebug() << point;
contextMenu->exec(point);
emit _parent->focusChangedIn();
}
Я напечатал точку, в которой он вызывает exec.Самым странным является то, что оба раза, когда я щелкаю правой кнопкой мыши в одном и том же месте, он будет печатать ЖЕ значение для позиционного параметра слота, как первого, так и второго, но будет находиться в правильном месте каждый раз, кроме первого.Я забыл установить какой-то другой флаг, когда добавил контекстное меню в класс Titlebar
?Это как-то связано с установкой родительского элемента QMenu's
на Titlebar
?Я просто ошеломлен, как одни и те же QPoint
могут exec
в двух разных местах экрана при одинаковом значении.Кто-нибудь знает, что может или не может произойти при первом вызове в слот Titlebar's
для execing
QMenu
?
РЕДАКТИРОВАТЬ: проблема возникла из-за выполнения этой строки кода вTitlebar
конструктор:
contextMenu = new DockItemContextMenu(this);
Изменение в:
contextMenu = new DockItemContextMenu;
исправило проблему.Кто-нибудь знает почему, или это возможно ошибка?Я скорее не принимаю это как ответ, потому что это не объясняет, почему это произошло вообще.
Вот минимальный пример с тем же эффектом.
MainWindow.h:
#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
MainWindow.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.h:
#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
Titlebar.cpp:
#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();
}
main.cpp:
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Так что запускается и воспроизводит ошибку соответственно.Если вы измените строку в Titlebar.cpp с
menu = new QMenu(this);
на:
menu = new QMenu;
, тогда она будет работать правильно.ТОЛЬКО первый щелчок правой кнопкой мыши, чтобы открыть контекстное меню, появится в неправильном месте на экране.Все последующие щелчки правой кнопкой мыши теперь будут следовать за виджетом / окном / сплиттером в любой комбинации.Я не понимаю, может кто-нибудь сказать мне, если это на самом деле ошибка или нет.