QGraphicsItem::pos возвращает 0, когда в макете

#c #qt #qt5 #qgraphicsitem #qlayout

#c #qt #qt5 #qgraphicsitem #qlayout

Вопрос:

У меня есть следующий фрагмент кода Qt в моем widget.cpp:

 Widget::Widget(QWidget *parent)
: QWidget(parent)
{
    m_Scene = new QGraphicsScene(this);
    m_Scene->setSceneRect(0, 0, 1024, 768);

    GraphicsTextItem* m_1 = new GraphicsTextItem(nullptr, QString("l_1"));

    GraphicsTextItem* m_2 = new GraphicsTextItem(nullptr, QString("l_2"));


    QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
    layout->addItem(m_1);
    layout->addItem(m_2);

    QGraphicsWidget* list = new QGraphicsWidget;
    list->setLayout(layout);
    m_Scene->addItem(list);

    qDebug() << m_2->x() << " " << m_2->y(); // Prints 0,0 Why?

    QGraphicsView* view = new QGraphicsView(this);
    view->setScene(m_Scene);
}
  

GraphicsTextItem является производным классом QGraphicsWidget :

 class GraphicsTextItem : public QGraphicsWidget
{
private:
    QString m_Name;
public:
    GraphicsTextItem(QGraphicsItem * parent = nullptr, const QStringamp; name = QString());
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
    {
        QFont font("Times", 12);
        painter->setFont(font);
        painter->drawText(0, 0, m_Name);
    }
};
  

Я также привожу свой краткий основной файл, расположенный в main.cpp:

 int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}
  

Мой вопрос в том, почему строка qDebug выдает мне 0,0, поскольку, безусловно, позиция виджета отлична от нуля. Если я не помещаю виджет в макет и вызываю setPos(), qDebug выводит правильное значение, установленное ранее.

Комментарии:

1. что такое GraphicsTextItem ?

2. Это QGraphicsWidget

Ответ №1:

Начальное положение всех QGraphicsItem равно (0, 0) и включает в себя m_2, а m_2 изменит свое положение, когда будет применен QGraphicsLinearLayout, что произойдет через мгновение после завершения синхронной задачи и запуска eventloop, это можно наблюдать с помощью QTimer::singleShot(0, ...) :

 Widget::Widget(QWidget *parent)
    : QWidget(parent)
{

    m_Scene = new QGraphicsScene(this);
    QGraphicsView* view = new QGraphicsView(this);
    view->setScene(m_Scene);

    m_Scene->setSceneRect(0, 0, 1024, 768);

    GraphicsTextItem *m_1 = new GraphicsTextItem(nullptr, QString("l_1"));

    GraphicsTextItem *m_2 = new GraphicsTextItem(nullptr, QString("l_2"));


    QGraphicsLinearLayout* layout = new QGraphicsLinearLayout;
    layout->addItem(m_1);
    layout->addItem(m_2);

    QGraphicsWidget* list = new QGraphicsWidget;
    list->setLayout(layout);
    m_Scene->addItem(list);

    qDebug() << "synchronous" << m_2->pos() << m_2->mapToScene(QPointF{});

    QTimer::singleShot(0, m_2, [m_2](){
        qDebug() << "asynchronous"<< m_2->pos() << m_2->mapToScene(QPointF{});
    });
}
  

Вывод:

 synchronous QPointF(0,0) QPointF(0,0)
asynchronous QPointF(62,6) QPointF(62,6)
  

Комментарии:

1. Спасибо! Связано ли это с событием show, которое запускается сразу после? В таком случае существует ли метод для повторного определения позиции по ссылке? Я спрашиваю это, потому что у меня будет другой элемент, который определяет траекторию между двумя GraphicsTextItems?

2. @Minee Это не имеет ничего общего с методом show, поскольку сцена в нем не нуждается, но задача вычисления размера макета выполняется асинхронно через событие. Позиция не может быть получена по ссылке (в Qt ссылки не используются), вместо этого вы должны получать позицию с помощью средства получения всякий раз, когда вам нужно, например, вы можете использовать QTimer для получения позиции за определенный период времени или просто для получения позиции, когда вам это нужно, поэтому в заключение вы должны быть GraphicsTextItem * m_1 членом класса.

3. @eyllanesc Спасибо за ответ, это действительно вызвало у меня головную боль. Однако один вопрос: всегда ли гарантируется, что, QTimer.singleShot(0, handler) будет вызван после отображения добавленных элементов? Может ли Qt свободно изменять порядок / оптимизировать приоритеты цикла событий, что может привести к singleShot выполнению до обновления сцены? Было бы лучше каким-то образом полагаться на itemChange() сигналы (или аналогичные) вместо того, чтобы применять логику к элементам после того, как они изменили положение (изначально) ?

4. @timmwagener 1) документы : В качестве особого случая время ожидания QTimer с таймаутом 0 истечет, как только будут обработаны все события в очереди событий оконной системы. И одним из таких событий является то, которое отображается на экране, так что да. 2) — другой вариант.