четверг, 31 мая 2012 г.

QML Line element своими руками

К моему огромному огорчению в QML не оказалось линии для рисования. Ну да не беда, нарисуем своими руками :)
Итак, все описание сделаем в одном заголовочном файле, дабы не засорять проект. Описание линии:

#ifndef LINE_H
#define LINE_H
#include <QDeclarativeItem>
#include <QPainter>
class Line : public QDeclarativeItem
{
    Q_OBJECT
    Q_PROPERTY(int x1 READ x1 WRITE setX1 NOTIFY x1Changed)
    Q_PROPERTY(int y1 READ y1 WRITE setY1 NOTIFY y1Changed)
    Q_PROPERTY(int x2 READ x2 WRITE setX2 NOTIFY x2Changed)
    Q_PROPERTY(int y2 READ y2 WRITE setY2 NOTIFY y2Changed)
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    Q_PROPERTY(int penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
public:
    Line(QDeclarativeItem *parent = 0) :
            QDeclarativeItem(parent), m_x1(0), m_y1(0), m_x2(0), m_y2(0),
            m_color(Qt::black), m_penWidth(1)
    {
        // Important, otherwise the paint method is never called
        setFlag(QGraphicsItem::ItemHasNoContents, false);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        QPen pen(m_color, m_penWidth);
        painter->setPen(pen);
        if(smooth() == true) {
            painter->setRenderHint(QPainter::Antialiasing, true);
        }
        int x = qMin(m_x1, m_x2) - m_penWidth/2;
        int y = qMin(m_y1, m_y2) - m_penWidth/2;
        painter->drawLine(m_x1 - x, m_y1 - y, m_x2 - x, m_y2 - y);
    }
    // Get methods
    int x1() const { return m_x1; }
    int y1() const { return m_y1; }
    int x2() const { return m_x2; }
    int y2() const { return m_y2; }
    QColor color() const { return m_color; }
    int penWidth() const { return m_penWidth; }
    // Set methods
    void setX1(int x1) {
        if(m_x1 == x1) return;
        m_x1 = x1;
        updateSize();
        emit x1Changed();
        update();
    }
    void setY1(int y1) {
        if(m_y1 == y1) return;
        m_y1 = y1;
        updateSize();
        emit y1Changed();
        update();
    }
    void setX2(int x2) {
        if(m_x2 == x2) return;
        m_x2 = x2;
        updateSize();
        emit x2Changed();
        update();
    }
    void setY2(int y2) {
        if(m_y2 == y2) return;
        m_y2 = y2;
        updateSize();
        emit x2Changed();
        update();
    }
    void setColor(const QColor &color) {
        if(m_color == color) return;
        m_color = color;
        emit colorChanged();
        update();
    }
    void setPenWidth(int newWidth) {
        if(m_penWidth == newWidth) return;
        m_penWidth = newWidth;
        updateSize();
        emit penWidthChanged();
        update();
    }
signals:
    void x1Changed();
    void y1Changed();
    void x2Changed();
    void y2Changed();
    void colorChanged();
    void penWidthChanged();
protected:
    void updateSize() {
        setX(qMin(m_x1, m_x2) - m_penWidth/2);
        setY(qMin(m_y1, m_y2) - m_penWidth/2);
        setWidth(qAbs(m_x2 - m_x1) + m_penWidth);
        setHeight(qAbs(m_y2 - m_y1) + m_penWidth);
    }
protected:
    int m_x1;
    int m_y1;
    int m_x2;
    int m_y2;
    QColor m_color;
    int m_penWidth;
};
QML_DECLARE_TYPE(Line)
#endif // LINE_H
#ifndef LINE_H
#define LINE_H
#include <QDeclarativeItem>
#include <QPainter>
class Line : public QDeclarativeItem
{
    Q_OBJECT
    Q_PROPERTY(int x1 READ x1 WRITE setX1 NOTIFY x1Changed)
    Q_PROPERTY(int y1 READ y1 WRITE setY1 NOTIFY y1Changed)
    Q_PROPERTY(int x2 READ x2 WRITE setX2 NOTIFY x2Changed)
    Q_PROPERTY(int y2 READ y2 WRITE setY2 NOTIFY y2Changed)
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    Q_PROPERTY(int penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
public:
    Line(QDeclarativeItem *parent = 0) :
            QDeclarativeItem(parent), m_x1(0), m_y1(0), m_x2(0), m_y2(0),
            m_color(Qt::black), m_penWidth(1)
    {
        // Important, otherwise the paint method is never called
        setFlag(QGraphicsItem::ItemHasNoContents, false);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
    {
        QPen pen(m_color, m_penWidth);
        painter->setPen(pen);
        if(smooth() == true) {
            painter->setRenderHint(QPainter::Antialiasing, true);
        }
        int x = qMin(m_x1, m_x2) - m_penWidth/2;
        int y = qMin(m_y1, m_y2) - m_penWidth/2;
        painter->drawLine(m_x1 - x, m_y1 - y, m_x2 - x, m_y2 - y);
    }
    // Get methods
    int x1() const { return m_x1; }
    int y1() const { return m_y1; }
    int x2() const { return m_x2; }
    int y2() const { return m_y2; }
    QColor color() const { return m_color; }
    int penWidth() const { return m_penWidth; }
    // Set methods
    void setX1(int x1) {
        if(m_x1 == x1) return;
        m_x1 = x1;
        updateSize();
        emit x1Changed();
        update();
    }
    void setY1(int y1) {
        if(m_y1 == y1) return;
        m_y1 = y1;
        updateSize();
        emit y1Changed();
        update();
    }
    void setX2(int x2) {
        if(m_x2 == x2) return;
        m_x2 = x2;
        updateSize();
        emit x2Changed();
        update();
    }
    void setY2(int y2) {
        if(m_y2 == y2) return;
        m_y2 = y2;
        updateSize();
        emit x2Changed();
        update();
    }
    void setColor(const QColor &color) {
        if(m_color == color) return;
        m_color = color;
        emit colorChanged();
        update();
    }
    void setPenWidth(int newWidth) {
        if(m_penWidth == newWidth) return;
        m_penWidth = newWidth;
        updateSize();
        emit penWidthChanged();
        update();
    }
signals:
    void x1Changed();
    void y1Changed();
    void x2Changed();
    void y2Changed();
    void colorChanged();
    void penWidthChanged();
protected:
    void updateSize() {
        setX(qMin(m_x1, m_x2) - m_penWidth/2);
        setY(qMin(m_y1, m_y2) - m_penWidth/2);
        setWidth(qAbs(m_x2 - m_x1) + m_penWidth);
        setHeight(qAbs(m_y2 - m_y1) + m_penWidth);
    }
protected:
    int m_x1;
    int m_y1;
    int m_x2;
    int m_y2;
    QColor m_color;
    int m_penWidth;
};
QML_DECLARE_TYPE(Line)
#endif // LINE_H


Всё это размещаете в файле line.h, который добавляете в проект.

В файле проекта добавляем:

QT += declarative
Вместе с добавленными библиотеками и нашей линией, main.cpp станет выглядеть так:

#include <QtGui/QApplication>
#include <QColor>
#include <QDeclarativeView>
#include "qmlapplicationviewer.h"
#include "line.h"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(createApplication(argc, argv));
    qmlRegisterType<Line>("CustomComponents", 1, 0, "Line");
    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/KnowledgeWeb/main.qml"));
    viewer.showExpanded();
    return app->exec();
}
Ну и на последок, остаётся дело за малым - нарисовать какую-нибудь линию.
В начало QML файла подключаем нашу компоненту:
import CustomComponents 1.0

Ну и рисуем где-нибудь линию:
        Line
    {
        x1: 0
        y1: 0
        x2: 100
        y2: 100
        penWidth: 2
        color: "darkRed"
    }
Вуаля :)

8 комментариев:

  1. // немного обновил код к версии QtQuick 2.x
    // автору все равно спасибо за код, он был опорой, может кому будет еще полезен так что вот:)
    #ifndef QMLLINE_H
    #define QMLLINE_H

    #include
    #include
    #include
    #include

    class QmlLine : public QQuickPaintedItem
    {
    Q_OBJECT
    Q_DISABLE_COPY(QmlLine)

    Q_PROPERTY(int x1 READ x1 WRITE setX1 NOTIFY x1Changed)
    Q_PROPERTY(int y1 READ y1 WRITE setY1 NOTIFY y1Changed)
    Q_PROPERTY(int x2 READ x2 WRITE setX2 NOTIFY x2Changed)
    Q_PROPERTY(int y2 READ y2 WRITE setY2 NOTIFY y2Changed)
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    Q_PROPERTY(int penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)

    public:
    QmlLine() : m_x1(0), m_y1(0), m_x2(0), m_y2(0),
    m_color(Qt::black), m_penWidth(1)
    {
    // Important, otherwise the paint method is never called
    this->setFlag(ItemHasContents, true);
    }

    void paint(QPainter *painter)
    {
    QPen pen(m_color, m_penWidth);
    painter->setPen(pen);
    if(smooth() == true) {
    painter->setRenderHint(QPainter::Antialiasing, true);
    }
    int x = qMin(m_x1, m_x2) - m_penWidth/2;
    int y = qMin(m_y1, m_y2) - m_penWidth/2;
    painter->drawLine(m_x1 - x, m_y1 - y, m_x2 - x, m_y2 - y);
    }

    // Get methods
    int x1() const { return m_x1; }
    int y1() const { return m_y1; }
    int x2() const { return m_x2; }
    int y2() const { return m_y2; }
    QColor color() const { return m_color; }
    int penWidth() const { return m_penWidth; }

    // Set methods
    void setX1(int x1) {
    if(m_x1 == x1) return;
    m_x1 = x1;
    updateSize();
    emit x1Changed();
    update();
    }
    void setY1(int y1) {
    if(m_y1 == y1) return;
    m_y1 = y1;
    updateSize();
    emit y1Changed();
    update();
    }
    void setX2(int x2) {
    if(m_x2 == x2) return;
    m_x2 = x2;
    updateSize();
    emit x2Changed();
    update();
    }
    void setY2(int y2) {
    if(m_y2 == y2) return;
    m_y2 = y2;
    updateSize();
    emit x2Changed();
    update();
    }
    void setColor(const QColor &color) {
    if(m_color == color) return;
    m_color = color;
    emit colorChanged();
    update();
    }
    void setPenWidth(int newWidth) {
    if(m_penWidth == newWidth) return;
    m_penWidth = newWidth;
    updateSize();
    emit penWidthChanged();
    update();
    }

    signals:
    void x1Changed();
    void y1Changed();
    void x2Changed();
    void y2Changed();
    void colorChanged();
    void penWidthChanged();

    protected:
    void updateSize() {
    setX(qMin(m_x1, m_x2) - m_penWidth/2);
    setY(qMin(m_y1, m_y2) - m_penWidth/2);
    setWidth(qAbs(m_x2 - m_x1) + m_penWidth);
    setHeight(qAbs(m_y2 - m_y1) + m_penWidth);
    }

    protected:
    int m_x1;
    int m_y1;
    int m_x2;
    int m_y2;
    QColor m_color;
    int m_penWidth;

    public:
    static void declare() {
    qmlRegisterType("LineComponent", 1, 0, "Line");
    }

    };
    #endif // QMLLINE_H

    ОтветитьУдалить
  2. опа! тут в коментариях теги запрещены поэтому в инклюдах следующее:
    #include "QObject"
    #include "QtQml"
    #include "QPainter"
    #include "QQuickPaintedItem"
    потом внизу:
    qmlRegisterType[QmlLine]("LineComponent", 1, 0, "Line");
    где скобки [] заменить на контейнерные

    ОтветитьУдалить
  3. а в main.cpp
    просто:
    //...<-тут все инклюды
    int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    QmlLine::declare(); //<- первый способ
    //qmlRegisterType[QmlLine]("LineComponent", 1, 0, "Line"); //<- второй способ
    // ...
    engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
    return app.exec();
    }

    ОтветитьУдалить
  4. и последний фикс в классе, там не учитывается то что края при большой толщине линии будут срезаться, поэтому такой фикс нужно сделать:
    void paint(QPainter *painter)
    {
    QPen pen(m_color, m_penWidth);
    painter->setPen(pen);
    if(smooth() == true) {
    painter->setRenderHint(QPainter::Antialiasing, true);
    }
    int x = qMin(m_x1, m_x2) - m_penWidth/2;
    int y = qMin(m_y1, m_y2) - m_penWidth/2;
    float A = m_penWidth/2.0; // <--
    float delta = A*qSin(45); // <--
    painter->drawLine(m_x1 - x + delta, m_y1 - y + delta, m_x2 - x - delta, m_y2 - y - delta); // <--
    }

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