Commit 8da5e19a authored by Valentin Platzgummer's avatar Valentin Platzgummer

measurement works mostly, some smaller issues left

parent 5a5b161e
......@@ -261,19 +261,18 @@
<file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file>
<file alias="RadioComponent.qml">src/AutoPilotPlugins/Common/RadioComponent.qml</file>
<file alias="SerialSettings.qml">src/ui/preferences/SerialSettings.qml</file>
<file alias="CircularGeneratorEditor.qml">src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml</file>
<file alias="MeasurementComplexItem/CircularGeneratorEditor.qml">src/MeasurementComplexItem/qml/CircularGeneratorEditor.qml</file>
<file alias="QGroundControl/Controls/CircularGeneratorMapVisual.qml">src/MeasurementComplexItem/qml/CircularGeneratorMapVisual.qml</file>
<file alias="MeasurementItemEditor.qml">src/MeasurementComplexItem/qml/MeasurementItemEditor.qml</file>
<file alias="QGroundControl/Controls/MeasurementItemMapVisual.qml">src/MeasurementComplexItem/qml/MeasurementItemMapVisual.qml</file>
<file alias="QGroundControl/Controls/CoordinateIndicator.qml">src/MeasurementComplexItem/qml/CoordinateIndicator.qml</file>
<file alias="QGroundControl/Controls/CoordinateIndicatorDrag.qml">src/MeasurementComplexItem/qml/CoordinateIndicatorDrag.qml</file>
<file alias="QGroundControl/Controls/DragCoordinate.qml">src/MeasurementComplexItem/qml/DragCoordinate.qml</file>
<file alias="LinearGeneratorEditor.qml">src/MeasurementComplexItem/qml/LinearGeneratorEditor.qml</file>
<file alias="ProgressIndicator.qml">src/MeasurementComplexItem/qml/ProgressIndicator.qml</file>
<file alias="MeasurementComplexItem/LinearGeneratorEditor.qml">src/MeasurementComplexItem/qml/LinearGeneratorEditor.qml</file>
<file alias="QGroundControl/Controls/GeoAreaVisualLoader.qml">src/MeasurementComplexItem/qml/GeoAreaVisualLoader.qml</file>
<file alias="MeasurementAreaEditor.qml">src/MeasurementComplexItem/qml/MeasurementAreaEditor.qml</file>
<file alias="MeasurementComplexItem/MeasurementAreaEditor.qml">src/MeasurementComplexItem/qml/MeasurementAreaEditor.qml</file>
<file alias="QGroundControl/Controls/MeasurementAreaMapVisual.qml">src/MeasurementComplexItem/qml/MeasurementAreaMapVisual.qml</file>
<file alias="SafeAreaEditor.qml">src/MeasurementComplexItem/qml/SafeAreaEditor.qml</file>
<file alias="MeasurementComplexItem/SafeAreaEditor.qml">src/MeasurementComplexItem/qml/SafeAreaEditor.qml</file>
<file alias="QGroundControl/Controls/SafeAreaMapVisual.qml">src/MeasurementComplexItem/qml/SafeAreaMapVisual.qml</file>
<file alias="SetupParameterEditor.qml">src/VehicleSetup/SetupParameterEditor.qml</file>
<file alias="SetupView.qml">src/VehicleSetup/SetupView.qml</file>
......@@ -290,6 +289,10 @@
<file alias="VirtualJoystick.qml">src/FlightDisplay/VirtualJoystick.qml</file>
<file alias="VTOLLandingPatternEditor.qml">src/PlanView/VTOLLandingPatternEditor.qml</file>
<file>src/MeasurementComplexItem/qml/GeoAreaEditorLoader.qml</file>
<file alias="QGroundControl/Controls/ItemDragger.qml">src/MeasurementComplexItem/qml/ItemDragger.qml</file>
<file alias="MeasurementComplexItem/AreaDataEditor.qml">src/MeasurementComplexItem/qml/AreaDataEditor.qml</file>
<file alias="MeasurementComplexItem/ParameterEditor.qml">src/MeasurementComplexItem/qml/ParameterEditor.qml</file>
<file alias="MeasurementComplexItem/qmldir">src/MeasurementComplexItem/qml/MeasurementComplexItem.qmldir</file>
</qresource>
<qresource prefix="/FirstRunPromptDialogs">
<file alias="UnitsFirstRunPrompt.qml">src/FirstRunPromptDialogs/UnitsFirstRunPrompt.qml</file>
......
This diff is collapsed.
......@@ -18,6 +18,8 @@ public:
AreaData &operator=(const AreaData &other);
Q_PROPERTY(QmlObjectListModel *areaList READ areaList NOTIFY areaListChanged)
Q_PROPERTY(bool showErrorMessages READ showErrorMessages WRITE
setShowErrorMessages NOTIFY showErrorMessagesChanged)
// Member Methodes
//!
......@@ -27,7 +29,8 @@ public:
//!
//! \brief remove
//! \param areaData Removes the area.
//! \note Deletes the area if it has no parent.
//! \note Deletes the area if it has either no parent or the parent is this
//! object.
void remove(GeoArea *areaData);
void clear();
//!
......@@ -46,8 +49,7 @@ public:
//! \note Origin might change if the list of areas changes.
const QGeoCoordinate &origin() const;
Q_INVOKABLE bool isValid() const;
Q_INVOKABLE bool tryMakeValid();
Q_INVOKABLE bool isCorrect();
//!
//! \brief initialize Initializes the areas in a valid way, such that they
//! area inside the bounding box. \param bottomLeft bottom left corner of the
......@@ -65,18 +67,33 @@ public:
//! either.
//!
Q_INVOKABLE bool initialized();
Q_INVOKABLE void intersection();
bool operator==(const AreaData &other) const;
bool operator!=(const AreaData &other) const;
QString errorString() const; // Contains a message about the last error.
bool showErrorMessages() const;
void setShowErrorMessages(bool showErrorMessages);
signals:
void areaListChanged();
void originChanged();
void error(); // Emitted if errorString() contains a new message.
void showErrorMessagesChanged();
private slots:
void _updateOrigin();
private:
void _setOrigin(const QGeoCoordinate &origin);
QGeoCoordinate _newOrigin();
void _processError(const QString &str);
bool _areasCorrect();
bool _getAreas(MeasurementArea **measurementArea, SafeArea **safeArea);
QGeoCoordinate _origin;
QmlObjectListModel _areaList;
bool _initialized;
QString _errorString;
bool _showErrorMessages;
};
......@@ -62,7 +62,7 @@ QString CircularGenerator::abbreviation() { return QStringLiteral("C. Gen."); }
bool CircularGenerator::get(Generator &generator) {
if (this->_d) {
if (this->_d->isValid()) {
if (this->_d->isCorrect()) {
// Prepare data.
auto origin = this->_d->origin();
origin.setAltitude(0);
......@@ -200,7 +200,7 @@ void CircularGenerator::resetReference() {
}
void CircularGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
if (this->_d != nullptr && !this->_connectionsEstablished) {
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......@@ -233,7 +233,7 @@ void CircularGenerator::establishConnections() {
}
void CircularGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
if (this->_d != nullptr && this->_connectionsEstablished) {
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......
......@@ -8,16 +8,26 @@ GeneratorBase::GeneratorBase(QObject *parent)
GeneratorBase::GeneratorBase(GeneratorBase::Data d, QObject *parent)
: QObject(parent), _d(d) {
establishConnections();
connect(_d, &AreaData::areaListChanged, this,
&GeneratorBase::_areaListChangedHandler);
}
GeneratorBase::~GeneratorBase() {}
GeneratorBase::Data GeneratorBase::data() const { return _d; }
void GeneratorBase::setData(const Data &d) {
deleteConnections();
_d = d;
establishConnections();
void GeneratorBase::setData(Data d) {
if (d != nullptr) {
if (_d != nullptr) {
disconnect(_d, &AreaData::areaListChanged, this,
&GeneratorBase::_areaListChangedHandler);
}
deleteConnections();
_d = d;
establishConnections();
connect(_d, &AreaData::areaListChanged, this,
&GeneratorBase::_areaListChangedHandler);
}
}
void GeneratorBase::establishConnections() {}
......@@ -27,6 +37,7 @@ void GeneratorBase::deleteConnections() {}
void GeneratorBase::_areaListChangedHandler() {
deleteConnections();
establishConnections();
emit generatorChanged();
}
} // namespace routing
......@@ -33,7 +33,7 @@ public:
virtual bool get(Generator &generator) = 0;
Data data() const;
void setData(const Data &d);
void setData(Data d);
signals:
void generatorChanged();
......
......@@ -48,7 +48,7 @@ QString LinearGenerator::abbreviation() { return QStringLiteral("L. Gen."); }
bool LinearGenerator::get(Generator &generator) {
if (_d) {
if (this->_d->isValid()) {
if (this->_d->isCorrect()) {
// Prepare data.
auto origin = this->_d->origin();
origin.setAltitude(0);
......@@ -148,7 +148,7 @@ Fact *LinearGenerator::alpha() { return &_alpha; }
Fact *LinearGenerator::minLength() { return &_minLength; }
void LinearGenerator::establishConnections() {
if (this->_d && !this->_connectionsEstablished) {
if (this->_d != nullptr && !this->_connectionsEstablished) {
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......@@ -178,7 +178,7 @@ void LinearGenerator::establishConnections() {
}
void LinearGenerator::deleteConnections() {
if (this->_d && this->_connectionsEstablished) {
if (this->_d != nullptr && this->_connectionsEstablished) {
auto measurementArea =
getGeoArea<const MeasurementArea *>(*this->_d->areaList());
auto serviceArea = getGeoArea<const SafeArea *>(*this->_d->areaList());
......
......@@ -376,72 +376,76 @@ void MeasurementComplexItem::_setAreaData(
}
}
bool MeasurementComplexItem::_updateRoute() {
// Reset data.
this->_route.clear();
this->_variantVector.clear();
this->_variantNames.clear();
emit variantNamesChanged();
if (this->_pAreaData->isValid()) {
// Prepare data.
auto origin = this->_pAreaData->origin();
origin.setAltitude(0);
if (!origin.isValid()) {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): origin invalid." << origin;
return false;
}
// Convert safe area.
auto serviceArea =
getGeoArea<const SafeArea *>(*this->_pAreaData->areaList());
auto geoSafeArea = serviceArea->coordinateList();
if (!(geoSafeArea.size() >= 3)) {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): safe area invalid." << geoSafeArea;
return false;
}
for (auto &v : geoSafeArea) {
if (v.isValid()) {
v.setAltitude(0);
} else {
void MeasurementComplexItem::_updateRoute() {
if (!editing()) {
// Reset data.
this->_route.clear();
this->_variantVector.clear();
this->_variantNames.clear();
emit variantNamesChanged();
if (this->_pAreaData->isCorrect()) {
// Prepare data.
auto origin = this->_pAreaData->origin();
origin.setAltitude(0);
if (!origin.isValid()) {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): safe area contains invalid coordinate."
<< geoSafeArea;
return false;
<< "_updateWorker(): origin invalid." << origin;
return;
}
}
// Routing par.
RoutingParameter par;
par.numSolutions = 5;
auto &safeAreaENU = par.safeArea;
snake::areaToEnu(origin, geoSafeArea, safeAreaENU);
// Convert safe area.
auto serviceArea =
getGeoArea<const SafeArea *>(*this->_pAreaData->areaList());
auto geoSafeArea = serviceArea->coordinateList();
if (!(geoSafeArea.size() >= 3)) {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): safe area invalid." << geoSafeArea;
return;
}
for (auto &v : geoSafeArea) {
if (v.isValid()) {
v.setAltitude(0);
} else {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): safe area contains invalid coordinate."
<< geoSafeArea;
return;
}
}
// Create generator.
if (this->_pGenerator != nullptr) {
routing::GeneratorBase::Generator g; // Transect generator.
if (this->_pGenerator->get(g)) {
// Start/Restart routing worker.
this->_pWorker->route(par, g);
return true;
// Routing par.
RoutingParameter par;
par.numSolutions = 5;
auto &safeAreaENU = par.safeArea;
snake::areaToEnu(origin, geoSafeArea, safeAreaENU);
// Create generator.
if (this->_pGenerator != nullptr) {
routing::GeneratorBase::Generator g; // Transect generator.
if (this->_pGenerator->get(g)) {
// Start/Restart routing worker.
this->_pWorker->route(par, g);
_setState(STATE::ROUTING);
return;
} else {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): generator creation failed.";
return;
}
} else {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): generator creation failed.";
return false;
<< "_updateWorker(): pGenerator == nullptr, number of registered "
"generators: "
<< this->_generatorList.size();
return;
}
} else {
qCDebug(MeasurementComplexItemLog)
<< "_updateWorker(): pGenerator == nullptr, number of registered "
"generators: "
<< this->_generatorList.size();
return false;
<< "_updateWorker(): plan data invalid.";
return;
}
} else {
qCDebug(MeasurementComplexItemLog) << "_updateWorker(): plan data invalid.";
return false;
}
}
......@@ -471,7 +475,7 @@ void MeasurementComplexItem::_changeVariant() {
}
auto &newVariantCoordinates = this->_variantVector[variant];
this->_route.swap(newVariantCoordinates);
emit routeChanged();
} else { // error
qCDebug(MeasurementComplexItemLog)
<< "Variant out of bounds (variant =" << variant << ").";
......@@ -497,6 +501,7 @@ void MeasurementComplexItem::_reverseRoute() {
auto &t = this->_route;
std::reverse(t.begin(), t.end());
}
emit routeChanged();
}
}
......@@ -661,27 +666,35 @@ int MeasurementComplexItem::generatorIndex() {
return this->_generatorList.indexOf(this->_pGenerator);
}
void MeasurementComplexItem::editingStart() {
if (!_editing(this->_state)) {
void MeasurementComplexItem::startEditing() {
if (!editing()) {
*_pEditorData = *_pAreaData;
_setAreaData(_pEditorData);
_setState(STATE::EDITING);
}
}
void MeasurementComplexItem::editingStop() {
if (_editing(this->_state)) {
if (_pEditorData->isValid()) {
void MeasurementComplexItem::stopEditing() {
if (editing()) {
bool correct = _pEditorData->isCorrect();
if (correct) {
*_pAreaData = *_pEditorData;
}
_setAreaData(_pAreaData);
_setState(STATE::IDLE);
if (_pEditorData->isValid() && *_pEditorData != *_pAreaData) {
if (correct && *_pEditorData != *_pAreaData) {
_updateRoute();
}
}
}
void MeasurementComplexItem::abortEditing() {
if (editing()) {
_setAreaData(_pAreaData);
_setState(STATE::IDLE);
}
}
void MeasurementComplexItem::_storeRoutingData(
MeasurementComplexItem::PtrRoutingData pRoute) {
if (this->_state == STATE::ROUTING) {
......@@ -781,10 +794,11 @@ void MeasurementComplexItem::_storeRoutingData(
this->_variant.setCookedValue(QVariant(0));
connect(&this->_variant, &Fact::rawValueChanged, this,
&MeasurementComplexItem::_changeVariant);
this->_changeVariant();
this->_setState(STATE::IDLE);
this->_route.swap(this->_variantVector.first());
emit routeChanged();
this->_setState(STATE::IDLE);
} else {
qCDebug(MeasurementComplexItemLog)
<< "_setTransects(): failed, variantVector empty.";
......
......@@ -105,19 +105,26 @@ public:
// Editing.
//!
//! \brief editingStart Starts area data editing.
//! \brief startEditing Starts area data editing.
//!
//! Starts area data editing. Route will not be updated bewteen a call
//! sequence of editingStart() and editingStop().
//!
void editingStart();
Q_INVOKABLE void startEditing();
//!
//! \brief editingStop Stops area editing.
//! \brief stopEditing Stops area editing.
//!
//! Stops area editing. Will reset area data to the state before
//! editingStart() if it is invalid. Triggers a route update.
//!
void editingStop();
Q_INVOKABLE void stopEditing();
//!
//! \brief abortEditing Aborts area editing.
//!
//! Will reset area data to the state before
//! editingStart().
//!
Q_INVOKABLE void abortEditing();
// Property getters
const AreaData *areaData() const;
......@@ -156,7 +163,7 @@ private slots:
// Worker functions.
void _storeRoutingData(PtrRoutingData pRoute);
bool _updateRoute();
void _updateRoute();
void _changeVariant();
void _reverseRoute();
......
This diff is collapsed.
#pragma once
#include "QmlObjectListModel.h"
#include <QObject>
#include <QScopedPointer>
#include <QSharedPointer>
#include "Geometry/WimaCorridor.h"
#include "Geometry/WimaCorridorData.h"
#include "Geometry/WimaJoinedArea.h"
#include "Geometry/WimaJoinedAreaData.h"
#include "Geometry/WimaMeasurementArea.h"
#include "Geometry/WimaMeasurementAreaData.h"
#include "Geometry/WimaServiceArea.h"
#include "Geometry/WimaServiceAreaData.h"
#include "WimaPlanData.h"
#include "Snake/NemoInterface.h"
#include "JsonHelper.h"
class MissionController;
class PlanMasterController;
namespace wima_planer_detail {
class WimaStateMachine;
}
class WimaPlaner : public QObject {
Q_OBJECT
enum FileType { WimaFile, PlanFile };
public:
WimaPlaner(QObject *parent = nullptr);
~WimaPlaner();
template <class T> WimaPlaner(T t, QObject *parent = nullptr) = delete;
template <class T> WimaPlaner(T t) = delete;
Q_PROPERTY(PlanMasterController *masterController READ masterController WRITE
setMasterController NOTIFY masterControllerChanged)
Q_PROPERTY(MissionController *missionController READ missionController WRITE
setMissionController NOTIFY missionControllerChanged)
Q_PROPERTY(QmlObjectListModel *visualItems READ visualItems NOTIFY
visualItemsChanged)
Q_PROPERTY(int currentPolygonIndex READ currentPolygonIndex WRITE
setCurrentPolygonIndex NOTIFY currentPolygonIndexChanged)
Q_PROPERTY(QString currentFile READ currentFile NOTIFY currentFileChanged)
Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT)
Q_PROPERTY(QStringList saveNameFilters READ saveNameFilters CONSTANT)
Q_PROPERTY(QString fileExtension READ fileExtension CONSTANT)
Q_PROPERTY(QGeoCoordinate joinedAreaCenter READ joinedAreaCenter CONSTANT)
Q_PROPERTY(NemoInterface *nemoInterface READ nemoInterface CONSTANT)
Q_PROPERTY(bool synchronized READ synchronized NOTIFY synchronizedChanged)
Q_PROPERTY(bool needsUpdate READ needsUpdate NOTIFY needsUpdateChanged)
Q_PROPERTY(bool readyForSynchronization READ readyForSynchronization NOTIFY
readyForSynchronizationChanged)
Q_PROPERTY(bool surveyReady READ surveyReady NOTIFY surveyReadyChanged)
Q_PROPERTY(bool progressLocked READ progressLocked WRITE setProgressLocked
NOTIFY progressLockedChanged)
// Property accessors
PlanMasterController *masterController(void);
MissionController *missionController(void);
QmlObjectListModel *visualItems(void);
int currentPolygonIndex(void) const;
QString currentFile(void) const;
QStringList loadNameFilters(void) const;
QStringList saveNameFilters(void) const;
QString fileExtension(void) const;
QGeoCoordinate joinedAreaCenter(void) const;
NemoInterface *nemoInterface(void);
bool synchronized();
bool needsUpdate();
bool readyForSynchronization();
bool surveyReady();
bool progressLocked();
// Property setters
void setMasterController(PlanMasterController *masterController);
void setMissionController(MissionController *missionController);
/// Sets the integer index pointing to the current polygon. Current polygon is
/// set interactive.
void setCurrentPolygonIndex(int index);
void setProgressLocked(bool l);
Q_INVOKABLE bool addMeasurementArea();
/// Removes an area from _visualItems
/// @param index Index of the area to be removed
Q_INVOKABLE void removeArea(int index);
Q_INVOKABLE bool addServiceArea();
Q_INVOKABLE bool addCorridor();
/// Remove all areas from WimaPlaner and all mission items from
/// MissionController
Q_INVOKABLE void removeAll();
/// Recalculates vehicle corridor, flight path, etc.
Q_INVOKABLE void update();
/// Pushes the generated mission data to the wimaController.
Q_INVOKABLE void synchronize();
Q_INVOKABLE void saveToCurrent();
Q_INVOKABLE void saveToFile(const QString &filename);
Q_INVOKABLE bool loadFromCurrent();
Q_INVOKABLE bool loadFromFile(const QString &filename);
Q_INVOKABLE void resetAllInteractive(void);
Q_INVOKABLE void setInteractive(void);
QJsonDocument saveToJson(FileType fileType);
// static Members
static const char *wimaFileExtension;
static const char *areaItemsName;
static const char *missionItemsName;
signals:
void masterControllerChanged(void);
void missionControllerChanged(void);
void visualItemsChanged(void);
void currentPolygonIndexChanged(int index);
void currentFileChanged();
void wimaBridgeChanged();
void synchronizedChanged(void);
void needsUpdateChanged(void);
void readyForSynchronizationChanged(void);
void surveyReadyChanged(void);
void progressLockedChanged();
private slots:
void updatePolygonInteractivity(int index);
void _update();
void CSDestroyedHandler();
void CSVisualTransectPointsChangedHandler();
void mAreaPathChangedHandler();
void mAreaTilesChangedHandler();
void mAreaProgressChangedHandler();
void mAreaProgressAcceptedHandler();
void mAreaReadyChangedHandler();
void sAreaPathChangedHandler();
void corridorPathChangedHandler();
void depotChangedHandler();
void missionControllerVisualItemsChangedHandler();
void missionControllerWaypointPathChangedHandler();
void missionControllerNewItemsFromVehicleHandler();
void missionControllerMissionItemCountChangedHandler();
void nemoInterfaceProgressChangedHandler();
#ifndef NDEBUG
void autoLoadMission(void);
#endif
private:
signals:
void joinedAreaValidChanged();
void stateChanged();
private:
// Member Functions
bool toPlanData(AreaData &planData);
bool shortestPath(const QGeoCoordinate &start,
const QGeoCoordinate &destination,
QVector<QGeoCoordinate> &path);
void setSynchronized(bool s);
void enableAreaMonitoring();
void disableAreaMonitoring();
void enableMissionControllerMonitoring();
void disableMissionControllerMonitoring();
bool areasMonitored();
bool missionControllerMonitored();
// Member Variables
PlanMasterController *_masterController;
MissionController *_missionController;
int _currentAreaIndex;
QString _currentFile;
WimaMeasurementArea _measurementArea;
WimaServiceArea _serviceArea;
WimaCorridor _corridor;
WimaJoinedArea _joinedArea;
QmlObjectListModel _visualItems; // all areas
CircularSurvey *_survey;
#ifndef NDEBUG
QTimer _autoLoadTimer; // timer to auto load mission after some time, prevents
// seg. faults
#endif
NemoInterface _nemoInterface;
// State
QScopedPointer<wima_planer_detail::WimaStateMachine> _stateMachine;
bool _areasMonitored;
bool _missionControllerMonitored;
bool _progressLocked;
bool _synchronized; // true if planData is synchronized with
// wimaController
};
This diff is collapsed.
#pragma once
#include <QDataStream>
#include <QObject>
namespace wima_planer_detail {
enum class STATE {
NEEDS_INIT,
WAITING_FOR_TILE_UPDATE,
NEEDS_J_AREA_UPDATE,
NEEDS_SURVEY_UPDATE,
WAITING_FOR_SURVEY_UPDATE,
NEEDS_PATH_UPDATE,
UP_TO_DATE
};
QDebug &operator<<(QDebug &ds, STATE s);
enum class EVENT {
INIT_DONE,
M_AREA_NOT_READY,
M_AREA_READY,
M_AREA_PATH_CHANGED,
S_AREA_PATH_CHANGED,
CORRIDOR_PATH_CHANGED,
M_AREA_TILES_CHANGED,
M_AREA_PROGRESS_CHANGED,
J_AREA_UPDATED,
DEPOT_CHANGED,
SURVEY_DESTROYED,
MISSION_ITEMS_DESTROYED,
SURVEY_UPDATE_TRIGGERED,
SURVEY_UPDATED,
PATH_CHANGED,
PATH_UPDATED
};
QDebug &operator<<(QDebug &ds, EVENT s);
class WimaStateMachine : public QObject {
Q_OBJECT
public:
explicit WimaStateMachine(QObject *parent = nullptr);
STATE state();
void updateState(EVENT e);
bool upToDate();
bool surveyReady();
signals:
void stateChanged();
void upToDateChanged();
void surveyReadyChanged();
private:
void setState(STATE s);
bool surveyReady(STATE s);
bool upToDate(STATE s);
STATE _state;
};
} // namespace wima_planer_detail
#include "GeoArea.h"
#include "snake.h"
#include "QGCLoggingCategory.h"
#include "QGCQGeoCoordinate.h"
#include <QDebug>
QGC_LOGGING_CATEGORY(GeoAreaLog, "GeoAreaLog")
const char *GeoArea::wimaAreaName = "GeoArea";
const char *GeoArea::areaTypeName = "AreaType";
const char *GeoArea::settingsGroup = "GeoArea";
......@@ -10,7 +17,7 @@ const char *GeoArea::settingsGroup = "GeoArea";
GeoArea::GeoArea(QObject *parent) : QGCMapPolygon(parent) { init(); }
GeoArea::GeoArea(const GeoArea &other, QObject *parent)
: QGCMapPolygon(other, parent) {
: QGCMapPolygon(other, parent), _errorString(other._errorString) {
init();
}
......@@ -34,13 +41,40 @@ bool GeoArea::loadFromJson(const QJsonObject &json, QString &errorString) {
return true;
}
bool GeoArea::isSimplePolygon() {
qWarning() << "WimaArea::isSimplePolygon: impl. missing.";
bool GeoArea::isCorrect() {
if (this->pathModel().count() >= 3) {
auto origin = this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate();
snake::FPolygon polygonENU;
snake::areaToEnu(origin, this->pathModel(), polygonENU);
std::string msg;
if (bg::is_valid(polygonENU, msg)) {
return true;
} else {
qCWarning(GeoAreaLog) << msg.c_str();
qCWarning(GeoAreaLog) << "origin: " << origin;
std::stringstream ss;
ss << bg::wkt(polygonENU);
qCWarning(GeoAreaLog) << "polygonENU: " << ss.str().c_str();
setErrorString(tr("Area invalid. Area must be a simple polygon."));
}
}
return false;
}
QString GeoArea::errorString() const { return this->_errorString; }
void GeoArea::init() { this->setObjectName(wimaAreaName); }
void GeoArea::setErrorString(const QString &str) {
this->_errorString = str;
emit errorStringChanged();
}
void GeoArea::setErrorString(const string &str) {
this->_errorString = str.c_str();
emit errorStringChanged();
}
bool copyAreaList(const QmlObjectListModel &from, QmlObjectListModel &to,
QObject *parent) {
// Check if elements are valid.
......
......@@ -13,6 +13,7 @@ public:
Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT)
Q_PROPERTY(QString editorQML READ editorQML CONSTANT)
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
virtual QString mapVisualQML(void) const = 0;
virtual QString editorQML(void) const = 0;
......@@ -23,15 +24,25 @@ public:
virtual GeoArea *clone(QObject *parent = nullptr) const = 0;
bool isSimplePolygon();
Q_INVOKABLE virtual bool isCorrect();
Q_INVOKABLE QString errorString() const;
// static Members
static const char *wimaAreaName;
static const char *areaTypeName;
static const char *settingsGroup;
signals:
void errorStringChanged();
protected:
void setErrorString(const QString &str);
void setErrorString(const std::string &str);
private:
void init();
QString _errorString;
};
// Example usage:
......
......@@ -91,7 +91,7 @@ MeasurementArea::MeasurementArea(QObject *parent)
MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
: GeoArea(other, parent),
_metaDataMap(FactMetaData::createMapFromJsonFile(
QStringLiteral(":/json/WimaMeasurementArea.SettingsGroup.json"),
QStringLiteral(":/json/MeasurementArea.SettingsGroup.json"),
this /* QObject parent */)),
_tileHeight(SettingsFact(settingsGroup, _metaDataMap[tileHeightName],
this /* QObject parent */)),
......@@ -103,6 +103,13 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
this /* QObject parent */)),
_state(STATE::IDLE) {
_tileHeight = other._tileHeight;
_tileWidth = other._tileWidth;
_minTileAreaPercent = other._minTileAreaPercent;
_showTiles = other._showTiles;
_progress = other._progress;
_tileData = other._tileData;
init();
}
......@@ -115,6 +122,7 @@ MeasurementArea::~MeasurementArea() {}
QString MeasurementArea::mapVisualQML() const {
return QStringLiteral("MeasurementAreaMapVisual.qml");
// return QStringLiteral("");
}
QString MeasurementArea::editorQML() const {
......@@ -211,6 +219,18 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
}
}
bool MeasurementArea::isCorrect() {
if (GeoArea::isCorrect()) {
if (ready()) {
return true;
} else {
setErrorString(
tr("Measurement Area tile calculation in progess. Please wait."));
}
}
return false;
}
bool MeasurementArea::setProgress(const QVector<int> &p) {
if (ready()) {
if (p.size() == this->tiles()->count() && this->_progress != p) {
......@@ -240,7 +260,7 @@ void MeasurementArea::doUpdate() {
const auto estNumTiles = totalArea / tileArea;
// Check some conditions.
if (long(std::ceil(estNumTiles.value())) <= SNAKE_MAX_TILES &&
this->count() >= 3 && this->isSimplePolygon()) {
this->GeoArea::isCorrect()) {
setState(STATE::UPDATEING);
auto polygon = this->coordinateList();
......
......@@ -50,6 +50,7 @@ public:
MeasurementArea *clone(QObject *parent = nullptr) const;
void saveToJson(QJsonObject &json) override;
bool loadFromJson(const QJsonObject &json, QString &errorString) override;
Q_INVOKABLE virtual bool isCorrect();
// Property getters.
Fact *tileHeight();
......
......@@ -25,7 +25,10 @@ SafeArea &SafeArea::operator=(const SafeArea &other) {
return *this;
}
QString SafeArea::mapVisualQML() const { return "SafeAreaMapVisual.qml"; }
QString SafeArea::mapVisualQML() const {
return "SafeAreaMapVisual.qml";
// return "";
}
QString SafeArea::editorQML() const { return "SafeAreaEditor.qml"; }
......@@ -37,11 +40,41 @@ const QGeoCoordinate &SafeArea::depot() const { return _depot; }
QGeoCoordinate SafeArea::depotQml() const { return _depot; }
bool SafeArea::setDepot(const QGeoCoordinate &coordinate) {
if (_depot.latitude() != coordinate.latitude() ||
_depot.longitude() != coordinate.longitude()) {
if (this->containsCoordinate(coordinate)) {
_depot = coordinate;
void SafeArea::putDepotInside() {
if (!this->containsCoordinate(this->_depot) &&
this->pathModel().count() > 0) {
if (this->_depot.isValid()) {
// Use nearest coordinate.
auto minDist = std::numeric_limits<double>::infinity();
int minIndex = 0;
for (int idx = 0; idx < this->pathModel().count(); ++idx) {
const QObject *obj = this->pathModel()[idx];
const auto *vertex = qobject_cast<const QGCQGeoCoordinate *>(obj);
if (vertex != nullptr) {
auto d = vertex->coordinate().distanceTo(this->_depot);
if (d < minDist) {
minDist = d;
minIndex = idx;
}
} else {
qCCritical(SafeAreaLog) << "init(): nullptr catched!";
}
}
this->setDepot(
this->pathModel().value<QGCQGeoCoordinate *>(minIndex)->coordinate());
} else {
// Use first coordinate.
this->setDepot(
this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate());
}
}
}
bool SafeArea::setDepot(const QGeoCoordinate &newDepot) {
if (_depot.latitude() != newDepot.latitude() ||
_depot.longitude() != newDepot.longitude()) {
if (this->containsCoordinate(newDepot)) {
_depot = newDepot;
_depot.setAltitude(0);
emit depotChanged();
return true;
......@@ -89,38 +122,22 @@ bool SafeArea::loadFromJson(const QJsonObject &json, QString &errorString) {
return retVal;
}
void SafeArea::init() {
this->setObjectName(safeAreaName);
connect(this, &GeoArea::pathChanged, [this] {
if (!this->_depot.isValid() || !this->containsCoordinate(this->_depot)) {
if (this->containsCoordinate(this->center())) {
// Use center.
this->setDepot(this->center());
} else if (this->_depot.isValid()) {
// Use nearest coordinate.
auto minDist = std::numeric_limits<double>::infinity();
int minIndex = 0;
for (int idx = 0; idx < this->pathModel().count(); ++idx) {
const QObject *obj = this->pathModel()[idx];
const auto *vertex = qobject_cast<const QGCQGeoCoordinate *>(obj);
if (vertex != nullptr) {
auto d = vertex->coordinate().distanceTo(this->_depot);
if (d < minDist) {
minDist = d;
minIndex = idx;
}
} else {
qCCritical(SafeAreaLog) << "init(): nullptr catched!";
}
}
this->setDepot(this->pathModel()
.value<QGCQGeoCoordinate *>(minIndex)
->coordinate());
} else if (this->pathModel().count() > 0) {
// Use first coordinate.
this->setDepot(
this->pathModel().value<QGCQGeoCoordinate *>(0)->coordinate());
bool SafeArea::isCorrect() {
if (GeoArea::isCorrect()) {
if (this->_depot.isValid()) {
if (this->containsCoordinate(this->_depot)) {
return true;
} else {
setErrorString(tr("Depot outside Safe Area"));
}
} else {
qCritical(SafeAreaLog) << "Depot invalid " << _depot;
}
});
}
return false;
}
void SafeArea::init() {
this->setObjectName(safeAreaName);
connect(this, &GeoArea::pathChanged, this, &SafeArea::putDepotInside);
}
......@@ -13,15 +13,17 @@ public:
Q_PROPERTY(
QGeoCoordinate depot READ depotQml WRITE setDepot NOTIFY depotChanged)
// Overrides from WimaPolygon
// Overrides from GeoArea
QString mapVisualQML(void) const override;
QString editorQML(void) const override;
SafeArea *clone(QObject *parent = nullptr) const;
void saveToJson(QJsonObject &json) override;
bool loadFromJson(const QJsonObject &json, QString &errorString) override;
Q_INVOKABLE virtual bool isCorrect();
// Property acessors
const QGeoCoordinate &depot(void) const;
bool setDepot(const QGeoCoordinate &newDepot);
QGeoCoordinate depotQml(void) const;
// static Members
......@@ -31,9 +33,8 @@ public:
static const char *depotAltitudeName;
signals:
void depotChanged(void);
public slots:
bool setDepot(const QGeoCoordinate &coordinate);
private slots:
void putDepotInside();
private:
// Member Methodes
......
......@@ -5,46 +5,46 @@
[
{
"name": "TileHeight",
"shortDescription": "The height of a tile",
"shrotDesc": "The height of a tile",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 2,
"defaultValue": 5
"default": 5
},
{
"name": "TileWidth",
"shortDescription": "The height of a tile",
"shrotDesc": "The height of a tile",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 2,
"defaultValue": 5
"default": 5
},
{
"name": "MinTileAreaPercent",
"shortDescription": "The minimal allowed area in percent (of width*height).",
"shrotDesc": "The minimal allowed area in percent (of width*height).",
"type": "double",
"units": "%",
"min": 0,
"max": 100,
"decimalPlaces": 2,
"defaultValue": 20
"default": 20
},
{
"name": "ShowTiles",
"shortDescription": "Show tiles",
"shrotDesc": "Show tiles",
"type": "bool",
"defaultValue": true
"default": true
},
{
"name": "BorderPolygonOffset",
"shortDescription": "The distance between the measurement area and it's enclosing polygon",
"shrotDesc": "The distance between the measurement area and it's enclosing polygon",
"type": "double",
"units": "m",
"min": 0,
"decimalPlaces": 1,
"defaultValue": 5
"default": 5
}
]
}
......@@ -20,6 +20,9 @@ namespace bu = boost::units;
#include <GeographicLib/Geocentric.hpp>
#include <GeographicLib/LocalCartesian.hpp>
#include "QGCQGeoCoordinate.h"
#include "QmlObjectListModel.h"
using namespace std;
namespace snake {
......@@ -105,13 +108,11 @@ template <class GeoPoint1, class GeoPoint2>
void toENU(const GeoPoint1 &origin, const GeoPoint2 &in, FPoint &out) {
static GeographicLib::Geocentric earth(GeographicLib::Constants::WGS84_a(),
GeographicLib::Constants::WGS84_f());
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(),
origin.altitude(), earth);
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(), 0,
earth);
double x = 0, y = 0, z = 0;
auto alt = in.altitude();
alt = std::isnan(alt) ? 0 : alt;
proj.Forward(in.latitude(), in.longitude(), alt, x, y, z);
proj.Forward(in.latitude(), in.longitude(), 0, x, y, z);
out.set<0>(x);
out.set<1>(y);
(void)z;
......@@ -121,13 +122,11 @@ template <class GeoPoint1, class GeoPoint2, class Point>
void toENU(const GeoPoint1 &origin, const GeoPoint2 &in, Point &out) {
static GeographicLib::Geocentric earth(GeographicLib::Constants::WGS84_a(),
GeographicLib::Constants::WGS84_f());
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(),
origin.altitude(), earth);
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(), 0,
earth);
double x = 0, y = 0, z = 0;
auto alt = in.altitude();
alt = std::isnan(alt) ? 0 : alt;
proj.Forward(in.latitude(), in.longitude(), alt, x, y, z);
proj.Forward(in.latitude(), in.longitude(), 0, x, y, z);
out.setX(x);
out.setY(y);
(void)z;
......@@ -137,8 +136,8 @@ template <class GeoPoint>
void fromENU(const GeoPoint &origin, const FPoint &in, GeoPoint &out) {
static GeographicLib::Geocentric earth(GeographicLib::Constants::WGS84_a(),
GeographicLib::Constants::WGS84_f());
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(),
origin.altitude(), earth);
GeographicLib::LocalCartesian proj(origin.latitude(), origin.longitude(), 0,
earth);
double lat = 0, lon = 0, alt = 0;
proj.Reverse(in.get<0>(), in.get<1>(), 0.0, lat, lon, alt);
......@@ -166,9 +165,26 @@ void areaToEnu(const GeoPoint &origin, const Container &in, FPolygon &out) {
bg::correct(out);
}
template <class GeoPoint>
void areaToEnu(const GeoPoint &origin, QmlObjectListModel &in, FPolygon &out) {
FPolygon buffer;
for (int i = 0; i < in.count(); ++i) {
auto vertex = in.value<const QGCQGeoCoordinate *>(i);
if (vertex != nullptr) {
FPoint p;
toENU(origin, vertex->coordinate(), p);
buffer.outer().push_back(p);
} else {
return;
}
}
bg::correct(buffer);
out = std::move(buffer);
}
template <class GeoPoint, class Container1, class Container2>
void areaFromEnu(const GeoPoint &origin, Container1 &in,
const Container2 &out) {
void areaFromEnu(const GeoPoint &origin, const Container1 &in,
Container2 &out) {
for (auto &vertex : in) {
typename Container2::value_type p;
fromENU(origin, vertex, p);
......@@ -177,7 +193,7 @@ void areaFromEnu(const GeoPoint &origin, Container1 &in,
}
template <class GeoPoint, class Container>
void areaFromEnu(const GeoPoint &origin, FPolygon &in, const Container &out) {
void areaFromEnu(const GeoPoint &origin, const FPolygon &in, Container &out) {
for (auto &vertex : in.outer()) {
typename Container::value_type p;
fromENU(origin, vertex, p);
......
......@@ -5,31 +5,31 @@
[
{
"name": "TransectDistance",
"shortDescription": "The distance between transects.",
"shortDesc": "The distance between transects.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
"default": 5.0
},
{
"name": "DeltaAlpha",
"shortDescription": "Angle discretisation.",
"shortDesc": "Angle discretisation.",
"type": "double",
"units": "Deg",
"min": 0.3,
"max": 90,
"decimalPlaces": 1,
"defaultValue": 5.0
"default": 5.0
},
{
"name": "MinLength",
"shortDescription": "The minimal transect length.",
"shortDesc": "The minimal transect length.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
"default": 5.0
}
]
}
......@@ -5,31 +5,31 @@
[
{
"name": "TransectDistance",
"shortDescription": "The distance between transects.",
"shrotDesc": "The distan",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
"default": 5.0
},
{
"name": "Alpha",
"shortDescription": "Transect angle.",
"shrotDesc": "Transect angle.",
"type": "double",
"units": "Deg",
"min": 0,
"max": 180,
"decimalPlaces": 1,
"defaultValue": 0.0
"default": 0.0
},
{
"name": "MinLength",
"shortDescription": "The minimal transect length.",
"shrotDesc": "The minimal transect length.",
"type": "double",
"units": "m",
"min": 0.3,
"decimalPlaces": 1,
"defaultValue": 5.0
"default": 5.0
}
]
}
......@@ -5,18 +5,18 @@
[
{
"name": "Variant",
"shortDescription": "Route variant.",
"shrotDesc": "Route variant.",
"type": "uint64",
"defaultValue": 0
"default": 0
},
{
"name": "Altitude",
"shortDescription": "Altitude",
"shrotDesc": "Altitude",
"type": "double",
"units": "m",
"min": 1,
"decimalPlaces": 1,
"defaultValue": 10.0
"default": 10.0
}
]
}
import QtQuick 2.0
import QtQuick.Layouts 1.11
import QtQuick.Controls 1.4
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.ScreenTools 1.0
GridLayout {
id:_root
property bool checked: true
property var missionItem: undefined
property int availableWidth: 300
property var _areaData: missionItem.areaData
property real _margin: ScreenTools.defaultFontPixelWidth / 2
width: availableWidth
columnSpacing: _margin
rowSpacing: _margin
columns: 2
Component.onCompleted: {
console.assert(missionItem !== undefined, "please set the missionItem property")
checkedChangedHandler()
}
onCheckedChanged: checkedChangedHandler()
ExclusiveGroup{id:areaGroup}
Repeater{
id: areaSelector
property int selectedIndex: -1
model: _missionItem.areaData.areaList
delegate: QGCRadioButton {
text: object.objectName
checkable: _root.checked
Layout.fillWidth: true
Layout.columnSpan: 2
onCheckedChanged: {
if (checked){
areaSelector.selectedIndex = index
}
}
Component.onCompleted: {
if (index === 0){
checked = true
}
object.interactive = Qt.binding(function(){return checked && _root.checked})
}
}
} // area Repeater
ColumnLayout {
id:editorParent
Layout.fillWidth: true
Layout.columnSpan: 2
}
Repeater{
id:areaEditorRepeater
model: _missionItem.areaData.areaList
delegate: Item{
id:editor
visible: index == areaSelector.selectedIndex
property var _visualItem: undefined
property var geoArea: object
Component.onCompleted: {
if (geoArea.editorQML && !_visualItem) {
var component = Qt.createComponent(geoArea.editorQML)
if (component.status === Component.Error) {
console.log("Error loading Qml: ", geoArea.editorQML, component.errorString())
} else {
_visualItem = component.createObject(editorParent, {
geoArea: editor.geoArea,
visible: Qt.binding(function(){return editor.visible}),
availableWidth: Qt.binding(function(){return editorParent.width})})
}
}
}
Component.onDestruction: {
if (_visualItem) {
_visualItem.destroy()
}
}
} // editor
} // areaEditorRepeater
QGCButton{
text: "Check Rules"
enabled: _root.checked
Layout.fillWidth: true
onClicked: {
_areaData.isCorrect()
}
}
QGCButton{
text: "Intersection"
enabled: _root.checked
Layout.fillWidth: true
onClicked: {
_areaData.intersection()
}
}
function checkedChangedHandler(){
if (_root.checked){
missionItem.startEditing()
} else {
missionItem.stopEditing()
}
}
}
......@@ -9,7 +9,7 @@ import QGroundControl.ScreenTools 1.0
GridLayout {
id: grid
property var generator // CircularGenerator
property var generator: undefined // CircularGenerator
property var availableWidth
property real _margin: ScreenTools.defaultFontPixelWidth / 2
......@@ -18,6 +18,10 @@ GridLayout {
rowSpacing: _margin
columns: 2
Component.onCompleted: {
console.assert(generator !== undefined, "please set the generator property")
}
QGCLabel { text: qsTr("Distance") }
FactTextField {
fact: generator.distance
......
......@@ -36,9 +36,10 @@ Item {
var component = Qt.createComponent(_root.geoArea.mapVisualQML)
if (component.status === Component.Error) {
console.log("Error loading Qml: ", _root.geoArea.mapVisualQML, component.errorString())
} else {
_root._visualItem = component.createObject(_root.map, { map: _root.map, geoArea: _root.geoArea})
_root._visualItem.clicked.connect(_root.clicked)
}
_root._visualItem = component.createObject(_root.map, { map: _root.map, geoArea: _root.geoArea})
_root._visualItem.clicked.connect(_root.clicked)
}
}
......
// Idea from:
// https://stackoverflow.com/questions/42992067/qtquick-dragging-mapquickitem-on-a-map
import QtQuick 2.3
import QtLocation 5.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controls 1.0
Rectangle {
id: root
property var anchor: undefined
property bool draggable: true
readonly property bool dragged: mouseArea.drag.active
signal dragStart
signal dragStop
signal clicked
property bool _mobile: ScreenTools.isMobile
property real _touchWidth: Math.max(anchor.width, ScreenTools.minTouchPixels)
property real _touchHeight: Math.max(anchor.height, ScreenTools.minTouchPixels)
property real _touchMarginHorizontal: _mobile ? (_touchWidth - anchor.width) / 2 : 0
property real _touchMarginVertical: _mobile ? (_touchHeight - anchor.height) / 2 : 0
x: anchor.x - _touchMarginHorizontal
y: anchor.y - _touchMarginVertical
width: anchor.width + (_touchMarginHorizontal * 2)
height: anchor.height + (_touchMarginVertical * 2)
color: "transparent"
z: QGroundControl.zOrderMapItems + 1
Component.onCompleted: {
console.assert(anchor !== undefined, "please set the anchor property")
}
onDraggedChanged: {
if (dragged) {
dragStart()
} else {
dragStop()
}
}
QGCMouseArea {
id: mouseArea
enabled: draggable
preventStealing: true
drag.target: root
drag.threshold: 0
anchors.fill: parent
cursorShape: draggable ?
(dragged ? Qt.ClosedHandCursor : Qt.OpenHandCursor)
: Qt.ArrowCursor
onClicked: {
focus = true
root.clicked()
}
}
Connections {
target: anchor
onXChanged: if (!dragged) x = anchor.x - _touchMarginHorizontal
onYChanged: if (!dragged) y = anchor.y - _touchMarginVertical
}
onXChanged: if (dragged) anchor.x = x + _touchMarginHorizontal
onYChanged: if (dragged) anchor.y = y + _touchMarginVertical
Drag.active: true
}
......@@ -8,8 +8,8 @@ import QGroundControl.ScreenTools 1.0
GridLayout {
property var generator // CircularGenerator
property var availableWidth
property var generator: undefined // LinearGenerator
property var availableWidth: 300
property real _margin: ScreenTools.defaultFontPixelWidth / 2
width: availableWidth
......@@ -17,6 +17,10 @@ GridLayout {
rowSpacing: _margin
columns: 2
Component.onCompleted: {
console.assert(generator !== undefined, "please set the generator property")
}
QGCLabel { text: qsTr("Distance") }
FactTextField {
fact: generator.distance
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QGroundControl 1.0
......@@ -13,140 +10,98 @@ import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
// Editor for Operating Area items
Rectangle {
id: _root
height: visible ? (editorColumn.height + (_margin * 2)) : 0
GridLayout {
id: editorColumn
columns: 2
columnSpacing: _margin
rowSpacing: _margin
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property bool polygonInteractive: areaItem.interactive
property var polygon: areaItem
property bool initNecesarry: true
property var geoArea: undefined
property int availableWidth
onPolygonInteractiveChanged: {
polygon.interactive = polygonInteractive;
}
property real _margin: ScreenTools.defaultFontPixelWidth / 2
QGCPalette { id: qgcPal; colorGroupEnabled: true }
Component.onCompleted: {
console.assert(geoArea !== undefined, "please set the areaItem property")
}
Column {
id: editorColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
SectionHeader {
id: tilesHeader
text: qsTr("Tiles")
Layout.columnSpan: 2
Layout.fillWidth: true
}
SectionHeader {
id: settingsHeader
text: qsTr("General")
GridLayout{
visible: tilesHeader.checked
Layout.fillWidth: true
Layout.columnSpan: 2
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel {
text: qsTr("Height")
Layout.fillWidth: true
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: settingsHeader.checked
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Offset") }
FactTextField {
fact: areaItem.borderPolygonOffset
Layout.fillWidth: true
}
}
FactCheckBox {
text: qsTr("Border Polygon")
fact: areaItem.showBorderPolygon
//enabled: !missionItem.followTerrain
}
Item {
height: ScreenTools.defaultFontPixelHeight / 2
width: 1
}
} // Column - Settings
SectionHeader {
id: tilesHeader
text: qsTr("Tiles")
FactTextField {
fact: geoArea.tileHeight
Layout.fillWidth: true
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: tilesHeader.checked
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Height") }
FactTextField {
fact: areaItem.tileHeight
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Width") }
QGCLabel {
text: qsTr("Width")
Layout.fillWidth: true
}
FactTextField {
fact: areaItem.tileWidth
Layout.fillWidth: true
}
FactTextField {
fact: geoArea.tileWidth
Layout.fillWidth: true
}
QGCLabel { text: qsTr("Min. Area") }
QGCLabel {
text: qsTr("Min. Area")
Layout.fillWidth: true
}
FactTextField {
fact: areaItem.minTileArea
Layout.fillWidth: true
}
FactTextField {
fact: geoArea.minTileArea
Layout.fillWidth: true
}
FactCheckBox {
text: qsTr("Show Tiles")
fact: areaItem.showTiles
}
} // Tile GridLayout
} // Tile Column
FactCheckBox {
text: qsTr("Show Tiles")
fact: geoArea.showTiles
}
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
Layout.fillWidth: true
Layout.columnSpan: 2
}
Grid {
columns: 2
columnSpacing: ScreenTools.defaultFontPixelWidth
visible: statsHeader.checked
GridLayout{
visible: statsHeader.checked
Layout.fillWidth: true
Layout.columnSpan: 2
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Area") }
QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(areaItem.area).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }
QGCLabel { text: qsTr("Area") }
// QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(geoArea.area).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }
QGCLabel { text: qsTr("Tiles") }
QGCLabel { text: areaItem.tiles.count }
QGCLabel { text: qsTr("Max. Tiles") }
QGCLabel { text: areaItem.maxTiles }
QGCLabel { text: qsTr("Tiles") }
QGCLabel { text: geoArea.tiles.count }
}
} // Column
} // Rectangle
QGCLabel { text: qsTr("Max. Tiles") }
QGCLabel { text: geoArea.maxTiles }
}
} // Column
......@@ -44,6 +44,7 @@ Item {
borderColor: "green"
interiorColor: "green"
altColor: QGroundControl.globalPalette.surveyPolygonTerrainCollision
z: QGroundControl.zOrderMapItems-1
interiorOpacity: _root.opacity
}
......
ParameterEditor 1.0 ParameterEditor.qml
AreaDataEditor 1.0 AreaDataEditor.qml
LinearGenerator 1.0 LinearGenerator.qml
CircularGenerator 1.0 CircularGenerator.qml
SafeAreaEditor 1.0 SafeAreaEditor.qml
MeasurementAreaEditor 1.0 MeasurementAreaEditor.qml
......@@ -37,21 +37,28 @@ Item {
signal clicked(int sequenceNumber)
on_EditingChanged: {
_destroyEntryCoordinate()
_destroyExitCoordinate()
_destroyTransectsComponent()
_destroyGeneratorVisuals()
}
// on_EditingChanged: {
// if (_editing){
// _destroyEntryCoordinate()
// _destroyExitCoordinate()
// _destroyTransectsComponent()
// _destroyGeneratorVisuals()
// } else {
// _addEntryCoordinate()
// _addExitCoordinate()
// _addTransectsComponent()
// _addGeneratorVisuals()
// }
// }
Component.onCompleted: {
console.assert(map != undefined, "please set the map property")
_addEntryCoordinate()
_addExitCoordinate()
_addTransectsComponent()
_addGeneratorVisuals()
var bbox = boundingBox()
_missionItem.areaData.initialize(bbox[0], bbox[1])
console.assert(map != undefined, "please set the map property")
}
Component.onDestruction: {
......@@ -71,10 +78,11 @@ Item {
id: visualTransectsComponent
MapPolyline {
property var route: _missionItem.route
line.color: "white"
line.width: 2
path: route.length > 0 ? route : []
path: _missionItem.route
onPathChanged: console.log("path:" + path)
}
}
......
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
ColumnLayout {
id:root
property int availableWidth: 300
property var missionItem: undefined ///< Mission Item for editor
property bool checked: true
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property var _generator: missionItem.generator
property var _generatorEditor: undefined
width: availableWidth
Component.onCompleted: {
console.assert(missionItem !== undefined, "please set the missionItem property")
_addGeneratorEditor()
}
Component.onDestruction: {
_destroyGeneratorEditor()
}
on_GeneratorChanged: {
_destroyGeneratorEditor()
_addGeneratorEditor()
}
SectionHeader {
id: generalHeader
Layout.fillWidth: true
text: qsTr("General")
}
GridLayout {
id: generalGrid
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: generalHeader.checked
QGCLabel { text: qsTr("Altitude!!!") }
QGCLabel { text: qsTr("Relative Altitude!!!") }
QGCLabel {
text: qsTr("Variant")
Layout.columnSpan: 2
visible: variantRepeater.len > 0
}
GridLayout{
Layout.columnSpan: 2
columnSpacing: _margin
rowSpacing: _margin
columns: 6
Repeater{
id: variantRepeater
property var fact: missionItem.variant
property int variant: fact.value
property var names: missionItem.variantNames
property int len: missionItem.variantNames.length
model: len
delegate: QGCRadioButton {
checked: index === variantRepeater.variant
text: variantRepeater.names[index] ? variantRepeater.names[index]: ""
onCheckedChanged: {
if (checked){
missionItem.variant.value = index
}
checked = Qt.binding(function(){ return index === variantRepeater.variant})
}
}
} // variant repeater
} // variant grid
} // general grid
// Generator Editor
SectionHeader {
id: generatorHeader
Layout.fillWidth: true
text: qsTr("Generator")
}
GridLayout{
Layout.fillWidth: true
columnSpacing: _margin
rowSpacing: _margin
columns: 2
visible: generatorHeader.checked
QGCComboBox {
property var names: missionItem.generatorNameList
property int length: names.length
enabled: root.checked
anchors.margins: ScreenTools.defaultFontPixelWidth
currentIndex: missionItem.generatorIndex
Layout.columnSpan: 2
model: missionItem.generatorNameList
onActivated: {
if (index != -1){
missionItem.switchToGenerator(index)
}
}
}
}
ColumnLayout{
id:generatorEditorParent
Layout.fillWidth: true
visible: generatorHeader.checked
}
// bussy indicator
ColumnLayout{
Layout.fillWidth: true
spacing: _margin
BusyIndicator{
id: indicator
property bool calculating: missionItem.calculating
running: calculating
visible: calculating || timer.running
onCalculatingChanged: {
if(!calculating){
// defer hiding
timer.restart()
}
}
Timer{
id: timer
interval: 1000
repeat: false
running: false
}
}
} // indicator column
function _addGeneratorEditor(){
if (_generator.editorQml && !_generatorEditor) {
var component = Qt.createComponent(_generator.editorQml)
if (component.status === Component.Error) {
console.log("Error loading Qml: ",
_generator.editorQml, component.errorString())
} else {
_generatorEditor =
component.createObject(
generatorEditorParent, {
"generator": root._generator,
"availableWidth": generatorEditorParent.width,
})
}
}
}
function _destroyGeneratorEditor(){
if (_generatorEditor){
_generatorEditor.destroy()
_generatorEditor = undefined
}
}
}
import QtQml 2.2
import QtQuick 2.0
import QtLocation 5.3
MapQuickItem {
id: root
width: 15
height: 15
anchorPoint.x: width/2
anchorPoint.y: height/2
property color primaryColor: "orange"
property color secondaryColor: "lightblue"
property real centerWidth: width / 2
property real centerHeight: height / 2
property real radius: Math.min(canvas.width, canvas.height) / 2
property real minimumValue: 0
property real maximumValue: 100
property real currentValue: 33
// this is the angle that splits the circle in two arcs
// first arc is drawn from 0 radians to angle radians
// second arc is angle radians to 2*PI radians
property real angle: (currentValue - minimumValue) / (maximumValue - minimumValue) * 2 * Math.PI
// we want both circle to start / end at 12 o'clock
// without this offset we would start / end at 9 o'clock
property real angleOffset: -Math.PI / 2
property string text: ""
signal clicked()
onPrimaryColorChanged: canvas.requestPaint()
onSecondaryColorChanged: canvas.requestPaint()
onMinimumValueChanged: canvas.requestPaint()
onMaximumValueChanged: canvas.requestPaint()
onCurrentValueChanged: canvas.requestPaint()
// draws two arcs (portion of a circle)
// fills the circle with a lighter secondary color
// when pressed
sourceItem: Canvas {
id: canvas
width: root.width
height: root.height
antialiasing: true
onPaint: {
var ctx = getContext("2d");
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
// fills the mouse area when pressed
// the fill color is a lighter version of the
// secondary color
if (mouseArea.pressed) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = Qt.lighter(root.secondaryColor, 1.25);
ctx.arc(root.centerWidth,
root.centerHeight,
root.radius,
0,
2*Math.PI);
ctx.fill();
}
// First, thinner arc
// From angle to 2*PI
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = primaryColor;
ctx.arc(root.centerWidth,
root.centerHeight,
root.radius,
angleOffset + root.angle,
angleOffset + 2*Math.PI);
ctx.stroke();
// Second, thicker arc
// From 0 to angle
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = root.secondaryColor;
ctx.arc(root.centerWidth,
root.centerHeight,
root.radius,
root.angleOffset,
root.angleOffset + root.angle);
ctx.stroke();
ctx.restore();
}
Text {
anchors.centerIn: parent
text: root.text
color: root.primaryColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: root.clicked()
onPressedChanged: canvas.requestPaint()
}
}
}
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Extras 1.4
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
// Editor for Operating Area items
Rectangle {
id: _root
height: visible ? (editorColumn.height + (_margin * 2)) : 0
GridLayout {
id: editorColumn
columns: 2
columnSpacing: _margin
rowSpacing: _margin
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5
property bool polygonInteractive: areaItem.interactive
property var polygon: areaItem
property bool initNecesarry: true
QGCPalette { id: qgcPal; colorGroupEnabled: true }
Column {
id: editorColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
SectionHeader {
id: scanHeader
text: qsTr("Settings")
}
Column {
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
visible: scanHeader.checked
GridLayout {
anchors.left: parent.left
anchors.right: parent.right
columnSpacing: _margin
rowSpacing: _margin
columns: 2
QGCLabel { text: qsTr("Offset") }
FactTextField {
fact: areaItem.borderPolygonOffset
Layout.fillWidth: true
}
}
Item {
height: ScreenTools.defaultFontPixelHeight / 2
width: 1
}
}
FactCheckBox {
text: qsTr("Border Polygon")
fact: areaItem.showBorderPolygon
//enabled: !missionItem.followTerrain
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
}
Grid {
columns: 2
columnSpacing: ScreenTools.defaultFontPixelWidth
visible: statsHeader.checked
QGCLabel { text: qsTr("Area") }
QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(areaItem.area).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }
}
} // Column
} // Rectangle
property var geoArea: undefined
property int availableWidth
property real _margin: ScreenTools.defaultFontPixelWidth / 2
Component.onCompleted: {
console.assert(geoArea !== undefined, "please set the areaItem property")
}
SectionHeader {
id: statsHeader
text: qsTr("Statistics")
Layout.fillWidth: true
Layout.columnSpan: 2
}
GridLayout {
columns: 2
columnSpacing: _margin
rowSpacing: _margin
visible: statsHeader.checked
Layout.fillWidth: true
Layout.columnSpan: 2
QGCLabel { text: qsTr("Area") }
//QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(geoArea.area).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString }
}
}
......@@ -24,36 +24,13 @@ Item {
property var map: undefined ///< Map control to place item in
property var geoArea: undefined ///< GeoArea derived class.
property var _depotVisual: undefined
property var _depotDrag: undefined
property bool _showDepot: geoArea.interactive
opacity: 0.3
signal clicked(int sequenceNumber)
on_ShowDepotChanged: {
if (_showDepot){
_addDepotVisual()
_addDepotDrag()
} else {
_destroyDepotVisual()
_destroyDepotDrag()
}
}
Component.onCompleted: {
if (_showDepot){
_addDepotVisual()
_addDepotDrag()
}
console.assert(map !== undefined, "please set the map property")
console.assert(geoArea !== undefined, "please set the geoArea property")
}
Component.onDestruction: {
_destroyDepotVisual()
}
// Area polygon
QGCMapPolygonVisuals {
......@@ -65,78 +42,61 @@ Item {
borderColor: "blue"
interiorColor: "blue"
altColor: QGroundControl.globalPalette.surveyPolygonTerrainCollision
interiorOpacity: _root.opacity
z: QGroundControl.zOrderMapItems-1
interiorOpacity: 0.3
}
Loader {
id:depotLoader
sourceComponent: depotComponent
}
// Depot Point.
Component {
id: depotPointComponent
MapQuickItem {
coordinate: _root.geoArea.depot
anchorPoint.x: sourceItem.anchorPointX
anchorPoint.y: sourceItem.anchorPointY
visible: true
sourceItem:
MissionItemIndexLabel {
checked: geoArea.interactive
label: qsTr("Launch")
highlightSelected: true
onClicked: _root.clicked(0)
visible: true
id: depotComponent
Item {
id: depotMapItem
MapQuickItem {
id: mapItem
coordinate: _root.geoArea.depot
anchorPoint.x: sourceItem.anchorPointX
anchorPoint.y: sourceItem.anchorPointY
visible: true
z: QGroundControl.zOrderMapItems
Component.onCompleted: {
coordinate = Qt.binding(function(){return _root.geoArea.depot})
}
}
}
Component {
id: depotDragComponent
MissionItemIndicatorDrag {
mapControl: _root.map
itemIndicator: _depot
itemCoordinate: geoArea.depot
visible: geoArea.interactive
property var depot: geoArea.depot
onItemCoordinateChanged: {
if (itemCoordinate.latitude !== depot.latitude ||
itemCoordinate.longitude !== depot.longitude){
if (_root.areaItem.containsCoordinate(itemCoordinate)){
_root.areaItem.depot = itemCoordinate
}
sourceItem:
MissionItemIndexLabel {
checked: true
label: qsTr("Depot")
highlightSelected: true
onClicked: _root.clicked(0)
visible: mapItem.visible
z: mapItem.z
}
itemCoordinate = Qt.binding(function(){return _root.geoArea.depot})
}
}
}
function _addDepotVisual() {
if (!_depotVisual){
_depotVisual = depotPointComponent.createObject(_root)
map.addMapItem(_depotVisual)
}
}
function _destroyDepotVisual() {
if (_depotVisual){
map.removeMapItem(_depotVisual)
_depotVisual.destroy()
_depotVisual = undefined
}
}
ItemDragger {
anchor: mapItem
z: QGroundControl.zOrderMapItems+1
draggable: _root.geoArea.interactive
function _addDepotDrag() {
if (!_depotDrag){
_depotDrag = depotDragComponent.createObject(_root)
}
}
onDragStop:{
_root.geoArea.depot = mapItem.coordinate
mapItem.coordinate = Qt.binding(function(){return _root.geoArea.depot})
}
}
function _destroyDepotDrag() {
if (_depotDrag){
_depotDrag.destroy()
_depotDrag = undefined
Component.onCompleted: {
_root.map.addMapItem(mapItem)
}
}
}
}
This diff is collapsed.
This diff is collapsed.
......@@ -113,6 +113,4 @@ MAVLinkChart 1.0 MAVLinkChart.qml
MeasurementItemMapVisual 1.0 MeasurementItemMapVisual.qml
CircularGeneratorMapVisual 1.0 CircularGeneratorMapVisual.qml
GeoAreaVisualLoader 1.0 GeoAreaVisualLoader.qml
DragCoordinate 1.0 DragCoordinate.qml
CoordinateIndicator 1.0 CoordinateIndicator.qml
CoordinateIndicatorDrag 1.0 CoordinateIndicatorDrag.qml
ItemDragger 1.0 ItemDragger.qml
......@@ -7,87 +7,91 @@
*
****************************************************************************/
#ifndef QmlObjectListModel_H
#define QmlObjectListModel_H
#include <QAbstractListModel>
class QmlObjectListModel : public QAbstractListModel
{
Q_OBJECT
class QmlObjectListModel : public QAbstractListModel {
Q_OBJECT
public:
QmlObjectListModel(QObject* parent = nullptr);
~QmlObjectListModel() override;
Q_PROPERTY(int count READ count NOTIFY countChanged)
/// Returns true if any of the items in the list are dirty. Requires each object to have
/// a dirty property and dirtyChanged signal.
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_INVOKABLE QObject* get(int index);
// Property accessors
int count () const;
bool dirty () const { return _dirty; }
void setDirty (bool dirty);
void append (QObject* object);
void append (QList<QObject*> objects);
QObjectList swapObjectList (const QObjectList& newlist);
void clear ();
QObject* removeAt (int i);
QObject* removeOne (QObject* object) { return removeAt(indexOf(object)); }
void insert (int i, QObject* object);
void insert (int i, QList<QObject*> objects);
bool contains (QObject* object) { return _objectList.indexOf(object) != -1; }
int indexOf (QObject* object) { return _objectList.indexOf(object); }
/// Moves an item to a new position
void move(int from, int to);
QObject* operator[] (int i);
const QObject* operator[] (int i) const;
template<class T> T value (int index) { return qobject_cast<T>(_objectList[index]); }
QList<QObject*>* objectList () { return &_objectList; }
/// Calls deleteLater on all items and this itself.
void deleteListAndContents ();
/// Clears the list and calls deleteLater on each entry
void clearAndDeleteContents ();
void beginReset ();
void endReset ();
QmlObjectListModel(QObject *parent = nullptr);
~QmlObjectListModel() override;
Q_PROPERTY(int count READ count NOTIFY countChanged)
/// Returns true if any of the items in the list are dirty. Requires each
/// object to have a dirty property and dirtyChanged signal.
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_INVOKABLE QObject *get(int index);
// Property accessors
int count() const;
bool dirty() const { return _dirty; }
void setDirty(bool dirty);
void append(QObject *object);
void append(QList<QObject *> objects);
QObjectList swapObjectList(const QObjectList &newlist);
void clear();
QObject *removeAt(int i);
QObject *removeOne(QObject *object) { return removeAt(indexOf(object)); }
void insert(int i, QObject *object);
void insert(int i, QList<QObject *> objects);
bool contains(QObject *object) { return _objectList.indexOf(object) != -1; }
int indexOf(QObject *object) { return _objectList.indexOf(object); }
/// Moves an item to a new position
void move(int from, int to);
QObject *operator[](int i);
const QObject *operator[](int i) const;
template <class T> T value(int index) {
return qobject_cast<T>(_objectList[index]);
}
QList<QObject *> *objectList() { return &_objectList; }
/// Calls deleteLater on all items and this itself.
void deleteListAndContents();
/// Clears the list and calls deleteLater on each entry
void clearAndDeleteContents();
void beginReset();
void endReset();
signals:
void countChanged (int count);
void dirtyChanged (bool dirtyChanged);
void countChanged(int count);
void dirtyChanged(bool dirtyChanged);
private slots:
void _childDirtyChanged (bool dirty);
void _childDirtyChanged(bool dirty);
private:
// Overrides from QAbstractListModel
int rowCount (const QModelIndex & parent = QModelIndex()) const override;
QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const override;
bool insertRows (int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows (int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QHash<int, QByteArray> roleNames(void) const override;
// Overrides from QAbstractListModel
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index,
int role = Qt::DisplayRole) const override;
bool insertRows(int position, int rows,
const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows,
const QModelIndex &index = QModelIndex()) override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
QHash<int, QByteArray> roleNames(void) const override;
private:
QList<QObject*> _objectList;
bool _dirty;
bool _skipDirtyFirstItem;
bool _externalBeginResetModel;
static const int ObjectRole;
static const int TextRole;
QList<QObject *> _objectList;
bool _dirty;
bool _skipDirtyFirstItem;
bool _externalBeginResetModel;
static const int ObjectRole;
static const int TextRole;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment