QMenu не выполняется в правильном положении с первого раза - PullRequest
0 голосов
/ 15 октября 2018

У меня есть очень странная проблема, связанная с 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;

, тогда она будет работать правильно.ТОЛЬКО первый щелчок правой кнопкой мыши, чтобы открыть контекстное меню, появится в неправильном месте на экране.Все последующие щелчки правой кнопкой мыши теперь будут следовать за виджетом / окном / сплиттером в любой комбинации.Я не понимаю, может кто-нибудь сказать мне, если это на самом деле ошибка или нет.

1 Ответ

0 голосов
/ 17 октября 2018

Вам нужно добавить одну строку кода, потому что вы используете QGraphicsProxyWidget, который является частью QGraphicsScene.Сцена представлена ​​QGraphicsView, который наследует QAbstractScrollArea.Это заставляет контекстное меню показываться через область просмотра, а не сам виджет.Поэтому добавление этой одной строки кода переопределит строку заголовка, чтобы не было внедрено в сцену, когда его родительский объект уже был внедрен в сцену .Эффективно заставляя его снова ссылаться на виджет, а не на область просмотра.

В MainWindow.cpp сразу после строки 26 добавьте

blue->setWindowFlags(Qt::BypassGraphicsProxyWidget);
...