Qt Quick parent,和parent的id不一样

Qt Quick parent, not the same as the id of the parent

我正在尝试 Qt Quick,我想为我的应用程序创建一个标题栏。所以我继承了QQuickPaintedItem,在上面画了一点,想用它作为我的Window的标题栏。我正在使用 Qt 5.7。这成功了,但只在一定程度上成功了……我会在代码后解释更多;我是这样做的:

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "mycustomtitlebar.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<MyCustomTitleBar>("my.custom.lib", 1, 0, "MyCustomTitleBar");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import my.custom.lib 1.0

Window {
    id: wnd
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MyCustomTitleBar {
        id: titleBar
        anchors.top: parent.top
        anchors.left: parent.left
        anchors.right: parent.right
        height: 100
        hostWidget: wnd
    }

    Rectangle {
        color: "beige"
        anchors.top: titleBar.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
    }
}

mycustomtitlebar.h

#ifndef MYCUSTOMTITLEBAR_H
#define MYCUSTOMTITLEBAR_H

#include <QQuickPaintedItem>
#include <QPoint>

class MyCustomTitleBar : public QQuickPaintedItem
{
    Q_OBJECT

    Q_PROPERTY(QWindow *hostWidget READ hostWidget WRITE setHostWidget)

public:
    MyCustomTitleBar(QQuickItem *parent = 0);

protected:
    virtual void paint(QPainter *pPainter) Q_DECL_OVERRIDE;
    virtual void mousePressEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;
    virtual void mouseMoveEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;
    virtual void mouseReleaseEvent(QMouseEvent *pEvent) Q_DECL_OVERRIDE;

private:
    QWindow *hostWidget() const;
    void setHostWidget(QWindow *pHostWidget);

private:
    QWindow *m_pHostWidget;
    QPoint m_initialMousePosition;
    bool m_leftMouseButtonPressed;
};

#endif // MYCUSTOMTITLEBAR_H

mycustomtitlebar.cpp

#include "mycustomtitlebar.h"

#include <QPainter>
#include <QDragMoveEvent>
#include <QWindow>

MyCustomTitleBar::MyCustomTitleBar(QQuickItem *parent)
    : QQuickPaintedItem(parent),
      m_leftMouseButtonPressed(false),
      m_pHostWidget(Q_NULLPTR)
{
    setAcceptedMouseButtons(Qt::AllButtons);
}

void MyCustomTitleBar::paint(QPainter *pPainter)
{
    // Dummy drawing...
    const QRect myRect(10, 10, width() - 20, height() - 20);
    qDebug() << myRect;
    pPainter->drawRect(myRect);
}

void MyCustomTitleBar::mousePressEvent(QMouseEvent *pEvent)
{
    m_leftMouseButtonPressed = true;
    m_initialMousePosition = pEvent->pos();
}

void MyCustomTitleBar::mouseMoveEvent(QMouseEvent *pEvent)
{
    if (m_leftMouseButtonPressed) {
        if (!m_pHostWidget) {
            qDebug() << Q_FUNC_INFO << "Host widget not set. Please set host widget";
            return;
        }

        const QPoint newMousePosition = pEvent->pos() - m_initialMousePosition + m_pHostWidget->position();
        m_pHostWidget->setPosition(newMousePosition);
    }

    QQuickPaintedItem::mouseMoveEvent(pEvent);
}

void MyCustomTitleBar::mouseReleaseEvent(QMouseEvent *pEvent)
{
    m_leftMouseButtonPressed = false;
}

QWindow *MyCustomTitleBar::hostWidget() const
{
    return m_pHostWidget;
}

void MyCustomTitleBar::setHostWidget(QWindow *pHostWidget)
{
    m_pHostWidget = pHostWidget;
}

现在这段代码完美运行,我 运行 应用程序,我可以单击标题栏并拖动,整个 window 移动到我想要的位置。

但问题是:如果我将 hostWidget: wnd 更改为 hostWidget: parent,它将不再起作用。 谁能解释一下为什么?因为wnd毕竟是parent

P.S.

我也从 Qt Creator 收到了这个非常奇怪的错误通知,但是代码编译并且 运行 没问题:

为什么?...

But here is the problem: if I change hostWidget: wnd to hostWidget: parent it doesn't work anymore. Can anyone explain why? Because wnd is the parent after all.

  1. Window QML type instantiates QQuickWindow.

  2. QQuickWindow does not inherit from QQuickItem.

  3. 相反,它包含一个 QQuickItem element accessible through its contentItem() 函数。

  4. parent 属性 引用了一个 QQuickItem 对象。

因此在您的示例中 titleBar.parent 指的是 wnd 的元素而不是 wnd 本身。

如果在调用 MyCustomTitleBar::setHostWidget() 之前尝试在内部将 titleBar.parent 动态转换为 QWindow*,则由于上面的 (2) 而失败(在这种情况下,您应该会看到相应的控制台错误)。

因为 titleBar.parent 不是 wnd,而是 wnd.contentItem。这就是为什么你发现它的 parent 不是 wnd.

为什么 wnd.contentItem 而不是 wnd

一般来说,任何项目都会成为它包含的所有子项目的 parent,但 Window 并非如此。

这里的问题是 parent 属性 的类型是 Item。遗憾的是,Window 没有继承自 Item。因此,一个 Window 应该包含一个真正的 Item 成为其所有 children 的 parent。这就是 Window.contentItem 的用途。