本系列所有文章可以在这里查看[http://blog.csdn.net/cloud_castle/article/category/2123873](http://blog.csdn.net/cloud_castle/article/category/2123873) 这个例子应该说是在上一个例子的基础上扩展了一些功能,扩展了什么呢?看看介绍: The Shaped Clock example shows how to apply a widget mask to a top-level widget to produce a shaped window. ![](https://box.kancloud.cn/2016-01-18_569cbd0127db4.jpg) Widget masks are used to customize the shapes of top-level widgets by restricting the available area for painting. On some window systems, setting certain window flags will cause the window decoration (title bar, window frame, buttons) to be disabled, allowing specially-shaped windows to be created. In this example, we use this feature to create a circular window containing an analog clock. Since this example's window does not provide a File menu or a close button, we provide a context menu with an Exit entry so that the example can be closed. Click the right mouse button over the window to open this menu. 介绍很长,主要也就是告诉我们他用window flags隐藏了边框,从而创建了一个特殊形状的窗口,因为没有关闭键,所以提供了关闭菜单。 看看代码: shapedclock.h: ~~~ #ifndef SHAPEDCLOCK_H #define SHAPEDCLOCK_H #include <QWidget> //! [0] class ShapedClock : public QWidget { Q_OBJECT public: ShapedClock(QWidget *parent = 0); QSize sizeHint() const; // 重写了sizeHint(),常成员函数,不会改变成员的值,其内部也不能调用非const函数 protected: void mouseMoveEvent(QMouseEvent *event); // 相比<span style="font-family: Arial, Helvetica, sans-serif;">Analog Clock Example,</span><span style="font-family: Arial, Helvetica, sans-serif;">增加了几个事件处理函数</span> void mousePressEvent(QMouseEvent *event); void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); private: QPoint dragPosition; // 一个全局的QPoint用来处理拖放事件 }; //! [0] #endif ~~~ 再来看实现代码: shapedclock.cpp: ~~~ #include <QtWidgets> #include "shapedclock.h" //! [0] ShapedClock::ShapedClock(QWidget *parent) : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowSystemMenuHint) // 其实我们不用setWindowsFlag()那么麻烦,放在初始化列表就可以了。第一个参数表明此窗口无框架,第二个添加了一个窗口系统菜单。 { QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(1000); QAction *quitAction = new QAction(tr("E&xit"), this); // 创建响应退出事件 quitAction->setShortcut(tr("Ctrl+Q")); // 设置快捷键Ctrl+Q connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); addAction(quitAction); setContextMenuPolicy(Qt::ActionsContextMenu); // 设置菜单显示策略,这里当actions()触发时显示 setToolTip(tr("Drag the clock with the left mouse button.\n" // 对整个窗口设置ToolTip "Use the right mouse button to open a context menu.")); setWindowTitle(tr("Shaped Analog Clock")); } //! [0] //! [1] void ShapedClock::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { dragPosition = event->globalPos() - frameGeometry().topLeft(); // 由鼠标相对屏幕的坐标减去窗口左上角坐标,其实得到的就是鼠标相对窗口的位置 event->accept(); // 尽管我们可以不这样做,但这是个好习惯。告诉Qt这个事件已经接收,不用再向我的父对象发送了。在这个例子中我们当然不希望拖动时钟时把主窗口一同拖动了吧。 } } //! [1] //! [2] void ShapedClock::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::LeftButton) { move(event->globalPos() - dragPosition); // 我们把上面的式子被减数和结果换个位子<span style="font-family: Arial, Helvetica, sans-serif;">就知道这个move的意思啦</span> event->accept(); } } //! [2] //! [3] void ShapedClock::paintEvent(QPaintEvent *) // paint函数可以参见Analog Clock Example { static const QPoint hourHand[3] = { QPoint(7, 8), QPoint(-7, 8), QPoint(0, -40) }; static const QPoint minuteHand[3] = { QPoint(7, 8), QPoint(-7, 8), QPoint(0, -70) }; QColor hourColor(127, 0, 127); QColor minuteColor(0, 127, 127, 191); int side = qMin(width(), height()); QTime time = QTime::currentTime(); 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(hourColor); painter.save(); painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0))); painter.drawConvexPolygon(hourHand, 3); painter.restore(); painter.setPen(hourColor); for (int i = 0; i < 12; ++i) { painter.drawLine(88, 0, 96, 0); painter.rotate(30.0); } painter.setPen(Qt::NoPen); painter.setBrush(minuteColor); painter.save(); painter.rotate(6.0 * (time.minute() + time.second() / 60.0)); painter.drawConvexPolygon(minuteHand, 3); painter.restore(); painter.setPen(minuteColor); for (int j = 0; j < 60; ++j) { if ((j % 5) != 0) painter.drawLine(92, 0, 96, 0); painter.rotate(6.0); } } //! [3] //! [4] void ShapedClock::resizeEvent(QResizeEvent * /* event */) // 与paintEvent一样,该事件在窗口创建时也会被调用一次,Mask创建在这个事件保证Mask区域能被准备更新 { int side = qMin(width(), height()); QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side, // 使用QRegion选择了一时钟的圆形区域 side, QRegion::Ellipse); setMask(maskedRegion); // 由setMask创建了这个"不规则窗口",而该窗口只会在可视区域相应鼠标事件 } //! [4] //! [5] QSize ShapedClock::sizeHint() const // 这个函数的返回值将作为Widget大小的一个参考,之所以说参考,当窗体内有布局和控件时,将优先考虑它们的size和sizePolicy { return QSize(100, 100); } //! [5] ~~~