我需要在我的应用程序中实现一个“正在加载...”窗口,但我更喜欢用一个带有上面文本的深色透明层来覆盖整个QMainWindow。有谁知道怎么做?我不确定如何在Qt中重叠小部件/布局。任何帮助将不胜感激。
答案 0 :(得分:18)
这个答案出现在我的与叠加相关的一系列答案中:first,second,third。
最简单的解决方案是简单地将子透明小部件添加到QMainWindow
。该小部件必须仅跟踪其父窗口的大小。正确处理小部件父级的更改以及与兄弟姐妹的z顺序非常重要。以下是如何操作的正确示例。
如果要叠加叠加层,后续叠加层应为z序中OverlayWidget
的子节点。如果他们是OverlayWidget
的兄弟姐妹,他们的堆叠顺序是不确定的。
此解决方案的好处是可以提供与其他代码的最小耦合。它不需要您应用叠加层的小部件的任何知识。您可以将叠加层应用于QMainWindow
或任何其他窗口小部件,窗口小部件也可以布局。
重新实现QMainWindow
的绘画事件不会被认为是最好的设计。它使它与特定的类绑定。如果你真的认为QWidget
实例的开销太大,你最好用测量来证明是这种情况。
当然,可以将叠加层仅设为QObject
并将绘制代码放入事件过滤器中。那将是另一种解决方案。这很难做到,因为您还必须正确处理父窗口小部件的Qt::WA_StaticContents
属性,并且窗口小部件可能会调用其scroll()
方法。处理单独的小部件是最简单的。
// https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-widget-19362455
#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 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{¢ral};
QTimer::singleShot(5000, &overlay, SLOT(hide()));
window.setCentralWidget(¢ral);
window.show();
return a.exec();
}
答案 1 :(得分:8)
我建议在顶部执行一个模态,无框架对话框,并在背景小部件上添加图形效果。 这是恕我直言,这是一个非常灵活和简短的解决方案,无需直接触及事件系统。
性能可能不好 - 可以通过调用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 );
答案 2 :(得分:0)
如果您正在谈论在“主窗口”上使用单独的布局/窗口小部件,您可以通过UI编辑器或UI的构造函数中的“加载...”窗口模式。