如何在QML中创建圆形鼠标区域

11

我有一个基本的自定义按钮,使用一个带有radius: width/2的矩形。现在我给按钮添加了一个MouseArea。但是MouseArea是一个正方形形状。这意味着当我点击圆形按钮周围想象的正方形的角落时,也会触发点击事件。我怎么样才能让MouseArea也变成圆形?

  import QtQuick 2.7
  import QtQuick.Window 2.2

  Window {
      visible: true
      width: 640
      height: 480
      title: qsTr("TestApp")

      Rectangle {
          id: background
          anchors.fill: parent
          color: Qt.rgba(0.25, 0.25, 0.25, 1);


          Rectangle {
              id: button
              width: 64
              height: 64
              color: "transparent"
              anchors.centerIn: parent
              radius: 32
              border.width: 4
              border.color: "grey"

              MouseArea {
                  anchors.fill: parent
                  onPressed: button.color = "red";
                  onReleased: button.color = "transparent";
              }
          }

      }
  }

如果将 clip: true 设置为 button,它会起作用吗?现在无法测试。 - folibis
@folibis: 很遗憾,没有。我已经在我的按钮上添加了clip: true,但问题仍然没有改变。谢谢。 - Varius
3个回答

27

PieMenu中窃取代码,这里是RoundMouseArea.qml

import QtQuick 2.0

Item {
    id: roundMouseArea

    property alias mouseX: mouseArea.mouseX
    property alias mouseY: mouseArea.mouseY

    property bool containsMouse: {
        var x1 = width / 2;
        var y1 = height / 2;
        var x2 = mouseX;
        var y2 = mouseY;
        var distanceFromCenter = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
        var radiusSquared = Math.pow(Math.min(width, height) / 2, 2);
        var isWithinOurRadius = distanceFromCenter < radiusSquared;
        return isWithinOurRadius;
    }

    readonly property bool pressed: containsMouse && mouseArea.pressed

    signal clicked

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        hoverEnabled: true
        acceptedButtons: Qt.LeftButton | Qt.RightButton
        onClicked: if (roundMouseArea.containsMouse) roundMouseArea.clicked()
    }
}
你可以像这样使用它:
import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    width: 640
    height: 480
    visible: true

    RoundMouseArea {
        id: roundMouseArea
        width: 100
        height: 100
        anchors.centerIn: parent

        onClicked: print("clicked")

        // Show the boundary of the area and whether or not it's hovered.
        Rectangle {
            color: roundMouseArea.pressed ? "red" : (roundMouseArea.containsMouse ? "darkorange" : "transparent")
            border.color: "darkorange"
            radius: width / 2
            anchors.fill: parent
        }
    }
}

圆形鼠标区域动图


为了一个能够适当地处理拖动的版本,我可以付出我的王国。 - Sparr
https://dev59.com/ILDla4cB1Zd3GeqP-JEI - Sparr

8
另一种选项是使用C++/QML方式,就像这个示例中描述的那样。该示例提供了一种使用任何形状的遮罩的方法。它可以根据您的需求进行定制。
以下是代码: maskedmousearea.cpp
MaskedMouseArea::MaskedMouseArea(QQuickItem *parent)
    : QQuickItem(parent),
      m_pressed(false),
      m_alphaThreshold(0.0),
      m_containsMouse(false)
{
    setAcceptHoverEvents(true);
    setAcceptedMouseButtons(Qt::LeftButton);
}

void MaskedMouseArea::setPressed(bool pressed)
{
    if (m_pressed != pressed) {
    m_pressed = pressed;
    emit pressedChanged();
    }
}

void MaskedMouseArea::setContainsMouse(bool containsMouse)
{
    if (m_containsMouse != containsMouse) {
    m_containsMouse = containsMouse;
    emit containsMouseChanged();
    }
}

void MaskedMouseArea::setMaskSource(const QUrl &source)
{
    if (m_maskSource != source) {
    m_maskSource = source;
    m_maskImage = QImage(QQmlFile::urlToLocalFileOrQrc(source));
    emit maskSourceChanged();
    }
}

void MaskedMouseArea::setAlphaThreshold(qreal threshold)
{
    if (m_alphaThreshold != threshold) {
    m_alphaThreshold = threshold;
    emit alphaThresholdChanged();
    }
}

bool MaskedMouseArea::contains(const QPointF &point) const
{
    if (!QQuickItem::contains(point) || m_maskImage.isNull())
    return false;

    QPoint p = point.toPoint();

    if (p.x() < 0 || p.x() >= m_maskImage.width() ||
    p.y() < 0 || p.y() >= m_maskImage.height())
    return false;

    qreal r = qBound<int>(0, m_alphaThreshold * 255, 255);
    return qAlpha(m_maskImage.pixel(p)) > r;
}

void MaskedMouseArea::mousePressEvent(QMouseEvent *event)
{
    setPressed(true);
    m_pressPoint = event->pos();
    emit pressed();
}

void MaskedMouseArea::mouseReleaseEvent(QMouseEvent *event)
{
    setPressed(false);
    emit released();

    const int threshold = qApp->styleHints()->startDragDistance();
    const bool isClick = (threshold >= qAbs(event->x() - m_pressPoint.x()) &&
                      threshold >= qAbs(event->y() - m_pressPoint.y()));

    if (isClick)
    emit clicked();
}

void MaskedMouseArea::mouseUngrabEvent()
{
    setPressed(false);
    emit canceled();
}

void MaskedMouseArea::hoverEnterEvent(QHoverEvent *event)
{
    Q_UNUSED(event);
    setContainsMouse(true);
}

void MaskedMouseArea::hoverLeaveEvent(QHoverEvent *event)
{
    Q_UNUSED(event);
    setContainsMouse(false);
}

在 QML 中的使用:
import Example 1.0
MaskedMouseArea {
    id: moonArea
    anchors.fill: parent
    alphaThreshold: 0.4
    maskSource: moon.source
}

注册自定义项:

qmlRegisterType<MaskedMouseArea>("Example", 1, 0, "MaskedMouseArea");

(注:该段文字为代码,无法直接翻译)

0
感谢 @Mitch。有时候这样的鼠标区域在离开后仍会说它包含鼠标,所以我在“containsMouse”属性的开头添加了“if(!mouseArea.containsMouse) return false;”。
property bool containsMouse: {
    if(!mouseArea.containsMouse)
        return false;

    var x1 = width / 2;
    var y1 = height / 2;
    var x2 = mouseX;
    var y2 = mouseY;
    var distanceFromCenter = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
    var radiusSquared = Math.pow(Math.min(width, height) / 2, 2);
    var isWithinOurRadius = distanceFromCenter < radiusSquared;
    return isWithinOurRadius;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接