新浪微博
腾讯微博
会员列表
统计排行
基本信息
到访IP统计
管理团队
管理统计
在线会员
会员排行
版块排行
帖子排行
标签排行
帮助
下拉
用户名
电子邮箱
用户名
密 码
记住登录
登录
找回密码
注册
快捷通道
关闭
您还没有登录,快捷通道只有在登录后才能使用。
立即登录
还没有帐号? 赶紧
注册一个
首页
论坛
Qt下载
作品展
群组
个人中心
捐赠、管理与开发
邮件订阅
帖子
文章
日志
用户
版块
群组
帖子
搜索
QTCN开发网
>
Qt应用版
>
Qml组件化编程2-可拖动组件和定制窗体
发帖
回复
返回列表
新帖
3369
阅读
3
回复
Qml组件化编程2-可拖动组件和定制窗体
[复制链接]
上一主题
下一主题
离线
dd759378563
UID:176218
注册时间
2016-10-15
最后登录
2019-12-19
在线时间
19小时
发帖
52
搜Ta的帖子
精华
0
金钱
577
威望
64
贡献值
0
好评度
54
访问TA的空间
加好友
用道具
新手上路
关闭
个人中心可以申请新版勋章哦
立即申请
知道了
加关注
发消息
只看楼主
正序阅读
楼主
发表于: 2019-05-11
简介
本文是《
Qml
组件化编程》系列文章的第二篇,涛哥将教大家,如何在Qml中实现可拖动组件,通过拖动
改变组件的大小和位置;以及实现定制窗体(无边框和标题栏), 并把拖动组件应用在顶层窗体。
文章主要发布在
涛哥的博客
和
知乎专栏-涛哥的Qt进阶之路
。
拖动组件
拖动改变坐标
拖动改变坐标的原理很简单,鼠标移动的时候改变目标Item的坐标即可。
说话的功夫,涛哥就造了个轮子出来
(其实是太常用了,涛哥已经写了很多遍)
复制代码
import QtQuick 2.9
import QtQuick.Controls 2.5
Item {
width: 800
height: 600
Rectangle {
id: moveItem
//注意拖动目标不要使用锚布局或者Layout,而是使用相对坐标
x: 100
y: 100
width: 300
height: 200
color: "lightblue"
MouseArea {
anchors.fill: parent
property real lastX: 0
property real lastY: 0
onPressed: {
//鼠标按下时,记录鼠标初始位置
lastX = mouseX
lastY = mouseY
}
onPositionChanged: {
if (pressed) {
//鼠标按住的前提下,坐标改变时,计算偏移量,应用到目标item的坐标上即可
moveItem.x += mouseX - lastX
moveItem.y += mouseY - lastY
}
}
}
}
}
上面例子中的MouseArea是拖动区域,Rectangle是要拖动的目标Item。
为了实现高度的可复用性,涛哥将MouseArea独立封装成一个组件,并提供一个control属性,
让外部使用组件实例的时候指定要拖动的目标。
复制代码
// TMoveArea.qml
import QtQuick 2.9
MouseArea {
id: root
property real lastX: 0
property real lastY: 0
property bool mask: false //有时候外面需要屏蔽拖动,导出一个mask属性, 默认false。
property var control: parent //导出一个control属性,指定要拖动的目标, 默认就用parent好了。注意目标要有x和y属性并且可修改
onPressed: {
lastX = mouseX;
lastY = mouseY;
}
onContainsMouseChanged: { //修改一下鼠标样式,以示区别
if (containsMouse) {
cursorShape = Qt.SizeAllCursor;
} else {
cursorShape = Qt.ArrowCursor;
}
}
onPositionChanged: {
if (!mask && pressed && control)
{
control.x +=mouseX - lastX
control.y +=mouseY - lastY
}
}
}
TMoveArea组件的用法
复制代码
Item {
anchors.fill: parent
Rectangle {
x: 100
y: 200
width: 400
height: 300
color: "darkred"
//实例化一个MoveArea
TMoveArea {
//指定control为parent。 其实默认就是parent,写出来示意一下
control: parent
anchors.fill: parent
}
}
}
一般来说,将
property
var
control
:
parent
中的var换成确切的类型比如Item会更好一些,Qml底层引擎处理var会慢一些,但是这样就限制了
目标必须是Item或者其子类。var是把双刃剑,有利有弊。涛哥后面要拖动的目标还包括QQuickView
这种类型,所以这里用var就好了。
拖动改变大小
拖动改变大小,原理参考下面这张示意图:
就是在要拖动的目标Item的8个位置分别放一个拖动组件,并在拖动时计算相应的坐标和大小变化即可。
涛哥先是把TMoveArea改造成了TDragRect
复制代码
// TDragRect.qml
import QtQuick 2.9
import QtQuick.Controls 2.0
Item {
id: root
property alias containsMouse: mouseArea.containsMouse
signal posChange(int xOffset, int yOffset)
implicitWidth: 12 //这里隐式的宽为12
implicitHeight: 12 //这里隐式的高为12
property int posType: Qt.ArrowCursor
//5.10之前, qml是不能定义枚举的,用只读的int属性代替一下。
readonly property int posLeftTop: Qt.SizeFDiagCursor
readonly property int posLeft: Qt.SizeHorCursor
readonly property int posLeftBottom: Qt.SizeBDiagCursor
readonly property int posTop: Qt.SizeVerCursor
readonly property int posBottom: Qt.SizeVerCursor
readonly property int posRightTop: Qt.SizeBDiagCursor
readonly property int posRight: Qt.SizeHorCursor
readonly property int posRightBottom: Qt.SizeFDiagCursor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
property int lastX: 0
property int lastY: 0
onContainsMouseChanged: {
if (containsMouse) {
cursorShape = posType;
} else {
cursorShape = Qt.ArrowCursor;
}
}
onPressedChanged: {
if (containsPress) {
lastX = mouseX;
lastY = mouseY;
}
}
onPositionChanged: {
if (pressed) {
posChange(mouseX - lastX, mouseY - lastY)
}
}
}
}
就是把前面的鼠标拖动时的处理逻辑,换成了带参数的信号发送出去,由外面决定怎么用这两个坐标
同时也定义了一组枚举,用来表示拖动区域的位置。位置不同,则鼠标样式不同。
之后涛哥写了一个叫TResizeBorder的组件,里面实例化了8个TDragRect组件,分别放在前面示意图
所示的位置,并实现了不同的处理逻辑。
(后来涛哥把上下左右四个中心点换成了四个边)
复制代码
// TResizeBorder.qml
import QtQuick 2.7
Rectangle {
id: root
color: "transparent"
border.width: 4
border.color: "black"
width: parent.width
height: parent.height
property var control: parent
TDragRect {
posType: posLeftTop
onPosChange: {
//不要简化这个判断条件,至少让以后维护的人能看懂。化简过后我自己都看不懂了。
if (control.x + xOffset < control.x + control.width)
control.x += xOffset;
if (control.y + yOffset < control.y + control.height)
control.y += yOffset;
if (control.width - xOffset > 0)
control.width-= xOffset;
if (control.height -yOffset > 0)
control.height -= yOffset;
}
}
TDragRect {
posType: posMidTop
x: (parent.width - width) / 2
onPosChange: {
if (control.y + yOffset < control.y + control.height)
control.y += yOffset;
if (control.height - yOffset > 0)
control.height -= yOffset;
}
}
TDragRect {
posType: posRightTop
x: parent.width - width
onPosChange: {
//向左拖动时,xOffset为负数
if (control.width + xOffset > 0)
control.width += xOffset;
if (control.height - yOffset > 0)
control.height -= yOffset;
if (control.y + yOffset < control.y + control.height)
control.y += yOffset;
}
}
TDragRect {
posType: posLeftMid
y: (parent.height - height) / 2
onPosChange: {
if (control.x + xOffset < control.x + control.width)
control.x += xOffset;
if (control.width - xOffset > 0)
control.width-= xOffset;
}
}
TDragRect {
posType: posRightMid
x: parent.width - width
y: (parent.height - height) / 2
onPosChange: {
if (control.width + xOffset > 0)
control.width += xOffset;
}
}
TDragRect {
posType: posLeftBottom
y: parent.height - height
onPosChange: {
if (control.x + xOffset < control.x + control.width)
control.x += xOffset;
if (control.width - xOffset > 0)
control.width-= xOffset;
if (control.height + yOffset > 0)
control.height += yOffset;
}
}
TDragRect {
posType: posMidBottom
x: (parent.width - width) / 2
y: parent.height - height
onPosChange: {
if (control.height + yOffset > 0)
control.height += yOffset;
}
}
TDragRect {
posType: posRightBottom
x: parent.width - width
y: parent.height - height
onPosChange: {
if (control.width + xOffset > 0)
control.width += xOffset;
if (control.height + yOffset > 0)
control.height += yOffset;
}
}
}
注意组件的顶层,使用的是透明的Rectangle,这样做的目的是,外面可以给这个组件设置
不同的颜色、边框等。无论哪种UI框架,透明处理都是需要一定的性能消耗的,所以在不需要
显示
出来的情况下,组件顶层最好还是用Item替代。
融合
我们来实例化一个能拖动改变大小和位置的Item
复制代码
Item {
width: 800
height: 600
Rectangle {
x: 300
y: 200
width: 120
height: 80
color: "darkred"
TMoveArea {
anchors.fill: parent
control: parent //默认就是parent,可以不写。这里写出来示意一下。
}
TResizeBorder {
control: parent //默认就是parent,可以不写。这里写出来示意一下。
anchors.fill: parent
}
}
}
用起来还是挺方便的,直接在目标Item里面实例化一个TResizeBorder组件,指定control即可。
这里同时实例化了TMoveArea和TResizeBorder两个组件,作为目标Item的child,就把两种功能 融合起来了。
注意前后顺序,如果反过来写则TMoveArea会把ResizeBorder遮盖住。(Qml是有z轴的,以后的文章涛哥再讲)
多级组件和Qml应用的框架结构
回过头来看一下,先是封装了两个组件:TMoveArea和TDragRect,之后又封装了一个组件:TResizeBorder,
而这个TResizeBorder里面使用了多个TDragRect组件,显然是有层级结构在里面的。
涛哥把TMoveArea和TDragRect这样的最基础的组件叫做一级组件,那么TResizeBorder就是一个二级组件。
涛哥大量的实战经验后,总结出了这样一种Qml应用框架结构:
一级和二级组件可以单独做成一个
插件
(或者叫
Qml
通用库)。
实际的
Qml
项目,在这些基础上,做一些功能性或者业务性的组件,即三级组件。
由这些三级组件组成一堆的页面(
Page
)。
最终的
main
.
qml
中,只剩下
Page
的布局。
示意图如下:
自定义
窗口
自定义窗口,这里以QQuickView
无边框
去掉边框,需要在
C++
中设置flag为Qt::FramelessWindowHint
同时我们注册view到qml上下文环境,给后面的功能来使用。
复制代码
...
QQuickView view;
view.setFlag(Qt::FramelessWindowHint);
view.rootContext()->setContextProperty("view", &view);
...
可拖动窗口
将我们前面做的两种拖动框放在main.qml中,填满顶层Item,并指定control为view。
复制代码
//main.qml
import QtQuick 2.0
#import TaoQuick 1.0 //这里是做成插件的情况下,引用了插件
#import "qrc:/Tao/Qml" //没有做插件的情况下,只要引用qml文件的资源路径即可
Item {
//标题栏
TitlePage {
id: titleRect
width: root.width
height: 60
...
//标题栏区域,实例化一个可以拖动位置的组件
TMoveArea {
height: parent.height
anchors {
left: parent.left
right: parent.right
rightMargin: 170 //留一点右边距,给最小化、最大化、关闭等按钮用
}
//指定拖动目标为view
control: view
}
...
}
//实例化一个拖动改大小的组件
TResizeBorder {
//指定拖动目标为view
control: view
anchors.fill: parent
}
...
}
自定义标题栏
标题栏的关键就是实现右侧的三个
按钮
,如果你看了
《Qml组件化编程1-按钮的定制与封装》
,
这都
没有
什么难度了。涛哥这里用
图片
按钮的方式实现。
注意最大化按钮在最大化状态下变成标准化按钮。
最小化:view.showMinimized()
最大化:view.showMaximized()
标准化:view.showNormal()
关闭: view.close()
这里给出关键代码
复制代码
Item{
...
property bool isMaxed: false
Row {
id: controlButtons
height: 20
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
spacing: 10
TImageBtn {
width: 20
height: 20
imageUrl: containsMouse ? "qrc:/Image/Window/minimal_white.png" : "qrc:/Image/Window/minimal_gray.png"
onClicked: {
view.showMinimized()
}
}
TImageBtn {
width: 20
height: 20
visible: !isMaxed
imageUrl: containsMouse ? "qrc:/Image/Window/max_white.png" : "qrc:/Image/Window/max_gray.png"
onClicked: {
view.showMaximized()
isMaxed = true
}
}
TImageBtn {
width: 20
height: 20
visible: isMaxed
imageUrl: containsMouse ? "qrc:/Image/Window/normal_white.png" : "qrc:/Image/Window/normal_gray.png"
onClicked: {
view.showNormal()
isMaxed = false
}
}
TImageBtn {
width: 20
height: 20
imageUrl: containsMouse ? "qrc:/Image/Window/close_white.png" : "qrc:/Image/Window/close_gray.png"
onClicked: {
view.close()
}
}
}
}
效果
最后,我们来看一下效果吧
转载声明
文章出自涛哥的博客 – 点击这里查看涛哥的博客
文章采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可, 转载请注明出处, 谢谢合作 ©
涛哥
联系方式
作者
涛哥
开发
理念
弘扬鲁班文化,传承工匠精神
博客
https://jaredtao.github.io
知乎
https://www.zhihu.com/people/wentao-jia
邮箱
jared2020@163.com
微信
xsd2410421
QQ
759378563
请放心联系我,乐于提供咨询服务,也可洽谈商务合作相关事宜。
打赏
如果觉得涛哥写的还不错,还请为涛哥打个赏,您的赞赏是涛哥持续创作的源泉。
共
条评分
涛哥是个Qml高手,著有《Qml组件化编程》《Qml特效》系列教程,见知乎专栏-Qt进阶之路:https://zhuanlan.zhihu.com/TaoQt
或微信公众号:Qt进阶之路
回复
举报
分享到
淘江湖
新浪
QQ微博
QQ空间
开心
人人
豆瓣
网易微博
百度
鲜果
白社会
飞信
离线
big_mouse
UID:144959
注册时间
2013-07-28
最后登录
2020-04-24
在线时间
467小时
发帖
186
搜Ta的帖子
精华
0
金钱
5340
威望
197
贡献值
0
好评度
187
访问TA的空间
加好友
用道具
禁止发言
加关注
发消息
只看该作者
3楼
发表于: 2020-04-15
共
条评分
回复
举报
离线
dd759378563
UID:176218
注册时间
2016-10-15
最后登录
2019-12-19
在线时间
19小时
发帖
52
搜Ta的帖子
精华
0
金钱
577
威望
64
贡献值
0
好评度
54
访问TA的空间
加好友
用道具
新手上路
加关注
发消息
只看该作者
2楼
发表于: 2019-05-18
代码格式已经整理
共
条评分
涛哥是个Qml高手,著有《Qml组件化编程》《Qml特效》系列教程,见知乎专栏-Qt进阶之路:https://zhuanlan.zhihu.com/TaoQt
或微信公众号:Qt进阶之路
回复
举报
离线
toby520
UID:121778
注册时间
2011-09-05
最后登录
2025-05-08
在线时间
2936小时
发帖
1265
搜Ta的帖子
精华
0
金钱
28791
威望
1375
贡献值
12
好评度
1365
访问TA的空间
加好友
用道具
论坛版主
加关注
发消息
只看该作者
1楼
发表于: 2019-05-12
代码格式 需要整理下
共
条评分
QtQML多多指教开发社区 http://qtclub.heilqt.com
将QtCoding进行到底
关注移动互联网,关注金融
开发跨平台客户端,服务于金融行业
专业定制界面
群号:312125701 373955953(qml控件定做)
回复
举报
发帖
回复
返回列表
http://www.qtcn.org/bbs
访问内容超出本站范围,不能确定是否安全
继续访问
取消访问
快速回复
限100 字节
您目前还是游客,请
登录
或
注册
进入高级模式
文字颜色
发 布
回复后跳转到最后一页
上一个
下一个
关闭
补充发布信息
验证码:
发 布
隐藏
快速跳转
站务及资讯
网站公告
新闻资讯
Qt官方发布
网站管理
Qt应用及资源
Qt 作品展
Qt代码秀
Qt应用版
Qt技术讨论区
Qt安装与发布
Qt中文处理
Qt基础编程
Qt QML开发
Qt嵌入式开发
Qt移动平台开发
其它技术开发讨论区
Qt图书专区
《C++ GUI Qt 4编程》(第2版)专栏
《Qt高级编程》专栏
《零基础学Qt4编程》专栏
《Qt设计模式》(第2版) 图书专栏
Python Qt GUI快速编程
Qt项目开发区
天池项目
Qt开放平台开发库
机械CAD
扇贝词典
非活跃项目区
社区中心
帖子回收站(Trash)
招聘、求职、供求、广告等
休闲娱乐
关闭
关闭
选中
1
篇
全选