半透明背景
示例展示了如何创建一个具有半透明背景的圆窗。
将背景设置为半透明的控件将对所有未绘制的像素透明,并且背景会通过小于100%的不透明度绘制的像素发光。未进行任何绘制的像素也不会接收任何鼠标输入。这可以用于自定义顶级控件的形状。在大多数窗口系统中,设置某些窗口标志将会导致窗口装饰(标题栏、窗口框架、按钮)被禁用,从而可以创建特殊形状的窗口。在这个例子中,我们使用这个特性来创建一个包含模拟时钟的圆形窗口。
由于这个示例的窗口不提供文件菜单或关闭按钮,我们提供了一个带有退出条目的上下文菜单,以便示例可以关闭。在窗口上单击鼠标右键以打开此菜单。
ShapeClock 类定义
ShapeClock
类基于模拟时钟示例中定义的 AnalogClock
类。整个类定义如下:
class ShapedClock : public QWidget { Q_OBJECT public: ShapedClock(QWidget *parent = nullptr); QSize sizeHint() const override; protected: void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void paintEvent(QPaintEvent *event) override; private: QPoint dragPosition; };
paintEvent() 实现将在半透明背景(表盘)上绘制一个模拟时钟。此外,我们还实现了 sizeHint(),这样我们就不需要显式调整控件的大小。
由于包含时钟控件的窗口将没有任何标题栏,我们提供了 mouseMoveEvent() 和 mousePressEvent() 的实现,以允许将时钟拖到屏幕上。变量 dragPosition
允许我们记录用户最后在控件上单击的位置。
ShapeClock 类实现
ShapeClock
构造函数设置了一个定时器并将其连接到控件的 update() 槽。此外,我们在控件上添加了一个操作,在控件上右击时,它将通过上下文菜单自动可用。
ShapedClock::ShapedClock(QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint) { setAttribute(Qt::WA_TranslucentBackground); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, QOverload<>::of(&ShapedClock::update)); timer->start(1000); QAction *quitAction = new QAction(tr("E&xit"), this); quitAction->setShortcut(tr("Ctrl+Q")); connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); addAction(quitAction); setContextMenuPolicy(Qt::ActionsContextMenu); setToolTip(tr("Drag the clock with the left mouse button.\n" "Use the right mouse button to open a context menu.")); setWindowTitle(tr("Shaped Analog Clock")); }
我们通过设置控件属性 Qt::WA_TranslucentBackground 来请求一个透明窗口。我们通过设置控件的 Qt::FramelessWindowHint 标志来告知窗口管理器不要用窗口框架装饰控件。因此,我们需要提供一种方法,让用户可以围绕屏幕移动时钟。
鼠标按钮事件被传送到 mousePressEvent()
处理器。
void ShapedClock::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft(); event->accept(); } }
如果鼠标左键在控件上按下,我们记录控件框架左上角(即使隐藏)与鼠标单击点之间的全局(屏幕)坐标位移。如果用户在按下左键的同时移动鼠标,将使用这个位移。由于我们处理了这个事件,我们通过调用它的 accept() 函数来接受它。
当鼠标在部件上移动时,会调用 mouseMoveEvent()
处理器。
void ShapedClock::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { move(event->globalPosition().toPoint() - dragPosition); event->accept(); } }
如果在鼠标移动时按下左键,部件的左上角将被移动到通过从全局坐标系中当前光标位置减去 dragPosition
所得到的点。如果我们拖动部件,我们也会接收该事件。
paintEvent()
函数主要与在 Analog Clock 示例中描述的相同。唯一的增加是我们使用 QPainter::drawEllipse() 绘制一个圆形钟面。我们将画笔的不透明度降低到 90%,并使用调色板的自定义背景色。
void ShapedClock::paintEvent(QPaintEvent *) { static const QPoint hourHand[4] = { QPoint(5, 14), QPoint(-5, 14), QPoint(-4, -71), QPoint(4, -71) }; static const QPoint minuteHand[4] = { QPoint(4, 14), QPoint(-4, 14), QPoint(-3, -89), QPoint(3, -89) }; static const QPoint secondsHand[4] = { QPoint(1, 14), QPoint(-1, 14), QPoint(-1, -89), QPoint(1, -89) }; const QColor hourColor(palette().color(QPalette::Text)); const QColor minuteColor(palette().color(QPalette::Text)); const QColor secondsColor(palette().color(QPalette::Accent)); int side = qMin(width(), height()); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); painter.translate(width() / 2, height() / 2); painter.scale(side / 200.0, side / 200.0); painter.setPen(Qt::NoPen); painter.setBrush(palette().window()); painter.setOpacity(0.9); painter.drawEllipse(QPoint(0, 0), 98, 98); painter.setOpacity(1.0); QTime time = QTime::currentTime(); painter.setPen(Qt::NoPen); painter.setBrush(hourColor); painter.save(); painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0))); painter.drawConvexPolygon(hourHand, 4); painter.restore(); for (int i = 0; i < 12; ++i) { painter.drawRect(73, -3, 16, 6); painter.rotate(30.0); } painter.setBrush(minuteColor); painter.save(); painter.rotate(6.0 * time.minute()); painter.drawConvexPolygon(minuteHand, 4); painter.restore(); painter.setBrush(secondsColor); painter.save(); painter.rotate(6.0 * time.second()); painter.drawConvexPolygon(secondsHand, 4); painter.drawEllipse(-3, -3, 6, 6); painter.drawEllipse(-5, -68, 10, 10); painter.restore(); painter.setPen(minuteColor); for (int j = 0; j < 60; ++j) { painter.drawLine(92, 0, 96, 0); painter.rotate(6.0); } }
最后,我们为部件实现了 sizeHint()
,以便在第一次显示时给出一个合理的默认大小。
© 2024 Qt 公司。本文件中包含的文档贡献归各自所有者所有。提供的文档在 Free Software Foundation 发布的 GNU 自由文档许可协议 1.3 版 的条款下获得许可。Qt 及其相关标志是芬兰的 Qt 公司和/或全球其他地区的商标。所有其他商标归各自所有者所有。