Blur effect over a QWidget in Qt

后端 未结 3 1342
野趣味
野趣味 2020-11-28 12:35

Is there any way to blur a widget in Qt? For instance, supose I want to create a \'Loading...\' dialog and blur the background (not active window).

相关标签:
3条回答
  • 2020-11-28 12:51

    You can refer to this article if you want to apply blur effect on an image. After you create your blurred image you can draw it in QWidget::paintEvent() function.

    0 讨论(0)
  • 2020-11-28 13:03

    See QGraphicsBlurEffect Class and QWidget::setGraphicsEffect().

    0 讨论(0)
  • 2020-11-28 13:04

    This answer is in a series of my overlay-related answers: first, second, third.

    It requires some care if you wish for it to work on all platforms. You can't apply effects directly to top-level windows. The hierarchy needs to look as follows:

    ContainerWidget
         |
         +----------+
         |          |
    **Target**   Overlay
    

    You apply the effect to the Target widget (say, a QMainWindow). The ContainerWidget is a helper class that keeps the children occupying the full size of the widget. This obviates the need for an explicit zero-margin layout.

    The below works, even on a Mac. It wouldn't, had you foregone the ContainerWidget. This works portably on Qt 5 only, unfortunately. On Qt 4, your "cross platform" support excludes Mac :( It works OK on Windows using either Qt 4 (4.8.5) or Qt 5.

    screenshot

    // https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-blur-19383427
    #include <QtGui>
    #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
    #include <QtWidgets>
    #endif
    
    class OverlayWidget : public QWidget {
       void newParent() {
          if (!parent()) return;
          parent()->installEventFilter(this);
          raise();
       }
    public:
       explicit OverlayWidget(QWidget *parent = {}) : QWidget(parent) {
          setAttribute(Qt::WA_NoSystemBackground);
          setAttribute(Qt::WA_TransparentForMouseEvents);
          newParent();
       }
    protected:
       //! Catches resize and child events from the parent widget
       bool eventFilter(QObject *obj, QEvent *ev) override {
          if (obj == parent()) {
             if (ev->type() == QEvent::Resize)
                resize(static_cast<QResizeEvent*>(ev)->size());
             else if (ev->type() == QEvent::ChildAdded)
                raise();
          }
          return QWidget::eventFilter(obj, ev);
       }
       //! Tracks parent widget changes
       bool event(QEvent *ev) override {
          if (ev->type() == QEvent::ParentAboutToChange) {
             if (parent()) parent()->removeEventFilter(this);
          }
          else if (ev->type() == QEvent::ParentChange)
             newParent();
          return QWidget::event(ev);
       }
    };
    
    class ContainerWidget : public QWidget
    {
    public:
       explicit ContainerWidget(QWidget *parent = {}) : QWidget(parent) {}
       void setSize(QObject *obj) {
          if (obj->isWidgetType()) static_cast<QWidget*>(obj)->setGeometry(rect());
       }
    protected:
       //! Resizes children to fill the extent of this widget
       bool event(QEvent *ev) override {
          if (ev->type() == QEvent::ChildAdded) {
             setSize(static_cast<QChildEvent*>(ev)->child());
          }
          return QWidget::event(ev);
       }
       //! Keeps the children appropriately sized
       void resizeEvent(QResizeEvent *) override {
          for(auto obj : children()) setSize(obj);
       }
    };
    
    class LoadingOverlay : public OverlayWidget
    {
    public:
       LoadingOverlay(QWidget *parent = {}) : OverlayWidget{parent} {
          setAttribute(Qt::WA_TranslucentBackground);
       }
    protected:
       void paintEvent(QPaintEvent *) override {
          QPainter p{this};
          p.fillRect(rect(), {100, 100, 100, 128});
          p.setPen({200, 200, 255});
          p.setFont({"arial,helvetica", 48});
          p.drawText(rect(), "Loading...", Qt::AlignHCenter | Qt::AlignTop);
       }
    };
    
    namespace compat {
    #if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
    using QT_PREPEND_NAMESPACE(QTimer);
    #else
    using Q_QTimer = QT_PREPEND_NAMESPACE(QTimer);
    class QTimer : public Q_QTimer {
    public:
       QTimer(QTimer *parent = nullptr) : Q_QTimer(parent) {}
       template <typename F> static void singleShot(int period, F &&fun) {
          struct Helper : public QObject {
             F fun;
             QBasicTimer timer;
             void timerEvent(QTimerEvent *event) override {
                if (event->timerId() != timer.timerId()) return;
                fun();
                deleteLater();
             }
             Helper(int period, F &&fun) : fun(std::forward<F>(fun)) {
                timer.start(period, this);
             }
          };
          new Helper(period, std::forward<F>(fun));
       }
    };
    #endif
    }
    
    int main(int argc, char *argv[])
    {
       QApplication a{argc, argv};
       ContainerWidget base;
       QLabel label("Dewey, Cheatem and Howe, LLC.", &base);
       label.setFont({"times,times new roman", 32});
       label.setAlignment(Qt::AlignCenter);
       label.setGraphicsEffect(new QGraphicsBlurEffect);
       LoadingOverlay overlay(&base);
       base.show();
       compat::QTimer::singleShot(2000, [&]{
          overlay.hide();
          label.setGraphicsEffect({});
       });
       return a.exec();
    }
    
    0 讨论(0)
提交回复
热议问题