这是一个有点通用的容器QWidget 的示例,它管理覆盖QWidget,在这种情况下,它是一个工具栏样式的小部件(但它实际上可以是任何东西)。对于一个简单的示例,代码有点冗长,但它试图涵盖一些不同的用例以使容器更加灵活。叠加小部件可以是 QToolBar,但不一定是。
这里的主要技术是覆盖小部件不在布局中定位,而是由父小部件“手动”管理其几何形状(参见代码中的positionToolbar())。每当容器或叠加层的大小发生变化时,都需要重新调整此几何形状。最方便的“钩子”是我们在示例中重新实现的QWidget::resizeEvent() 方法。我们还监视覆盖小部件的大小变化,例如。添加/删除子项或修改其样式时。
另一个方向可能是编写一个自定义的QLayout 子类,它基本上做同样的事情(在QLayoutItem::setGeometry() 覆盖中)。它会涉及更多,但也更灵活,因为它可以用于任何小部件或作为子布局。
更新:我已经创建了这样一个布局管理器,它被称为OverlayStackLayout (docs)。也是一个简单但实用的image viewer example 应用程序,灵感来自这个简短的应用程序。
ToolbarOverlayWidget.h
#include <QEvent>
#include <QPointer>
#include <QToolBar>
#include <QWidget>
class ToolbarOverlayWidget : public QWidget
{
Q_OBJECT
public:
ToolbarOverlayWidget(QWidget *parent = nullptr) :
QWidget(parent)
{
// WA_LayoutOnEntireRect will ensure that any QLayout set on this widget will
// ignore QWidget::contentsMargins(), which allows us to use them for toolbar
// margins/positioning instead. This does not affect any layout()->contentsMargins()
// which can still be used to pad anything the main layout itself contains.
setAttribute(Qt::WA_LayoutOnEntireRect);
// create a default toolbar
setToolbar(new QToolBar(this));
}
~ToolbarOverlayWidget() override
{
// don't delete the toolbar widget if we don't own it
if (m_toolbar && !m_ownTbWidget)
m_toolbar->setParent(nullptr);
}
// Returns toolbar widget instance as a QToolBar.
// Returns nullptr if no toolbar widget is set, or widget does not inherit QToolBar.
QToolBar *toolbar() const { return qobject_cast<QToolBar*>(m_toolbar.data()); }
// Set a widget to be used as a toolbar. ToolbarOverlayWidget takes ownership of toolbar.
void setToolbar(QWidget *toolbar)
{
// dispose of old toolbar?
if (m_toolbar) {
m_toolbar->removeEventFilter(this);
m_toolbar->disconnect(this);
if (m_ownTbWidget)
m_toolbar->deleteLater();
else
m_toolbar->setParent(nullptr);
m_toolbar.clear();
}
if (!toolbar)
return;
m_toolbar = toolbar;
// toolbar's parent should be this widget, also keep track of if we owned it originally
m_ownTbWidget = (m_toolbar->parent() == this);
if (!m_ownTbWidget)
m_toolbar->setParent(this);
m_toolbar->setAutoFillBackground(true); // ensure a background if otherwise unstyled
m_toolbar->installEventFilter(this); // see eventFilter()
if (QToolBar *tb = qobject_cast<QToolBar*>(toolbar)) {
// reposition toolbar if icon size or button style change
connect(tb, &QToolBar::iconSizeChanged, this, [this](const QSize &) {
positionToolbar(); });
connect(tb, &QToolBar::toolButtonStyleChanged, this, [this](Qt::ToolButtonStyle) {
positionToolbar(); });
}
if (isVisible())
positionToolbar();
}
QSize sizeHint() const override
{
if (m_toolbar.isNull())
return QWidget::sizeHint();
// ensure a reasonable size hint if we have a toolbar which is larger than any contents
return QWidget::sizeHint().expandedTo(m_toolbar->sizeHint());
}
protected:
void resizeEvent(QResizeEvent *e) override
{
QWidget::resizeEvent(e);
// keep the toolbar properly positioned
positionToolbar();
}
// filter is installed on the toolbar widget
bool eventFilter(QObject *w, QEvent *e) override
{
if (!m_toolbar.isNull() && w == m_toolbar) {
switch (e->type()) {
// reposition the toolbar if its size hint (possibly) changed
case QEvent::ChildAdded:
case QEvent::ChildRemoved:
case QEvent::StyleChange:
case QEvent::FontChange:
if (isVisible())
positionToolbar();
break;
default:
break;
}
}
return QWidget::eventFilter(w, e);
}
private slots:
// Keep the toolbar properly positioned and sized
void positionToolbar() const
{
if (m_toolbar.isNull())
return;
const QRect rect = contentsRect(); // available geometry for toolbar
QRect tbRect(rect.topLeft(), m_toolbar->sizeHint()); // default TB position and size
// expand to full width?
if (m_toolbar->sizePolicy().expandingDirections() & Qt::Horizontal)
tbRect.setWidth(rect.width());
// constrain width if it is too wide to fit
else if (tbRect.width() > rect.width())
tbRect.setWidth(rect.width());
// otherwise center the toolbar if it is narrower than available width
else if (tbRect.width() < rect.width())
tbRect.moveLeft(rect.x() + (rect.width() - tbRect.width()) / 2);
// constrain height
if (tbRect.height() > rect.height())
tbRect.setHeight(rect.height());
// Set position and size of the toolbar.
m_toolbar->setGeometry(tbRect);
// Make sure the toolbar stacks on top
m_toolbar->raise();
}
private:
QPointer<QWidget> m_toolbar;
bool m_ownTbWidget = true;
};
实现示例:这显示了几个样式选项,都使用 CSS。在第一个中,工具栏是最小宽度并在可用区域中居中,在第二个中,工具栏的样式为全宽,带有居中的按钮和部分透明的背景。第二个版本使用普通的 QWidget 而不是 QToolBar,因为它的样式更灵活(QToolBar 有一些“怪癖”可以很好地表达)。
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Use a stack widget as top-level for demo. This will have two pages.
QStackedWidget stack;
stack.resize(640, 480);
// common style for tool buttons
const QString commonCss(QStringLiteral(
"QToolButton {"
" font: bold normal 14px sans-serif;"
" color: #62777F;"
" background: transparent;"
" border-radius: 12px;"
" padding: 3px 6px 4px;"
"}"
"QToolButton:checked, QToolButton:hover {"
" color: #D5F2E5;"
" background-color: #62777F;"
"}"
"QToolButton:pressed { background-color: #72AF95; }"
));
// creates a new ToolbarOverlayWidget holding one scalable image label
auto imageWidget = [&stack](const QString &img) {
ToolbarOverlayWidget *w = new ToolbarOverlayWidget(&stack);
w->setLayout(new QVBoxLayout);
w->layout()->setContentsMargins(0,0,0,0);
QLabel *lbl = new QLabel(w);
lbl->setPixmap(QPixmap(img));
lbl->setScaledContents(true);
lbl->setMinimumSize(160, 120);
w->layout()->addWidget(lbl);
return w;
};
// Page 1: The first stack page uses a default QToolBar, which is simpler but less flexible.
{
ToolbarOverlayWidget *widget = imageWidget("../../images/image1.jpg");
// Set toolbar appearance
widget->setContentsMargins(0, 10, 0, 0); // 10px above toolbar, works better than CSS margin
widget->toolbar()->setStyleSheet(commonCss + QLatin1String(
"QToolBar {"
" background: #B5CAC1;"
" border-radius: 14px;"
" padding: 4px;" // can only set one padding for all sides of a qtoolbar
" spacing: 12px;" // between items
"}"
"QToolBar::separator { width: 1px; background-color: #72AF95; }"
));
// Add items to toolbar
QActionGroup *viewGrp = new QActionGroup(widget);
auto addViewAction = [viewGrp, widget](const QString &ttl, bool chk = false) {
QAction *act = widget->toolbar()->addAction(ttl);
act->setCheckable(true);
act->setChecked(chk);
viewGrp->addAction(act);
return act;
};
addViewAction("Years");
addViewAction("Months");
addViewAction("Days");
addViewAction("All Photos", true);
widget->toolbar()->addSeparator();
// page stack "push" action
QObject::connect(widget->toolbar()->addAction("view >"), &QAction::triggered, [&stack]() {
stack.setCurrentIndex(1);
});
stack.addWidget(widget);
}
// Page 2: This page uses a plain widget for a toolbar.
{
ToolbarOverlayWidget *widget = imageWidget("../../images/image1.jpg");
// Create a custom toolbar-style widget
QWidget *toolbar = new QWidget(widget);
toolbar->setLayout(new QHBoxLayout);
toolbar->layout()->setContentsMargins(3, 14, 3, 28);
toolbar->layout()->setSpacing(18);
toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
toolbar->setObjectName("ToolbarWidget");
toolbar->setStyleSheet(commonCss + QLatin1String(
"#ToolbarWidget {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 black, stop: 1 transparent);"
"}"
"QToolButton {"
" color: #D5F2E5;"
" background-color: #62777F;"
"}"
"QToolButton:checked, QToolButton:hover:!pressed {"
" color: #62777F;"
" background-color: #D5F2E5;"
"}"
));
// Add items to toolbar
auto addButton = [toolbar](const QString &ttl, QLayout *lo, bool chk = false) {
QToolButton *tb = new QToolButton(toolbar);
tb->setText(ttl);
tb->setCheckable(chk);
lo->addWidget(tb);
return tb;
};
// left expander to keep buttons centered
toolbar->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
// page stack "pop" action
QObject::connect(addButton("< back", toolbar->layout()), &QToolButton::clicked, [&stack]() {
stack.setCurrentIndex(0);
});
addButton("Adjust", toolbar->layout());
addButton("Select", toolbar->layout(), true);
// zoom buttons, new sub-layout w/out spacing
QHBoxLayout *zoomBtnLayout = new QHBoxLayout;
zoomBtnLayout->setSpacing(0);
const QString zoomCss =
QStringLiteral("QToolButton { border-top-%1-radius: 0; border-bottom-%1-radius: 0; }");
addButton("-", zoomBtnLayout)->setStyleSheet(zoomCss.arg("right"));
addButton("+", zoomBtnLayout)->setStyleSheet(zoomCss.arg("left"));
toolbar->layout()->addItem(zoomBtnLayout);
// right expander to keep buttons centered
toolbar->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
// Use the custom widget as toolbar
widget->setToolbar(toolbar);
stack.addWidget(widget);
}
stack.show();
return a.exec();
}
#include "main.moc"
相关问题: