Qt World Summit: Register Today!

Transparency inside of window manager frame(NOT LAYERED)

  • I've spent the last 8hrs trying to find a why to do this but basically with windows forms if you set a transparency key on the main window, everything with that color would then visually transparent.

    I want to do the same thing in Qt but the problem is i have no idea how to set WM frame property. Setting it as layered is pointless because then i'd have to build my own interface, which i do not want to do as my app needs to stay native to the OS. For the sake of simplicity i'm not targeting multiple platforms, just win32 so any window manager dependent hacks are fine.


    As you can see i've tried every flag related to background there is yet it STILL stays a solid black, however if i extend the dwm glass to cover the entire app all the transparencys work fine... its just blured. Why/what i'm doing is building a screenshot utility where you can simply resize the window over what you wish capture and then hit print screen and have it do some automation with the image.

    This is the .NET version i've made if you want to look, its under the "frame" tab: http://dev.ameoto.com/packages/screeny/screenyInstaller.msi


  • Did you try w.setWindowOpacity(0.5) ?

  • [quote author="dfaure" date="1290621095"]Did you try w.setWindowOpacity(0.5) ?[/quote]

    So far from what i wanted but seeing as i fond a way around this i'll post for future reference... Whats required is a window manager hack, in this case its tricking GDI to leave the frame alone while still using layer effects.

    First of all we setup the WM as a layered window. Just do this after assigning a pointer to your app class, here you can see mine is "w" and also using the winId() method to get a pointer to the WN(for windows its a HWND).
    @SetWindowLong(w.winId(), GWL_EXSTYLE, WS_EX_LAYERED);
    const COLORREF color = RGB(0, 255, 0);@
    Now if you run your application now one of two things will happen... if your using aero you'll see nothing. But if your using a standard theme you'll get a vanilla styled window with a functional frame.

    So. How to get this to work in aero? Well first we need to do some work with the DWM extension, so rather then writing this out with more hacks just use this: http://labs.qt.nokia.com/2009/09/15/using-blur-behind-on-windows/

    Ok now once you've added that setup the following:
    @if (QtWin::isCompositionEnabled()) {
    "QWidget { background-color: rgba(0, 255, 0, 0); }"
    QtWin::extendFrameIntoClientArea(&w, 1, 1, 32, 1);
    w.setContentsMargins(0, 0, 0, 0);

    // Call UpdateLayeredWindow super secret source hacks
    HDC hdcScreen = GetDC(w.winId());
    HDC hDC = CreateCompatibleDC(hdcScreen);
    BLENDFUNCTION blend = {0};
    blend.BlendOp = AC_SRC_OVER;
    blend.SourceConstantAlpha = 200;
    blend.AlphaFormat = AC_SRC_ALPHA;
    POINT ptPos = {256, 256};
    SIZE sizeWnd = {640, 360};
    POINT ptSrc = {0, 0};
    UpdateLayeredWindow(w.winId(), hdcScreen, &ptPos, &sizeWnd, hDC, &ptSrc, color, &blend, ULW_ALPHA|ULW_COLORKEY);
    ReleaseDC(NULL, hdcScreen);
    "QWidget#captureRect { background-color:rgba(0,255,0,255); }"
    SetLayeredWindowAttributes(w.winId(), color, 0, ULW_COLORKEY);@

    What just happened? IDK D: but now when you display your app the widget area is completely transparent while retaining the full WM frame and functionality, this also brings the glass effect down 32 pixels allowing you to do some pretty fun stuff on glass.

    Theres also a fall-back for standard window styles by setting the centralWidget's background to the WM color key(in the other mode we set it but give it 0% opacity). Additionally ptPos and sizeWnd set the position and size respectively, of the window initially as Qt's calls are given much later on in the message loop.

    Your also able to draw images with a full 32bit alpha channel in aero mode but due to the limitations of this code anything drawn over the color key block will have a opaque bg, one why around this is to use masks to clear the area prior to drawing, but i could never get that to work so meh.


    Hope someone finds this usefull!

  • i am also getting trouble with, so may I request for a simple demo?

  • The following code comes from "Qt centre question":http://www.qtcentre.org/threads/11161-Tricky-problem-with-ARGB-widget-UpdateLayeredWindow

    class MyWidget : public QWidget
    void paintEvent(QPaintEvent *event);
    void mouseMoveEvent(QMouseEvent *event);

    QPixmap widgetMask;
    int myWidgetX; // <==
    int myWidgetY; // <==

    void MyWidget::paintEvent(QPaintEvent *event)
    // Code for painting on a QPixmap rather than on screen
    QPainter painter(&widgetMask);

    // If a move is needed with the resizing of the widget, no move there
    // but update of the private integers, it will be synced in the native call
    myWidgetX = newXPos; // <==
    myWidgetY = newXPos; // <==
    // Native call to handle semi-transparency on win32 (with the wonderful cheat for move&resize in one go)


    void mouseMoveEvent(QMouseEvent *event)
    // Let's suppose the widget should be moved but a repaint is not needed
    // calling Qt move() but DON'T FORGET to update myWidgetX & myWidgetY to avoid OUT-OF-SYNC
    // move(newXPos, newXPos); <= bad if you forget to update myWidgetX&Y

    myWidgetX = newXPos;  // <==
    myWidgetY = newXPos;  // <==
    move(myWidgetX, myWidgetY); // real Qt move(), I don't need to do repaint there


    void MyWidget::updateAlpha()
    HBITMAP oldBitmap;
    HBITMAP hBitmap;
    SIZE size;
    size.cx = widgetMask.width();
    size.cy = widgetMask.height();
    HDC screenDc = GetDC(NULL);
    POINT pointSource;
    pointSource.x = 0;
    pointSource.y = 0;
    POINT topPos;
    topPos.x = myWidgetX; // <==
    topPos.y = myWidgetY; // <==
    HDC memDc = CreateCompatibleDC(screenDc);
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.SourceConstantAlpha = alpha;
    blend.AlphaFormat = AC_SRC_ALPHA;
    hBitmap = widgetMask.toWinHBITMAP(QPixmap::PremultipliedAlpha);
    oldBitmap = (HBITMAP)SelectObject(memDc, hBitmap);
    UpdateLayeredWindow(winId(), screenDc, &topPos, &size, memDc, &pointSource, 0, &blend, ULW_ALPHA);
    ReleaseDC( NULL, screenDc);

    if (hBitmap!=NULL)
        SelectObject(memDc, oldBitmap);
    move(myWidgetX, myWidgetY); // <==


Log in to reply