Темный прозрачный слой над QMainWindow в Qt

Мне нужно реализовать окно «Загрузка …» в моем приложении, но я предпочитаю покрывать весь QMainWindow темным прозрачным слоем с текстом выше. Кто-нибудь знает, как это сделать? Я не уверен, как перекрывать виджеты / макеты в Qt. Любая помощь будет оценена.

Этот ответ находится в серии моих ответов, связанных с оверлей: первый , второй , третий .

Самое тривиальное решение – просто добавить дочерний прозрачный виджет в QMainWindow . Этот виджет должен просто отслеживать размер его родительского windows. Важно правильно обрабатывать изменения родительского виджета и z-порядок с братьями и сестрами. Ниже приведен правильный пример того, как это сделать.

Если вы хотите складывать наложения, последующие оверлеи должны быть OverlayWidget элементами OverlayWidget в z-порядке. Если они должны быть братьями и сестрами OverlayWidget , их порядок укладки не определен.

Это решение имеет преимущество обеспечения минимальной связи с другим кодом. Это не требует каких-либо знаний от виджета, к которому вы применяете наложение. Вы можете применить наложение к QMainWindow или любому другому виджету, виджет также может быть в макете.

Повторное выполнение события окраски QMainWindow не будет считаться лучшим дизайном. Это делает его привязанным к определенному classу. Если вы действительно считаете, что экземпляр QWidget слишком много накладных расходов, вам лучше иметь измерения, чтобы показать, что это так.

Конечно, можно сделать наложение просто QObject и поместить код рисования в фильтр событий. Это было бы альтернативным решением. Это сложнее сделать, так как вам также необходимо правильно Qt::WA_StaticContents атрибут Qt::WA_StaticContents родительского виджета и с помощью виджета, потенциально вызывающего его метод scroll() . Работа с отдельным виджетами является самой простой.

скриншот примера

 // https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-widget-19362455 #include  #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include  #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(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 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::AlignVCenter); } }; int main(int argc, char * argv[]) { QApplication a{argc, argv}; QMainWindow window; QLabel central{"Hello"}; central.setAlignment(Qt::AlignHCenter | Qt::AlignTop); central.setMinimumSize(400, 300); LoadingOverlay overlay{&central}; QTimer::singleShot(5000, &overlay, SLOT(hide())); window.setCentralWidget(&central); window.show(); return a.exec(); } 

Я бы предложил выполнить модальный, бескаркасный диалог сверху и добавить графический эффект на фоновый виджет. Это IMHO очень гибкое и короткое решение, не касаясь системы событий напрямую.

Производительность может быть плохой – можно улучшить это, вызвав drawSource (), но у меня пока нет надежного решения.

 class DarkenEffect : public QGraphicsEffect { public: void draw( QPainter* painter ) override { QPixmap pixmap; QPoint offset; if( sourceIsPixmap() ) // No point in drawing in device coordinates (pixmap will be scaled anyways) pixmap = sourcePixmap( Qt::LogicalCoordinates, &offset ); else // Draw pixmap in device coordinates to avoid pixmap scaling; { pixmap = sourcePixmap( Qt::DeviceCoordinates, &offset ); painter->setWorldTransform( QTransform() ); } painter->setBrush( QColor( 0, 0, 0, 255 ) ); // black bg painter->drawRect( pixmap.rect() ); painter->setOpacity( 0.5 ); painter->drawPixmap( offset, pixmap ); } }; // prepare overlay widget overlayWidget->setWindowFlags( Qt::FramelessWindowHint | Qt::Dialog | Qt::WindowStaysOnTopHint ); // usage parentWidget->setGraphicsEffect( new DarkenEffect ); overlayWidget->exec(); parentWidget->setGraphicsEffect( nullptr ); 

Если вы говорите об использовании отдельного макета / виджета над своим «Главным окном», вы можете просто сделать окно «Загрузка …» модальным либо через редактор пользовательского интерфейса, либо в конструктор для вашего пользовательского интерфейса.

  • iPhone: скрыть панель поиска UITableView по умолчанию
  • Как определить несколько действий JButton из другого classа
  • Как программно перемещать UIScrollView для фокусировки в контроле над клавиатурой?
  • Применение MVC с помощью JavaFx
  • Как отобразить текущее значение предпочтения Android в сводке предпочтений?
  • Форматирование даты MM / DD / YYYY в текстовом поле в VBA
  • Подвижные столбцы со значением с помощью java
  • Как лучше всего настроить Swing GUI?
  • Qt сигнализирует по streamам, один - stream GUI?
  • установить и отключить иконки JToggleButton
  • Android RecyclerView добавление и удаление элементов
  • Давайте будем гением компьютера.