How to embed a QML view into a native window
Asked Answered
F

1

10

I'm having a problem trying to insert a QML view into a native OSX window. I know it's possible, but I don't know what I'm doing wrong.

Basically, my goal is, given a native NSView* to then embed a QML based widget. The problem is that I get it to point where it does indeed renderer the qml inside the view but it creates an extra transparent window to the side and it doesn't seem to redraw the QML view properly.

Here's the code that I'm using (please disregard all the memory leaks):

@interface AppDelegate ()
-(void)processEvents;

@property(nonatomic) NSTimer* timer;
@property(nonatomic) QApplication* qt;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    NSWindow* window = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
    NSView *view = [window contentView];
    assert(view);

    char* test[0];
    int count = 0;

    QApplication::instance()->setAttribute(Qt::AA_MacPluginApplication);
    _qt = new QApplication(count, test);

    QMacNativeWidget* native = new QMacNativeWidget(view);
    assert(native);

    QQuickWidget* qml = new QQuickWidget(native);
    qml->setSource(QUrl(QStringLiteral("main.qml")));

    QVBoxLayout* layout = new QVBoxLayout();
    layout->addWidget(qml);

    native->setLayout(layout);

    qml->show();
    native->show();


    NSView* qmlView = (NSView*)native->winId();
    [view addSubview:qmlView];

    _timer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(processEvents) userInfo:nil repeats:YES];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification
{
    // Insert code here to tear down your application
    [_timer invalidate];
    _qt->quit();

}

-(void)processEvents
{
    _qt->processEvents();
    _qt->sendPostedEvents(0,-1);
}

@end

And here's the simple qml:

import QtQuick 2.7

Item
{
    visible: true
    x: 0;
    y: 0;
    width: 100
    height: 100
    Rectangle
    {
        anchors.fill: parent
        color: 'blue'
        MouseArea
        {
            anchors.fill: parent
            onClicked:
            {
                console.log(parent.color);
                if(parent.color == '#0000ff')
                    parent.color = 'green';
                else
                    parent.color = 'blue';
            }
        }
    }
}
Foghorn answered 5/8, 2016 at 15:0 Comment(0)
F
1

QQuickWidget composities its content with other widget content in a slightly complex way, involving a framebuffer and offscreen window, which I suspect might explain the odd results you see - I would not expect it to work in a plugin situation.

The easiest option would be use a QQuickWindow (or QQuickView) with createWindowContainer to turn the QWindow into a QWidget you can parent to your QMacNativeWidget.

However, I think the most robust approach would be to ignore widgets and windows entirely, and integrate at the framebuffer level, using QQuickRenderControl and an NSOpenGLView. This is more work to code up but keeps the NSView hierarchy straightforward, and should give the best possible performance. You can either render directly into the native OpenGL view (which requires creating a QOpenGLContext from the native context, possible since Qt 5.4), or into a framebuffer using a texture shared between the QtQuick and NSOpenGLContext.

Fite answered 19/8, 2016 at 10:46 Comment(4)
Thank you so much for taking the time to answer. I will research these options and see if they work. In the meantime, would you be able to point me to an example implemented somewhere?Foghorn
For createWindowContainer simply look at the docs - it will take a QQuickWindow and wrap it in a widget. This will work better in a plugin scenario than QQuickWidget, due to different implementation choices.Fite
For QQuickRenderControl, see the QQuickRenderControl example that ships with Qt - you need to replace the WindowSingleThreaded in that example with a NSOpenGLView, and wrap the associated openGLContext in a QOpenGLContext to use it with the render control.Fite
Thanks James, I'll give those a try and post an example here when I get a chanceFoghorn

© 2022 - 2024 — McMap. All rights reserved.