qml demo分析(maskedmousearea-异形窗口)
一、效果展示
如本文的标题所示,这篇文章分析的demo是一个异形窗口,主要展示鼠标在和异形区域交互的使用,效果如图1所示,当鼠标移动到白云或者月亮上时,相应的物体会高亮,当鼠标按下时,物体会有一个放大的动画效果,鼠标离开时恢复原样。
图1 月亮和云朵
二、源码分析
正式算起来,这是我分析的第五篇qml示例程序了,在这里他么有一个共同点,qml控件直接展示不了的东西都是使用C++类或者js函数来完成,比如这篇文章要讲的异形区域判断;qml demo分析(customgeometry-贝塞尔曲线)文章中的贝塞尔曲线绘制;qml demo分析(maroon-小游戏)小游戏中的代码复杂逻辑使用js控制;qml demo分析(abstractitemmodel-数据分离)示例中的model结构等。那么从这几篇文章中我们也能体会到qml不是一个人在战斗,他更多的是在于ui展示,而具体的逻辑或者更为复杂的操作需要交给C++程序或者js代码来完成,关于C++和qml混合编程、js和qml混合编程之前的代码都有涉及,不了解的同学可以直接点击相关链接进入。
本篇示例代码相对来说比较简单,主要就是两部分:qml和C++
1、qml代码
qml代码中总共有3张图片,先添加的图片在下层显示,如果你想要让某一张图片在上次显示那么就需要在最后添加该组件。这3张图片的行为是一模一样,因此我只分析月亮这一个组件,代码如下
1 //后添加的元素在上 2 Image { 3 id: moon 4 anchors.centerIn: parent 5 scale: moonArea.pressed ? 1.1 : 1.0//按下时 放大 6 opacity: moonArea.containsMouse ? 1.0 : 0.7//hover时 无透明度 7 source: Qt.resolvedUrl("images/moon.png") 8 9 MaskedMouseArea {//自定义组件 新增鼠标是否按下判断、鼠标是否在区域内 10 id: moonArea 11 anchors.fill: parent 12 alphaThreshold: 0 13 maskSource: moon.source 14 } 15 16 Behavior on opacity {//透明度使用渐变 17 NumberAnimation { duration: 200 } 18 } 19 Behavior on scale {//放大使用渐变 20 NumberAnimation { duration: 100 } 21 } 22 }
上述代码就是qml中关于月亮的展示,MaskedMouseArea组件是使用qmlRegisterType宏注册到qml系统中的。本篇文章我就不在讲解main函数了,如果忘记的同学可以到qml demo分析(customgeometry-贝塞尔曲线)文章中回顾。
2、C++代码
本篇文章有一个自定义的C++类,主要是给qml程序提供鼠标按下、鼠标hover等状态,头文件如下
1 #include <QImage> 2 #include <QQuickItem> 3 4 class MaskedMouseArea : public QQuickItem 5 { 6 Q_OBJECT 7 Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged) 8 Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged) 9 Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged) 10 Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged) 11 12 public: 13 MaskedMouseArea(QQuickItem *parent = 0); 14 15 bool contains(const QPointF &point) const;//重写contains接口 判断鼠标是否在在异形窗口内 默认实现判断参数点是否在bounding rect内 16 17 //鼠标是否按下 配合Q_PROPERTY宏 可以被qml系统调用 例如:scale: rightCloudArea.pressed ? 1.1 : 1.0 18 bool isPressed() const { return m_pressed; } 19 bool containsMouse() const { return m_containsMouse; } 20 21 QUrl maskSource() const { return m_maskSource; } 22 void setMaskSource(const QUrl &source); 23 24 qreal alphaThreshold() const { return m_alphaThreshold; } 25 void setAlphaThreshold(qreal threshold); 26 27 signals: 28 void pressed();//自定义信号 在qml系统中均有OnPressed槽 29 void released(); 30 void clicked(); 31 void canceled(); 32 void pressedChanged(); 33 void maskSourceChanged(); 34 void containsMouseChanged(); 35 void alphaThresholdChanged(); 36 37 protected: 38 void setPressed(bool pressed); 39 void setContainsMouse(bool containsMouse); 40 41 void mousePressEvent(QMouseEvent *event); 42 void mouseReleaseEvent(QMouseEvent *event); 43 44 void hoverEnterEvent(QHoverEvent *event); 45 void hoverLeaveEvent(QHoverEvent *event); 46 47 void mouseUngrabEvent(); 48 49 private: 50 bool m_pressed; 51 QUrl m_maskSource; 52 QImage m_maskImage; 53 QPointF m_pressPoint; 54 qreal m_alphaThreshold; 55 bool m_containsMouse; 56 };
头文件中的Q_PROPERTY如果忘记其含义,可以到qml demo分析(customgeometry-贝塞尔曲线)文章中了解,其中每一个Q_PROPERTY宏第一个参数为属性,READ指定读取属性的接口,WRITE指定设置属性的接口,NOTIFY指定当属性改变时所触发的信号,当然了这个属性还有更多的其他功能,感兴趣的同学可以自行上帮助文档查阅。
1 bool MaskedMouseArea::contains(const QPointF &point) const 2 { 3 if (!QQuickItem::contains(point) || m_maskImage.isNull()) 4 return false; 5 6 QPoint p = point.toPoint(); 7 8 if (p.x() < 0 || p.x() >= m_maskImage.width() || 9 p.y() < 0 || p.y() >= m_maskImage.height()) 10 return false; 11 12 qreal r = qBound<int>(0, m_alphaThreshold * 255, 255); 13 return qAlpha(m_maskImage.pixel(p)) > r;//根据alpha值判断 异形区域 14 }
自定义QQuickItem类,最重要的是判断鼠标是否在区域内,也就是contains函数,该函数默认是判断鼠标是否在组件所在矩形区域,为了让交互行为更好,我们需要让鼠标在月亮的圆上或者云朵内才高亮物体,因此我们重写了次接口,该接口是用图片的alpha值来判断,当该值达到一定透明度时,认为其不在区域内,具体代码如上所示。
这篇示例代码在Qt5.7.0_vs2013\Examples\Qt-5.7\quick\customitems\maskedmousearea目录下。我使用的qt5.6.1-1版本,该版本为自行编译版本,编译参考:msvc2013编译qt5.6源码
三、相关文章
qml demo分析(abstractitemmodel-数据分离)
qml demo分析(customgeometry-贝塞尔曲线)