当 MouseArea 被另一个 MouseArea 覆盖时,如何在 qml 中更改光标形状
How to change cursor shape in qml when MouseArea is covered with another MouseArea
有两个具有两种不同职责的 MouseAreas,一个 (green) 部分位于另一个 (red) 之上。我想在 red MA 悬停时更改光标的形状(即使它在 green MA 下方),我想 绿色 MA 仅对压力做出反应。
两个 MA 可能在不同的文件中,所以我不想在它们之间建立明确的依赖关系,比如在 green 中设置适当的 cursorShape 每当在 red[ 中包含鼠标时=13=]变化。有没有办法阻止 green MouseArea 处理光标形状?
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 200
height: 200
Rectangle { anchors.fill: parent; color: "yellow"; }
MouseArea {
width: 150
height: 150
hoverEnabled: true
cursorShape: Qt.OpenHandCursor
Rectangle { anchors.fill: parent; color: "red"; }
onPositionChanged: console.log("position", mouse.x, mouse.y)
onContainsMouseChanged: console.log("containsMouse", containsMouse)
}
MouseArea {
x: 50
y: 50
width: 150
height: 150
hoverEnabled: false
Rectangle { anchors.fill: parent; color: "green"; }
onPressed: console.log("Ahoj!")
}
}
我不认为这样做是可能的,至少在 QML 中是不可能的。绿色鼠标区域未将 hoverEnabled
设置为 true,因此您不会收到任何位置变化。
解决此问题的更好方法是使用更大的 MouseArea
填充您感兴趣的最大区域,并使用 mapToItem() / mapFromItem() 将全局鼠标位置转换为局部坐标与每个鼠标区域的比较:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: window
visible: true
width: 200
height: 200
Rectangle {
anchors.fill: parent
color: "yellow"
}
MouseArea {
id: globalMouseArea
anchors.fill: parent
hoverEnabled: true
}
MouseArea {
id: redMouseArea
width: 150
height: 150
cursorShape: containsMouse ? Qt.OpenHandCursor : Qt.ArrowCursor
enabled: false
readonly property bool containsMouse: {
var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
return contains(Qt.point(relativePos.x, relativePos.y));
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
Rectangle {
id: greenMouseArea
x: 50
y: 50
width: 150
height: 150
color: containsMouse ? "brown" : "green"
readonly property bool containsMouse: {
var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
return contains(Qt.point(relativePos.x, relativePos.y));
}
Connections {
target: globalMouseArea
onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
}
signal pressed
onPressed: console.log("Ahoj!")
}
}
如您所见,绿色鼠标区域不再是鼠标区域。看起来堆叠顺序高于另一个鼠标区域的鼠标区域将阻止较低鼠标区域的位置更改,即使较高的鼠标区域没有 hoverEnabled
设置为 true。
另外请注意,如果不是 QTBUG-41452,它会稍微简洁一些。即,您可以缩短 containsMouse
表达式:
readonly property bool containsMouse: contains(mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY))
如果您担心这里的代码重复,那么您可以改用一个函数:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: window
visible: true
width: 200
height: 200
Rectangle {
anchors.fill: parent
color: "yellow"
}
MouseArea {
id: globalMouseArea
anchors.fill: parent
hoverEnabled: true
}
function containsMouse(item) {
var relativePos = globalMouseArea.mapToItem(item, globalMouseArea.mouseX, globalMouseArea.mouseY);
return item.contains(Qt.point(relativePos.x, relativePos.y));
}
MouseArea {
id: redMouseArea
width: 150
height: 150
cursorShape: window.containsMouse(redMouseArea) ? Qt.OpenHandCursor : Qt.ArrowCursor
enabled: false
Rectangle {
anchors.fill: parent
color: "red"
}
}
Rectangle {
id: greenMouseArea
x: 50
y: 50
width: 150
height: 150
color: containsMouse ? "brown" : "green"
readonly property bool containsMouse: window.containsMouse(greenMouseArea)
Connections {
target: globalMouseArea
onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
}
signal pressed
onPressed: console.log("Ahoj!")
}
}
我为绿色鼠标区域使用了一个属性,否则它会调用containsMouse()
两次,这是浪费。
MouseArea
属性或任何其他现成的解决方案都无法做到这一点。 MouseArea
总是设置一些光标形状——如果未指定 cursorShape 属性 则使用默认值 (Qt.ArrowCursor
)
您当然可以使用 mapToItem()
/mapFromItem()
来解决此问题(如 Mitch 所建议的)。但也有其他可能性:
您可以临时将覆盖鼠标区域visible
更改为false
。
或者,如果 MouseArea
都是兄弟姐妹,您可以对 z
属性 进行操作以获得适合您需要的特定对象层次结构。
您可以创建自己的 CusorShapeArea,如下所示。它将帮助您更轻松地控制光标形状。
首先创建 C++ class CursorShapeArea:
cursorshapearea.h
#ifndef CURSORSHAPEAREA_H
#define CURSORSHAPEAREA_H
#include <QDeclarativeItem>
class QsltCursorShapeArea : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY cursorShapeChanged)
public:
explicit QsltCursorShapeArea(QDeclarativeItem *parent = 0);
Qt::CursorShape cursorShape() const;
Q_INVOKABLE void setCursorShape(Qt::CursorShape cursorShape);
private:
int m_currentShape;
signals:
void cursorShapeChanged();
};
#endif // CURSORSHAPEAREA_H
cursorshapearea.cpp
#include "cursorshapearea.h"
QsltCursorShapeArea::QsltCursorShapeArea(QDeclarativeItem *parent) :
QDeclarativeItem(parent),
m_currentShape(-1)
{
}
Qt::CursorShape QsltCursorShapeArea::cursorShape() const
{
return cursor().shape();
}
void QsltCursorShapeArea::setCursorShape(Qt::CursorShape cursorShape)
{
if (m_currentShape == (int) cursorShape)
return;
setCursor(cursorShape);
emit cursorShapeChanged();
m_currentShape = cursorShape;
}
然后注册这个qml类型:
#include <QtDeclarative>
#include "cursorshapearea.h"
int main(int argc, char **argv)
{
...
qmlRegisterType<CursorShapeArea>("MyTools", 1, 0, "CursorShapeArea");
...
}
然后在 QML 中使用它,在您的问题中:
import QtQuick 1.1
import CursorShapeArea 0.1
Window {
visible: true
width: 200
height: 200
Rectangle { anchors.fill: parent; color: "yellow"; }
MouseArea {
id: redArea
width: 150
height: 150
hoverEnabled: true
//cursorShape: Qt.OpenHandCursor
Rectangle { anchors.fill: parent; color: "red"; }
onPositionChanged: console.log("position", mouse.x, mouse.y)
onContainsMouseChanged: console.log("containsMouse", containsMouse)
}
MouseArea {
x: 50
y: 50
width: 150
height: 150
hoverEnabled: false
Rectangle { anchors.fill: parent; color: "green"; }
onPressed: console.log("Ahoj!")
}
CursorShapeArea {
anchors.fill: redArea
cursorShape: Qt.OpenHandCursor
}
}
您可以将绿色鼠标区域的cursorShape
设置为undefined
,这样可以防止它改变光标。
有两个具有两种不同职责的 MouseAreas,一个 (green) 部分位于另一个 (red) 之上。我想在 red MA 悬停时更改光标的形状(即使它在 green MA 下方),我想 绿色 MA 仅对压力做出反应。
两个 MA 可能在不同的文件中,所以我不想在它们之间建立明确的依赖关系,比如在 green 中设置适当的 cursorShape 每当在 red[ 中包含鼠标时=13=]变化。有没有办法阻止 green MouseArea 处理光标形状?
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 200
height: 200
Rectangle { anchors.fill: parent; color: "yellow"; }
MouseArea {
width: 150
height: 150
hoverEnabled: true
cursorShape: Qt.OpenHandCursor
Rectangle { anchors.fill: parent; color: "red"; }
onPositionChanged: console.log("position", mouse.x, mouse.y)
onContainsMouseChanged: console.log("containsMouse", containsMouse)
}
MouseArea {
x: 50
y: 50
width: 150
height: 150
hoverEnabled: false
Rectangle { anchors.fill: parent; color: "green"; }
onPressed: console.log("Ahoj!")
}
}
我不认为这样做是可能的,至少在 QML 中是不可能的。绿色鼠标区域未将 hoverEnabled
设置为 true,因此您不会收到任何位置变化。
解决此问题的更好方法是使用更大的 MouseArea
填充您感兴趣的最大区域,并使用 mapToItem() / mapFromItem() 将全局鼠标位置转换为局部坐标与每个鼠标区域的比较:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: window
visible: true
width: 200
height: 200
Rectangle {
anchors.fill: parent
color: "yellow"
}
MouseArea {
id: globalMouseArea
anchors.fill: parent
hoverEnabled: true
}
MouseArea {
id: redMouseArea
width: 150
height: 150
cursorShape: containsMouse ? Qt.OpenHandCursor : Qt.ArrowCursor
enabled: false
readonly property bool containsMouse: {
var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
return contains(Qt.point(relativePos.x, relativePos.y));
}
Rectangle {
anchors.fill: parent
color: "red"
}
}
Rectangle {
id: greenMouseArea
x: 50
y: 50
width: 150
height: 150
color: containsMouse ? "brown" : "green"
readonly property bool containsMouse: {
var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
return contains(Qt.point(relativePos.x, relativePos.y));
}
Connections {
target: globalMouseArea
onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
}
signal pressed
onPressed: console.log("Ahoj!")
}
}
如您所见,绿色鼠标区域不再是鼠标区域。看起来堆叠顺序高于另一个鼠标区域的鼠标区域将阻止较低鼠标区域的位置更改,即使较高的鼠标区域没有 hoverEnabled
设置为 true。
另外请注意,如果不是 QTBUG-41452,它会稍微简洁一些。即,您可以缩短 containsMouse
表达式:
readonly property bool containsMouse: contains(mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY))
如果您担心这里的代码重复,那么您可以改用一个函数:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: window
visible: true
width: 200
height: 200
Rectangle {
anchors.fill: parent
color: "yellow"
}
MouseArea {
id: globalMouseArea
anchors.fill: parent
hoverEnabled: true
}
function containsMouse(item) {
var relativePos = globalMouseArea.mapToItem(item, globalMouseArea.mouseX, globalMouseArea.mouseY);
return item.contains(Qt.point(relativePos.x, relativePos.y));
}
MouseArea {
id: redMouseArea
width: 150
height: 150
cursorShape: window.containsMouse(redMouseArea) ? Qt.OpenHandCursor : Qt.ArrowCursor
enabled: false
Rectangle {
anchors.fill: parent
color: "red"
}
}
Rectangle {
id: greenMouseArea
x: 50
y: 50
width: 150
height: 150
color: containsMouse ? "brown" : "green"
readonly property bool containsMouse: window.containsMouse(greenMouseArea)
Connections {
target: globalMouseArea
onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
}
signal pressed
onPressed: console.log("Ahoj!")
}
}
我为绿色鼠标区域使用了一个属性,否则它会调用containsMouse()
两次,这是浪费。
MouseArea
属性或任何其他现成的解决方案都无法做到这一点。 MouseArea
总是设置一些光标形状——如果未指定 cursorShape 属性 则使用默认值 (Qt.ArrowCursor
)
您当然可以使用 mapToItem()
/mapFromItem()
来解决此问题(如 Mitch 所建议的)。但也有其他可能性:
您可以临时将覆盖鼠标区域visible
更改为false
。
或者,如果 MouseArea
都是兄弟姐妹,您可以对 z
属性 进行操作以获得适合您需要的特定对象层次结构。
您可以创建自己的 CusorShapeArea,如下所示。它将帮助您更轻松地控制光标形状。
首先创建 C++ class CursorShapeArea:
cursorshapearea.h
#ifndef CURSORSHAPEAREA_H
#define CURSORSHAPEAREA_H
#include <QDeclarativeItem>
class QsltCursorShapeArea : public QDeclarativeItem
{
Q_OBJECT
Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY cursorShapeChanged)
public:
explicit QsltCursorShapeArea(QDeclarativeItem *parent = 0);
Qt::CursorShape cursorShape() const;
Q_INVOKABLE void setCursorShape(Qt::CursorShape cursorShape);
private:
int m_currentShape;
signals:
void cursorShapeChanged();
};
#endif // CURSORSHAPEAREA_H
cursorshapearea.cpp
#include "cursorshapearea.h"
QsltCursorShapeArea::QsltCursorShapeArea(QDeclarativeItem *parent) :
QDeclarativeItem(parent),
m_currentShape(-1)
{
}
Qt::CursorShape QsltCursorShapeArea::cursorShape() const
{
return cursor().shape();
}
void QsltCursorShapeArea::setCursorShape(Qt::CursorShape cursorShape)
{
if (m_currentShape == (int) cursorShape)
return;
setCursor(cursorShape);
emit cursorShapeChanged();
m_currentShape = cursorShape;
}
然后注册这个qml类型:
#include <QtDeclarative>
#include "cursorshapearea.h"
int main(int argc, char **argv)
{
...
qmlRegisterType<CursorShapeArea>("MyTools", 1, 0, "CursorShapeArea");
...
}
然后在 QML 中使用它,在您的问题中:
import QtQuick 1.1
import CursorShapeArea 0.1
Window {
visible: true
width: 200
height: 200
Rectangle { anchors.fill: parent; color: "yellow"; }
MouseArea {
id: redArea
width: 150
height: 150
hoverEnabled: true
//cursorShape: Qt.OpenHandCursor
Rectangle { anchors.fill: parent; color: "red"; }
onPositionChanged: console.log("position", mouse.x, mouse.y)
onContainsMouseChanged: console.log("containsMouse", containsMouse)
}
MouseArea {
x: 50
y: 50
width: 150
height: 150
hoverEnabled: false
Rectangle { anchors.fill: parent; color: "green"; }
onPressed: console.log("Ahoj!")
}
CursorShapeArea {
anchors.fill: redArea
cursorShape: Qt.OpenHandCursor
}
}
您可以将绿色鼠标区域的cursorShape
设置为undefined
,这样可以防止它改变光标。