Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to embed a QML view into a native window

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';
            }
        }
    }
}
like image 857
bitwise Avatar asked Aug 05 '16 15:08

bitwise


1 Answers

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.

like image 200
James Turner Avatar answered Sep 26 '22 06:09

James Turner