本文使用的是QtCreater,qml的版本是5.15.2.
首先建一个box,设置为鲜艳的红色,随后使用MouseArea的属性,注意这里要设置MouseArea的大小,可以用x,y,width,height;
也可以使用锚定布局元素anchors.fill=parent
表示填充成其父物体的形状。
随后使用drag.target: box
表示鼠标的拖拽绑定在box这个物体上,随后就可以拖拽动这个物体了。
核心代码:
我这里的文件名:drag_page.qml
Item {
Text
{
x: 49
y: 49
width: 131
height: 24
text: qsTr("window_2")
font.styleName: "Regular"
font.pixelSize: 18
color: "#000000"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Rectangle
{
id:box
x:200
y:60
width:50
height: 50
color:"#FF0000"
//Drag.active: mouse.drag.active
}
MouseArea
{
id:mouse
//hoverEnabled: true
anchors.fill: parent
drag.target: box
//drag.axis: Drag.XAndYAxis
}
}
我这里使用两个loader加载两个窗口,形状如下:
左右的窗口是目前创建的,就是客户端常有的界面。右侧可以继续绘制一些窗口这里给出代码:
这个文件名叫做:menuBar.qml
import QtQuick 2.0
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Item {
MenuBar
{
Menu
{
title: qsTr("&File")
Action { text: qsTr("&New...") }
Action { text: qsTr("&Open...") }
Action { text: qsTr("&Save") }
Action { text: qsTr("Save &As...") }
MenuSeparator { }
Action { text: qsTr("&Quit") }
}
Menu
{
title: qsTr("&Edit")
Action { text: qsTr("Cu&t") }
Action { text: qsTr("&Copy") }
Action { text: qsTr("&Paste") }
}
Menu
{
title: qsTr("&Help")
Action { text: qsTr("&About") }
}
}
Text
{
x: 49
y: 49
width: 131
height: 24
text: qsTr("window_1")
font.styleName: "Regular"
font.pixelSize: 18
color: "#000000"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
结合前面的drag_page.qml
,现在就有了上面的两个窗口。如此便可拖动,但是还有是有问题,请继续看下面。
现在来拖动。发现当鼠标在window_2的时候,是可以拖动红色box的,但是当鼠标出了window_2的时候,红色box是不能被拖动的,这是因为鼠标的作用域不对了。
所以,长按住物体,能跨窗口拖动,但是拖回来有bug,这个时候我们可以考虑在这两个窗口的上面增加一个蒙层,在蒙层上面拖动box,如此就能实现丝滑的跨窗口拖动了。
本质:让box和mouseArea都在一个区域,就能丝滑了。
这部分代码就写在main.qml
里面:
import QtQuick 2.15
import QtQuick.Window 2.15
Window
{
title: qsTr("Hello World")
width: 1920
height: 1080
visible: true
Rectangle
{
id:left_control
x:0
y:0
width: 320
height: parent.height
border.color:"blue"
//visible: true
Loader
{
id: loader
width: parent.width
height:parent.height
focus: true
source:"qrc:/menuBar.qml"
}
}
Rectangle
{
id:show_window
x:left_control.width + 5
y:0
width:1000
height: parent.height
color:"#EDF1F2"
border.color:"blue"
Loader
{
width: parent.width
height:parent.height
source: "qrc:/drag_page.qml"
}
}
//就是下面这两个{}
Rectangle
{
id:box
x:200
y:60
width:50
height: 50
color:"#FF0000"
//Drag.active: mouse.drag.active
}
MouseArea
{
id:mask
anchors.fill:parent
drag.target: box
}
}
细心的朋友发现上面还是有问题,这样导致main.qml
的鼠标事件 ,会把下面窗口比如drag_page.qml
的鼠标事件给遮挡住,如此就不妙了。
没有关系,我们可以进行鼠标事件穿透
。
核心代码:
propagateComposedEvents: true
mouse.accepted = false
举例如下
MouseArea
{
id:mask
anchors.fill:parent
drag.target: box
propagateComposedEvents: true
onClicked:
{
mouse.accepted = false
console.log("this is main's mouse clicked")
}
}
main.qml
文件代码:
import QtQuick 2.15
import QtQuick.Window 2.15
//import "menuBar.qml"
Window
{
title: qsTr("Hello World")
width: 1920
height: 1080
visible: true
Rectangle
{
id:left_control
x:0
y:0
width: 320
height: parent.height
border.color:"blue"
//visible: true
Loader
{
id: loader
width: parent.width
height:parent.height
focus: true
source:"qrc:/menuBar.qml"
}
}
Rectangle
{
id:show_window
x:left_control.width + 5
y:0
width:1000
height: parent.height
color:"#EDF1F2"
border.color:"blue"
Loader
{
width: parent.width
height:parent.height
source: "qrc:/drag_page.qml"
}
}
Rectangle
{
id:box
x:200
y:60
width:50
height: 50
color:"#FF0000"
//Drag.active: mouse.drag.active
}
MouseArea
{
id:mask
anchors.fill:parent
drag.target: box
propagateComposedEvents: true
onClicked:
{
mouse.accepted = false
console.log("this is main's mouse clicked")
}
}
}
再深入,可以发现,鼠标穿透事件只能穿透三个函数:只有clicked, doubleClicked and pressAndHold
这三个函数才能穿透。这就很不舒服了,那我的positionChanged怎么办,其他的一些函数怎么办?
其实还是有解决的方法的!!!
使用dropArea
.
我们在一个窗口绘制box,再另一个窗口设置dopArea区域,这样,当这个box进入到这个dropArea区域的时候,就能被响应,从而完成一些事件。
下面这里就是,box
在window_2
里面,而dropArea
在window1
里面,当box进入绿色区域,能引发响应,比如onEntered,onPositionChanged
等等。
核心代码:
下面这段写在drop_page.qml
Rectangle
{
id:box
x:200
y:60
width:50
height: 50
color:"#FF0000"
//不要忘记这行哟,设置active为ID为mouse的鼠标
Drag.active: mouse.drag.active
}
MouseArea
{
id:mouse
//hoverEnabled: true
anchors.fill: parent
drag.target: box
//drag.axis: Drag.XAndYAxis
//这里需要自己调用一些函数,有些函数是自动调用的
onReleased: dropArea_id.Drag.drop()
onClicked:
{
//console.log("this is drag_page's mouse clicked")
}
}
下面这段写在menuBar.qml
里面:
Rectangle
{
id:dropArea_id
border.width: 5
x:50
y:100
z:0
width: 200
height: 300
border.color:"#00FF00"
DropArea{
anchors.fill: parent
onEntered: console.info('enter')
onExited: console.info('exit')
onDropped: console.info('dropped')
onPositionChanged: console.info('positon changed')
}
}
我们可以在蒙层上面绘制一个box,然后在需要控制的窗口去控制它,让他随鼠标移动到window_1,因为我们发现,只要鼠标不放,物体一定可以被跟着走。
因为box是在蒙层上面的,而当前控制物体的鼠标是在window_2的,所以,这里就有一个子窗口鼠标坐标向父窗口鼠标坐标转换的一个过程。
怎么转换呢?借助onPositionChanged函数,知道父子窗口的鼠标偏差,其实就是一个相对位置的问题。子窗口的0,0点是在子窗口的左上角,父窗口一样。鼠标在子窗口的鼠标位置转换到父窗口,就是要加上:子窗口原点的x,y分别距离父窗口原点x,y的距离。
核心代码:
这段写在window_2
窗口,用于控制
MouseArea
{
id:mouse
anchors.fill: parent
//if hoverEnabled: true
//not press can be get mouse position change
hoverEnabled: true
onPositionChanged:
{
//use event_bus_id for sending singal
//you should add the pos *** from children window to father
//it means: event_bus_id.sendMousePos(mouse.x + *** ,mouse.y + *** )
event_bus_id.sendMousePos(mouse.x,mouse.y)
}
}
下面这段写在main.qml
另外EventBus.qml
加上:signal sendMousePos(double x,double y)
// 全局事件通信
EventBus
{
id: event_bus_id
}
Connections
{
target:event_bus_id
function onSendMousePos(x,y)
{
box.x = x
box.y = y
console.log(box.x,box.y)
}
}
以下借鉴:https://blog.csdn.net/ebthon/article/details/84112389
myCursor.h
#include
#include
#include
#include
#include
class MyCursor : public QObject {
Q_OBJECT
public:
MyCursor();
~MyCursor();
Q_INVOKABLE void setMyCursor(QObject *obj,QString source,int width,int height);
};
myCursor.cpp
#include "myCursor.h"
MyCursor::MyCursor(){
}
MyCursor::~MyCursor(){
}
void MyCursor::setMyCursor(QObject *obj_mouse,QString image_source,int width,int height){
if(nullptr==obj_mouse){
return;
}
// 需要将 Qml对象转换为 QQuickItem对象才能 setCursor()
QQuickItem *itemObj = qobject_cast<QQuickItem*>(obj_mouse);
if(nullptr==obj_mouse){
return;
}
//示例
//itemObj->setCursor(QCursor(QPixmap(":///resource/image/simulation_page/car.png")));
itemObj->setCursor(QCursor(QPixmap(image_source).scaled(width, height))); //, Qt::KeepAspectRatio
}
然后在main.cpp
里面导入上面两个文件,加上这句代码:
//myCursor是以后用来调用的名字
engine.rootContext()->setContextProperty("myCursor", new MyCursor());
最后在需要改变mouse样式的mouseArea里面传参就行。
//修改鼠标形状,以及大小
image_source = ":///resource/image/car.png"
myCursor.setMyCursor(mouse_id, image_source,75,75)
还不够,这样的话,跨窗口,鼠标id仍然是蒙层的id,这样一样会被遮挡。最后发现,当我们按下的时候,可以手动设置蒙层的mouseArea的大小区域,使得不被干扰,当释放的时候,又可以设置他的区域,甚至乎设置为0.
以上,喜欢来个赞!!