Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. OS X: Clicks on fully transparent parts of windows no longer passes through in Qt 5.6

OS X: Clicks on fully transparent parts of windows no longer passes through in Qt 5.6

Scheduled Pinned Locked Moved Unsolved General and Desktop
qt5.6framelesstransparencyqwidget
12 Posts 3 Posters 3.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S SGaist
    21 Jan 2016, 23:45

    Hi,

    Shouldn't you also set the Qt::WA_TransparentForMouseEvents attribute for that ?

    G Offline
    G Offline
    Guy Gizmo
    wrote on 22 Jan 2016, 07:50 last edited by
    #3

    @SGaist That would make clicks pass through for the entire window. I just want the transparent parts of the window to have clicks pass through.

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 22 Jan 2016, 21:39 last edited by
      #4

      Then I'd ask on the interest mailing list about that behavior change. You'll find there Qt's developers/maintainers (this forum is more user oriented)

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      G 1 Reply Last reply 29 Jan 2016, 04:26
      0
      • S SGaist
        22 Jan 2016, 21:39

        Then I'd ask on the interest mailing list about that behavior change. You'll find there Qt's developers/maintainers (this forum is more user oriented)

        G Offline
        G Offline
        Guy Gizmo
        wrote on 29 Jan 2016, 04:26 last edited by
        #5

        @SGaist et al,
        I tried the mailing list but haven't gotten any response so far. Can anyone recommend anything else to try?

        I'm finding myself stuck with Qt related issues in my app. So far, 5.3.x, 5.5.x, and 5.6 all have different, mutually exclusive bugs that are serious enough that I need to fix them. I suppose I could try 5.4.x and hope it works out. But it'd be much better if I could stay up to date with the latest stable Qt.

        1 Reply Last reply
        0
        • G Offline
          G Offline
          Guy Gizmo
          wrote on 11 Jul 2016, 19:51 last edited by
          #6

          This is old but I'm bumping again, as I'm still stuck with this problem. I just tried Qt 5.7.0 and (not unexpectedly) it has the same behavior.

          I'd really like to make use of the features of Qt 5.6 and later, especially since 5.6 is a version with long term support. But as long as I can't find a workaround for this issue, I'm stuck with 5.5.1.

          1 Reply Last reply
          0
          • S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 11 Jul 2016, 20:40 last edited by
            #7

            If you can create a minimal compilable application that shows the behavior problem then you should take a look at the bug report system and see if something known. If not please consider opening a new report providing your sample application.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            G 1 Reply Last reply 18 Jul 2016, 23:28
            0
            • S SGaist
              11 Jul 2016, 20:40

              If you can create a minimal compilable application that shows the behavior problem then you should take a look at the bug report system and see if something known. If not please consider opening a new report providing your sample application.

              G Offline
              G Offline
              Guy Gizmo
              wrote on 18 Jul 2016, 23:28 last edited by
              #8

              @SGaist Here is the bug report.

              In case you or anyone else is interested, here is a minimal sample application that demonstrates the

              // mainwindow.h
              #include <QMainWindow>
              
              class MainWindow : public QMainWindow
              {
              	Q_OBJECT
              public:
              	MainWindow(QWidget *parent = 0);
              protected:
              	void paintEvent(QPaintEvent *);
              };
              
              // main.cpp
              #include <QApplication>
              #include <QPainter>
              #include "mainwindow.h"
              
              MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
              {
                  setAttribute(Qt::WA_TranslucentBackground);
              	setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
              }
              
              void MainWindow::paintEvent(QPaintEvent *)
              {
              	QPainter windowPainter(this);
              	windowPainter.setBrush(Qt::red);
              	windowPainter.drawRect(QRect(0,0,50,50));
              	windowPainter.setBrush(Qt::transparent);
              	windowPainter.drawRect(rect().adjusted(0,0,-1,-1));
              }
              
              int main(int argc, char *argv[])
              {
              	QApplication a(argc, argv);
              	MainWindow window;	
              	window.show();
              	return a.exec();
              }
              

              The app creates a transparent window that includes a frame outlining the window and a red box. When built with Qt 5.5.x, clicking on the frame or red box will activate the window, and clicking on the transparent area will click whatever element in under the window. When built with Qt 5.6 and later, clicking anywhere inside the frame of the window will activate the window and not pass through the click.

              1 Reply Last reply
              0
              • S Offline
                S Offline
                SGaist
                Lifetime Qt Champion
                wrote on 19 Jul 2016, 22:00 last edited by
                #9

                Thanks for sharing !

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply
                0
                • B Offline
                  B Offline
                  bashtian
                  wrote on 21 May 2023, 20:09 last edited by
                  #10

                  This is an old post but I am facint the same issue on PyQt6, TransluscentBackground set on MacOS, on Windows it works fine, I did not test on Linux yet, did you @Guy-Gizmo found a solution?

                  G 1 Reply Last reply 22 May 2023, 15:05
                  0
                  • B bashtian
                    21 May 2023, 20:09

                    This is an old post but I am facint the same issue on PyQt6, TransluscentBackground set on MacOS, on Windows it works fine, I did not test on Linux yet, did you @Guy-Gizmo found a solution?

                    G Offline
                    G Offline
                    Guy Gizmo
                    wrote on 22 May 2023, 15:05 last edited by
                    #11

                    @bashtian said in OS X: Clicks on fully transparent parts of windows no longer passes through in Qt 5.6:

                    This is an old post but I am facint the same issue on PyQt6, TransluscentBackground set on MacOS, on Windows it works fine, I did not test on Linux yet, did you @Guy-Gizmo found a solution?

                    Yes, actually. You can check out the Shaped Clock example for a demonstration of how to do this: https://doc.qt.io/qt-6/qtwidgets-widgets-shapedclock-example.html

                    The key is to use setMask() so that Qt knows which regions are supposed to be transparent to clicks.

                    That said, I did hit some issues with this. It's easy if the opaque region of your window is a simple shape like an ellipse or rectangle, since you can simply create a QRegion that covers that shape and set the mask to that, but that wasn't so in my case. There would be transparent pixels in any number of shapes that I wanted clicks to fall through. So I needed to render out the contents of my window as a QBitmap with any fully transparent pixels being white and all others being black.

                    BUT, there was another snag: on high-DPI displays it wouldn't quite work. At least on macOS the mask needed to have a pixel density of 1, even if the window itself has a pixel density of 2. So I wrote a method that generates a low density mask where it'll have a black pixel if any of the encompassing high-DPI pixels in the widget itself are the slightest bit non-transparent. I'll share that code here since others might find it useful:

                    void MyWindow::updateMask()
                    {
                        // In my case, the window contains a single top-level widget that does all of
                        // the rendering named contentsWidget.
                        // In order for this method to work, your widgets may need to be structured
                        // like that too:
                        QRect grabRect = rect().translated(-contentsWidget->geometry().topLeft());
                        QBitmap widgetMask = contentsWidget->grab(grabRect).mask();
                        
                        if (devicePixelRatio() == 2) {
                            // Unfortunately masks cannot be retina resolution, so if our widget is
                            // on a retina display, we have to generate a mask at non-retina
                            // resolution. But just rendering on a non-retina paint device and then
                            // generating a mask unfortunately doesn't work, as there'll inevitably
                            // be some pixels in the mask that should be black but are actually
                            // white. The right way to do it is to render at retina resolution, and
                            // then scale the bitmap mask down to the right size. But a normal
                            // scale won't do -- each pixel on our mask needs to be black if any of
                            // the four retina pixels that comprise it are black, i.e. a bitwise-OR
                            // of the four pixels. So we need to do the scaling ourselves:
                            QImage maskImage = widgetMask.toImage();    
                            QImage scaledMaskImage(maskImage.size()/2, maskImage.format());
                            
                            for(int y = 0; y < scaledMaskImage.height(); ++y) {
                                uint16_t *line1 = (uint16_t *)maskImage.scanLine(y*2);
                                uint16_t *line2 = (uint16_t *)maskImage.scanLine(y*2+1);
                                uint8_t *line = scaledMaskImage.scanLine(y);
                                
                                for(int x = 0; x < scaledMaskImage.bytesPerLine(); ++x) {
                                    uint16_t a = line1[x];
                                    uint16_t b = line2[x];
                                    
                                    // This expression is the simplified version of the full expression
                                    // that will combine the mask's bits together as described above.
                                    line[x] = ~uint8_t((a     & 0x0001) |
                                                       (a>>1  & 0x0003) |
                                                       (a>>2  & 0x0006) |
                                                       (a>>3  & 0x000C) |
                                                       (a>>4  & 0x0018) |
                                                       (a>>5  & 0x0030) |
                                                       (a>>6  & 0x0060) |
                                                       (a>>7  & 0x00C0) |
                                                       (a>>8  & 0x0080) |                
                                                       (b     & 0x0001) |
                                                       (b>>1  & 0x0003) |
                                                       (b>>2  & 0x0006) |
                                                       (b>>3  & 0x000C) |
                                                       (b>>4  & 0x0018) |
                                                       (b>>5  & 0x0030) |
                                                       (b>>6  & 0x0060) |
                                                       (b>>7  & 0x00C0) |
                                                       (b>>8  & 0x0080));                
                                    // Not sure why it's necessary to negate this value given that
                                    // we're using the same format of image as the original mask
                                }
                            }
                    
                            setMask(QBitmap::fromImage(scaledMaskImage));
                        } else {
                            setMask(widgetMask);
                        }
                    }
                    

                    Note that this might not be necessary on Windows. My app is currently macOS only so I'm not sure what's necessary to get proper transparent rendering in Windows in high DPI displays just yet.

                    You mentioned your project is Python and this is C++ code, but if you need to do the same thing in Python this can at least show what it is you need to do. The syntax for writing out a mask image will be different in Python but the math should be the same.

                    B 1 Reply Last reply 23 May 2023, 20:19
                    1
                    • G Guy Gizmo
                      22 May 2023, 15:05

                      @bashtian said in OS X: Clicks on fully transparent parts of windows no longer passes through in Qt 5.6:

                      This is an old post but I am facint the same issue on PyQt6, TransluscentBackground set on MacOS, on Windows it works fine, I did not test on Linux yet, did you @Guy-Gizmo found a solution?

                      Yes, actually. You can check out the Shaped Clock example for a demonstration of how to do this: https://doc.qt.io/qt-6/qtwidgets-widgets-shapedclock-example.html

                      The key is to use setMask() so that Qt knows which regions are supposed to be transparent to clicks.

                      That said, I did hit some issues with this. It's easy if the opaque region of your window is a simple shape like an ellipse or rectangle, since you can simply create a QRegion that covers that shape and set the mask to that, but that wasn't so in my case. There would be transparent pixels in any number of shapes that I wanted clicks to fall through. So I needed to render out the contents of my window as a QBitmap with any fully transparent pixels being white and all others being black.

                      BUT, there was another snag: on high-DPI displays it wouldn't quite work. At least on macOS the mask needed to have a pixel density of 1, even if the window itself has a pixel density of 2. So I wrote a method that generates a low density mask where it'll have a black pixel if any of the encompassing high-DPI pixels in the widget itself are the slightest bit non-transparent. I'll share that code here since others might find it useful:

                      void MyWindow::updateMask()
                      {
                          // In my case, the window contains a single top-level widget that does all of
                          // the rendering named contentsWidget.
                          // In order for this method to work, your widgets may need to be structured
                          // like that too:
                          QRect grabRect = rect().translated(-contentsWidget->geometry().topLeft());
                          QBitmap widgetMask = contentsWidget->grab(grabRect).mask();
                          
                          if (devicePixelRatio() == 2) {
                              // Unfortunately masks cannot be retina resolution, so if our widget is
                              // on a retina display, we have to generate a mask at non-retina
                              // resolution. But just rendering on a non-retina paint device and then
                              // generating a mask unfortunately doesn't work, as there'll inevitably
                              // be some pixels in the mask that should be black but are actually
                              // white. The right way to do it is to render at retina resolution, and
                              // then scale the bitmap mask down to the right size. But a normal
                              // scale won't do -- each pixel on our mask needs to be black if any of
                              // the four retina pixels that comprise it are black, i.e. a bitwise-OR
                              // of the four pixels. So we need to do the scaling ourselves:
                              QImage maskImage = widgetMask.toImage();    
                              QImage scaledMaskImage(maskImage.size()/2, maskImage.format());
                              
                              for(int y = 0; y < scaledMaskImage.height(); ++y) {
                                  uint16_t *line1 = (uint16_t *)maskImage.scanLine(y*2);
                                  uint16_t *line2 = (uint16_t *)maskImage.scanLine(y*2+1);
                                  uint8_t *line = scaledMaskImage.scanLine(y);
                                  
                                  for(int x = 0; x < scaledMaskImage.bytesPerLine(); ++x) {
                                      uint16_t a = line1[x];
                                      uint16_t b = line2[x];
                                      
                                      // This expression is the simplified version of the full expression
                                      // that will combine the mask's bits together as described above.
                                      line[x] = ~uint8_t((a     & 0x0001) |
                                                         (a>>1  & 0x0003) |
                                                         (a>>2  & 0x0006) |
                                                         (a>>3  & 0x000C) |
                                                         (a>>4  & 0x0018) |
                                                         (a>>5  & 0x0030) |
                                                         (a>>6  & 0x0060) |
                                                         (a>>7  & 0x00C0) |
                                                         (a>>8  & 0x0080) |                
                                                         (b     & 0x0001) |
                                                         (b>>1  & 0x0003) |
                                                         (b>>2  & 0x0006) |
                                                         (b>>3  & 0x000C) |
                                                         (b>>4  & 0x0018) |
                                                         (b>>5  & 0x0030) |
                                                         (b>>6  & 0x0060) |
                                                         (b>>7  & 0x00C0) |
                                                         (b>>8  & 0x0080));                
                                      // Not sure why it's necessary to negate this value given that
                                      // we're using the same format of image as the original mask
                                  }
                              }
                      
                              setMask(QBitmap::fromImage(scaledMaskImage));
                          } else {
                              setMask(widgetMask);
                          }
                      }
                      

                      Note that this might not be necessary on Windows. My app is currently macOS only so I'm not sure what's necessary to get proper transparent rendering in Windows in high DPI displays just yet.

                      You mentioned your project is Python and this is C++ code, but if you need to do the same thing in Python this can at least show what it is you need to do. The syntax for writing out a mask image will be different in Python but the math should be the same.

                      B Offline
                      B Offline
                      bashtian
                      wrote on 23 May 2023, 20:19 last edited by
                      #12

                      @Guy-Gizmo thank you very much for the answer, I will check it out, the click throu doesnt work at all on my mac, I will ttry to sort that out and see if I can combine both to achieve that effect.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved