Overlaying QWidgets



  • I probably already know the answer to this but, is there any way of overlaying one QWidget on top of another QWidget without the bottom widget being the top widgets parent?


  • Moderators

    They could be siblings or further relatives but they do need to have a common ancestor to be placed in the same window.
    You have control over the stacking order of such widgets with raise(), lower() and stackUnder().

    The other option is that they don't have a common ancestor i.e. they become separate windows, but then it becomes a little trickier to handle the various moves, resizes and stacking order.



  • @Chris-Kawa thanks for your reply. There is a common parent for both QWidgets and, ideally, I'd be using a QGridLayout so that both of the QWidgets fill the entire layout (one underneath each other). I'm pretty sure this won't be possible though.


  • Moderators

    No, two widgets in the same layout can't overlay each other. It's the actual job of the layout that they don't. One of them (usually the top one) needs to be "free floating" i.e. not in a layout. Resizing needs to be done manually.



  • Would a QStackedLayout help me in this instance? Literally just discovered such a wonder.

    The QWidget to be hidden is a QQuickWidget and I need to ensure that, even when not visible, the QML is still rendered correctly.


  • Moderators

    That depends on what you want. In a stacked layout only one widget is visible at a time. The others are not drawn. Stack layout is useful to implement something like QTabWidget.

    What are you trying to do exactly?



  • I'll give you a cut-down version of what I'm wanting to achieve...

    I've posted on this forum previously about my target application and I'm still trying to figure out a nice solution. So basically, I have a 3rd party COM object which draws a "nautical chart" using a Windows GDI DC. I'm also trying to integrate the QML Map into this application for added flexibility for the end user.

    The only way I've managed to figure out how to get both the QML and COM/DC to play nicely so far is to put a QWidget on top of a QQuickWidget and to allow the child to fill the QQuickWidget entirely. The problem with doing this is that firstly, I need to use the winId of the top-most QWidget in order to draw the COM object but with doing this, I then get thousands of QML warnings which I am unable to stop - however, I can grab the rendered QML and draw it as a QImage onto my QWidget as a layer beneath the COM object (I really hope this makes sense so far).

    Ultimately, this isn't really a nice solution and the QML warnings make further debugging of the application impossible. I have tried other solutions - custom QQuickItem which would draw the 3rd party COM object but this doesn't work. As soon as I go anywhere near a winId, this breaks the QML stuff.

    My thinking thus far is that if I am able to stack the QQuickWidget and the QWidget but without setting the any parents then I won't get any of the annoying QML warnings and I'll also be able to quickly swap between the two views.

    If there is the possibility of off-screen rendering both the QML and the COM then bringing them both together in another single QWidget, that'd be perfect but my tinkering so far hasn't come up with a workable solution for this.

    Sorry for the waffle...


  • Moderators

    Maybe instead of trying to marry QML with GDI you could draw with GDI to a bitmap and then paint that in a regular widget's paint event. For example something like this:

    class GDIWrapper : public QWidget
    {
    public:
        void paintEvent(QPaintEvent*) override
        {
            HDC hDC = CreateCompatibleDC(NULL);
            DWORD* pSrcData = nullptr;
            BITMAPINFO bmi = { sizeof( BITMAPINFOHEADER ), width(), height(), 1, 32, BI_RGB, 0, 0, 0, 0, 0};
            HBITMAP bitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void**)&pSrcData, 0, 0);
            HGDIOBJ prev_bitmap = SelectObject(hDC, bitmap);
    
            DrawSomething(hDC); //this would be the 3rd party draw
    
            SelectObject(hDC, prev_bitmap);
            GdiFlush();
    
            { //scoped so that QImage does not outlive bitmap
                QImage img ((uchar*)pSrcData, width(), height(), QImage::Format_RGB32);
                QPainter p(this);
                p.drawImage(0,0,img);
            }
    
            DeleteObject(bitmap);
            DeleteDC(hDC);
        }
    };
    

    Of course you'd need to add the necessary error checking.
    You could then easily overlay that over QQuickWidget without the need for native handle (winId):

    auto gdi_wrapper = new GDIWrapper();
    auto quick_widget = new QQuickWidget(QUrl::fromLocalFile(":/some.qml"));
    quick_widget->setLayout(new QVBoxLayout);
    quick_widget->layout()->addWidget(gdi_wrapper);
    


  • @Chris-Kawa Thanks for your suggestion. Looks like a very good one to me - will try this later.

    The ultimate solution, I guess, would be using your code in a custom 'QQuickItem' so that I could add it to the QML - then I could make use of additional QML features.

    Or maybe I'm trying to push the QML integration too far. I dunno.



  • @Chris-Kawa I've just implemented your suggestion and the resulting QImage appears to be rotated and flipped. Bizarre!


  • Moderators

    Coordinate systems and related data storage often differ between frameworks. If you can't control how the 3rd party organizes its output just set the right flip/rotation using setTransform() on your painter before drawing.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.