Qt 5.3 QML: создание линий без использования Canvas

В QtQuick практически отсутствуют элементы, представляющие собой геометрические фигуры (есть только Rectangle). Видимо, предполагается, что если требуется рисовать что-то хоть сколько-то сложное, нужно использовать элемент Canvas. Однако, в случае интерактивного интерфейса Canvas не очень удобен - чтобы обработать, например, нажатие клавиши мышки по элементу на Canvas, нужно получить координаты указателя мыши, понять, какой элемент расположен по этим координатам, понять, какие еще элементы затрагиваются этим событием, и перестроить Canvas. В случае, если интерфейс построен из отдельных элементов (типа Item или Rectangle), каждый элемент может сам обрабатывать свои события, а связывать элементы между собой можно через сигналы-слоты. Получается гораздо удобнее.
Итак, я не хочу использовать Canvas, но мне необходимо нарисовать линию. Так как у нас есть только Rectangle, то будем использовать именно его: ведь линия - это тот же прямоугольник, просто с очень маленькой высотой (например, один пиксель), повернутый на определенный угол.

Представленный ниже код QML я позаимствовал отсюда.
import QtQuick 2.0

Rectangle {
    id: l
    property real x1: 0
    property real y1: 0

    property real x2: 0
    property real y2: 0

    color: "black"
    height: 1
    smooth: true;

    x: x1
    y: y1 - height / 2

    transformOrigin: Item.Left;

    width: getWidth(x1, y1, x2, y2);
    rotation: getSlope(x1, y1, x2, y2);

    function getWidth(sx1, sy1, sx2, sy2)
    {
        var w = Math.sqrt(Math.pow((sx2 - sx1), 2) + Math.pow((sy2 - sy1), 2));
        return w;
    }

    function getSlope(sx1,sy1,sx2,sy2)
    {
        var a, m, d;
        var b = sx2 - sx1;
        if (b === 0) {
            x = x + height
            return 90;
        }
        a = sy2 - sy1;
        m = a / b;
        d = Math.atan(m) * 180 / Math.PI;

        if (a < 0 && b < 0)
            return d + 180;
        else if (a >= 0 && b >= 0)
            return d;
        else if (a < 0 && b >= 0)
            return d;
        else if (a >= 0 && b < 0)
            return d + 180;
        else
            return 0;
    }
}

Видно, что линия задается через координаты двух точек - начала и конца линии. Причем координаты начала совпадают с координатами элемента Rectangle. Центром вращения назначается левый верхний угол, итоговая ширина элемента Rectangle и угол поворота рассчитываются исходя из координат начала и конца линии.

1 комментарий :

  1. У вас ошибка. Вы нарушаете принцип единственной ответственности изменяя свойство x из привязки свойства rotation. Это приводит к Binding loop warning и к другим неприятным моментам. Так же return 90 - не учитывает направления вдоль оси OY.

    if (b === 0) {
    x = x + height
    return 90;
    }

    Замените на:

    if (b === 0)
    return Math.sign(sy2 - sy1) * 90

    ОтветитьУдалить