QML -Objekte in Qt sind ziemlich cool und einfach zu handhaben, aber was ist, wenn uns Standardobjekte nicht ausreichen? Dann können Sie Ihr eigenes Objekt erstellen, es in C++ programmieren und in die QML -Logik der Schicht einbetten. In diesem Tutorial schlage ich vor, einen kleinen improvisierten Timer zu erstellen, der gestartet, gestoppt und gelöscht werden kann, aber das Design des Timers wird in der C ++ - Schicht entwickelt und tatsächlich wird die meiste Arbeit in C ++ erledigt .
Und um ein benutzerdefiniertes QuickItem zu entwickeln, müssen Sie QQuickPaintedItem verwenden, in dem es einen Timer gibt, der in der Abbildung unten gezeigt wird und wie ein normales QGraphicsItem , aber es wird eine Reihe von Eigenschaften haben, die von der QML-Schicht gesteuert werden können.
Benutzerdefinierte QQuickItem-Projektstruktur
- CustomQuickItem.pro - Projektprofil;
- deployment.pri - Projektbereitstellungsprofil für verschiedene Architekturen;
- clockcircle.h - Header-Datei des Projekt-Timers selbst;
- clockcircle.cpp - Projekt-Timer-Quellcodedatei;
- main.cpp - Quellcodedatei der Hauptfunktion des Projekts;
- main.qml - qml-Quellcodedatei.
CustomQuickItem.pro
Um benutzerdefinierte QQuickItem-Klassen in der QML-Schicht zu registrieren, müssen Sie das quickwidgets-Modul verbinden, wie es in dieser Datei geschieht.
TEMPLATE = app QT += qml quick quickwidgets CONFIG += c++11 SOURCES += main.cpp \ clockcircle.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Default rules for deployment. include(deployment.pri) HEADERS += \ clockcircle.h
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickWidget> #include "clockcircle.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // Всё, что требуется в данном файле - это зарегистрировать новый класс (Тип объекта) для QML слоя qmlRegisterType<ClockCircle>("ClockCircle",1,0,"ClockCircle"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
clockCircle.h
Da dieses Objekt ein Objekt ist, das von der QML-Schicht verfügbar sein muss, ist es notwendig, alle seine Eigenschaften als Q_PROPERTY zu definieren, in dem alle Setter, Getter und dementsprechend Signale zum Ändern dieser Eigenschaften angezeigt werden. Darüber hinaus verfügt die Klasse über mehrere Q_INVOKABLE -Methoden, die auch von der QML-Schicht verfügbar sein werden. Dies sind clear(), start(), stop(), sie steuern Timer von der Schnittstelle aus.
Der Timer hat mehrere Eigenschaften:
- m_name - Objektname;
- m_backgroundColor - Hintergrundfarbe des Timers;
- m_borderNonActiveColor - Hintergrundfarbe des Rahmens (kreisförmiger Fortschrittsbalken), im ungefüllten Zustand;
- m_borderActiveColor - Hintergrundfarbe des Rahmens, Füllung des Fortschrittsbalkens;
- m_angle - Rotationswinkel des aktiven Teils des Fortschrittsbalkens;
- m_circleTime - aktuelle Zeit des Timers.
#ifndef CLOCKCIRCLE_H #define CLOCKCIRCLE_H #include <QtQuick/QQuickPaintedItem> #include <QColor> #include <QBrush> #include <QPen> #include <QPainter> #include <QTime> #include <QTimer> class ClockCircle : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QColor borderActiveColor READ borderActiveColor WRITE setBorderActiveColor NOTIFY borderActiveColorChanged) Q_PROPERTY(QColor borderNonActiveColor READ borderNonActiveColor WRITE setBorderNonActiveColor NOTIFY borderNonActiveColorChanged) Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged) Q_PROPERTY(QTime circleTime READ circleTime WRITE setCircleTime NOTIFY circleTimeChanged) public: explicit ClockCircle(QQuickItem *parent = 0); void paint(QPainter *painter) override; // Переопределяем метод, в котором будет отрисовываться наш объект // Методы, доступные из QML для ... Q_INVOKABLE void clear(); // ... очистки времени, ... Q_INVOKABLE void start(); // ... запуска таймера, ... Q_INVOKABLE void stop(); // ... остановки таймера, ... QString name() const; QColor backgroundColor() const; QColor borderActiveColor() const; QColor borderNonActiveColor() const; qreal angle() const; QTime circleTime() const; public slots: void setName(const QString name); void setBackgroundColor(const QColor backgroundColor); void setBorderActiveColor(const QColor borderActiveColor); void setBorderNonActiveColor(const QColor borderNonActiveColor); void setAngle(const qreal angle); void setCircleTime(const QTime circleTime); signals: void cleared(); void nameChanged(const QString name); void backgroundColorChanged(const QColor backgroundColor); void borderActiveColorChanged(const QColor borderActiveColor); void borderNonActiveColorChanged(const QColor borderNonActiveColor); void angleChanged(const qreal angle); void circleTimeChanged(const QTime circleTime); private: QString m_name; // Название объекта, по большей части до кучи добавлено QColor m_backgroundColor; // Основной цвет фона QColor m_borderActiveColor; // Цвет ободка, заполняющий при прогрессе ободок таймера QColor m_borderNonActiveColor; // Цвет ободка фоновый qreal m_angle; // Угол поворота графика типа пирог, будет формировать прогресс на ободке QTime m_circleTime; // Текущее время таймера QTimer *internalTimer; // Таймер, по которому будет изменяться время }; #endif // CLOCKCIRCLE_H
clockcircle.cpp
Jeglicher Code, der sich auf Property Setter und Getter bezieht, kann automatisch über das Kontextmenü generiert werden, indem Sie mit der rechten Maustaste auf das geschriebene Q_PROPERTY klicken. Achten Sie nur auf setAngle(const qreal angle) , da es geändert wird, um die Drehung zurückzusetzen Winkel auf Null.
#include "clockcircle.h" ClockCircle::ClockCircle(QQuickItem *parent) : QQuickPaintedItem(parent), m_backgroundColor(Qt::white), m_borderActiveColor(Qt::blue), m_borderNonActiveColor(Qt::gray), m_angle(0), m_circleTime(QTime(0,0,0,0)) { internalTimer = new QTimer(this); // Инициализируем таймер /* А также подключаем сигнал от таймера к лямбда функции * Структура лямбда-функции [объект](аргументы){тело} * */ connect(internalTimer, &QTimer::timeout, [=](){ setAngle(angle() - 0.3); // поворот определяется в градусах. setCircleTime(circleTime().addMSecs(50)); // Добавляем к текущему времени 50 милисекунд update(); // Перерисовываем объект }); } void ClockCircle::paint(QPainter *painter) { // Отрисовка объекта QBrush brush(m_backgroundColor); // выбираем цвет фона, ... QBrush brushActive(m_borderActiveColor); // активный цвет ободка, ... QBrush brushNonActive(m_borderNonActiveColor); // не активный цвет ободка painter->setPen(Qt::NoPen); // Убираем абрис painter->setRenderHints(QPainter::Antialiasing, true); // Включаем сглаживание painter->setBrush(brushNonActive); // Отрисовываем самый нижний фон в виде круга painter->drawEllipse(boundingRect().adjusted(1,1,-1,-1)); // с подгонкой под текущие размеры, которые // будут определяться в QML-слое. // Это будет не активный фон ободка // Прогресс бар будет формироваться с помощью отрисовки Pie графика painter->setBrush(brushActive); // Отрисовываем активный фон ободка в зависимости от угла поворота painter->drawPie(boundingRect().adjusted(1,1,-1,-1), // с подгонкой под размеры в QML слое 90*16, // Стартовая точка m_angle*16); // угол поворота, до которого нужно отрисовать объект painter->setBrush(brush); // основной фон таймера, перекрытием которого поверх остальных painter->drawEllipse(boundingRect().adjusted(10,10,-10,-10)); // будет сформирован ободок (он же прогресс бар) } void ClockCircle::clear() { setCircleTime(QTime(0,0,0,0)); // Очищаем время setAngle(0); // Выставляем поворот на ноль update(); // Обновляем объект emit cleared(); // ИСпускаем сигнал очистки } void ClockCircle::start() { internalTimer->start(50); // Запускаем таймер с шагом 50 мс } void ClockCircle::stop() { internalTimer->stop(); // Останавливаем таймер } QString ClockCircle::name() const { return m_name; } QColor ClockCircle::backgroundColor() const { return m_backgroundColor; } QColor ClockCircle::borderActiveColor() const { return m_borderActiveColor; } QColor ClockCircle::borderNonActiveColor() const { return m_borderNonActiveColor; } qreal ClockCircle::angle() const { return m_angle; } QTime ClockCircle::circleTime() const { return m_circleTime; } void ClockCircle::setName(const QString name) { if (m_name == name) return; m_name = name; emit nameChanged(name); } void ClockCircle::setBackgroundColor(const QColor backgroundColor) { if (m_backgroundColor == backgroundColor) return; m_backgroundColor = backgroundColor; emit backgroundColorChanged(backgroundColor); } void ClockCircle::setBorderActiveColor(const QColor borderActiveColor) { if (m_borderActiveColor == borderActiveColor) return; m_borderActiveColor = borderActiveColor; emit borderActiveColorChanged(borderActiveColor); } void ClockCircle::setBorderNonActiveColor(const QColor borderNonActiveColor) { if (m_borderNonActiveColor == borderNonActiveColor) return; m_borderNonActiveColor = borderNonActiveColor; emit borderNonActiveColorChanged(borderNonActiveColor); } void ClockCircle::setAngle(const qreal angle) { if (m_angle == angle) return; m_angle = angle; /* Данное добавление сделано для того, * чтобы обнулить поворот при достижении таймером * 60 секунд * */ if(m_angle <= -360) m_angle = 0; emit angleChanged(m_angle); } void ClockCircle::setCircleTime(const QTime circleTime) { if (m_circleTime == circleTime) return; m_circleTime = circleTime; emit circleTimeChanged(circleTime); }
main.qml
Nun, jetzt muss nur noch ein neues Objekt zur QML-Ebene hinzugefügt, konfiguriert und das Ergebnis angezeigt werden. In diesem Fall gibt es drei Schaltflächen, die Q_INVOKABLE-Timer-Methoden steuern und auch die Zeit in unserem Timer einstellen, während er läuft, basierend auf der Arbeit von Signalen und Slots .
import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 import QtQml 2.2 // После того, как объект зарегистрирован в C++ слое // его необходимо подключить в QML import ClockCircle 1.0 Window { visible: true width: 400 height: 400 // А теперь добавляем объект ClockCircle { id: clockCircle // позиционируем его и задаём размеры anchors.top: parent.top anchors.topMargin: 50 anchors.horizontalCenter: parent.horizontalCenter width: 200 height: 200 // Определяем его свойства, которые Q_PROPERTY name: "clock" backgroundColor: "whiteSmoke" borderActiveColor: "LightSlateGray" borderNonActiveColor: "LightSteelBlue" // Добавляем текст, на который будет выставляться время таймера Text { id: textTimer anchors.centerIn: parent font.bold: true font.pixelSize: 24 } // При изменении времени, помещаем это время на таймер onCircleTimeChanged: { textTimer.text = Qt.formatTime(circleTime, "mm:ss.zzz") } } Button { id: start text: "Start" onClicked: clockCircle.start(); // Запуск таймера anchors { left: parent.left leftMargin: 20 bottom: parent.bottom bottomMargin: 20 } } Button { id: stop text: "Stop" onClicked: clockCircle.stop(); // остановка таймера anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom bottomMargin: 20 } } Button { id: clear text: "Clear" onClicked: clockCircle.clear(); // очистка таймера anchors { right: parent.right rightMargin: 20 bottom: parent.bottom bottomMargin: 20 } } }
Videoanleitung
Laden Sie den Programmcode der Lektion herunter - CustomQuickItem
Полгода назад искал как сделать такой объект, делал на QML - первое знакомство с ним было, по вашему "QML-004". А тут вот на блюдечке все готово) Попробуем потом переделать как в вашем 032 уроке, сравним кто шустрее.
Самый шустрый будет как раз в уроке 032. Там же OpenGL!!!
Когда на работе стояла такая задача, я сначала реализовал в этом уроке 024 по QML, но понял, что при большом количестве объектов ой как плохо CPU тянет, в итоге разобрался с OpenGL и всё стало работать с пол пинка. Реально быстрее отрисовывает. Всё-таки CPU не должен графикой заниматься ))