1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-03-15 07:29:15 +01:00

OP-60: Analog dials gadget can now also display the numeric value on the dial if the dial is configured to do so (just add an element called with the same name as the needle + "-text" and the text will be displayed there). Removed unnecessary update() calls which slowed the gadget down. Wiki documentation to follow...

git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@897 ebee16cc-31ac-478f-84a7-5cbb03baadba
This commit is contained in:
edouard 2010-06-26 13:44:42 +00:00 committed by edouard
parent 0cc894c94a
commit 6373bec4dd
8 changed files with 601 additions and 459 deletions

View File

@ -61,6 +61,7 @@ void AirspeedGadget::loadConfiguration(IUAVGadgetConfiguration* config)
m_widget->setN3Min(m->getN3Min());
m_widget->setN3Max(m->getN3Max());
m_widget->setN3Factor(m->getN3Factor());
m_widget->setDialFont(m->getFont());
m_widget->connectNeedles(m->getN1DataObject(),m->getN1ObjField(),
m->getN2DataObject(),m->getN2ObjField(),
m->getN3DataObject(),m->getN3ObjField()

View File

@ -82,6 +82,8 @@ AirspeedGadgetConfiguration::AirspeedGadgetConfiguration(QString classId, const
stream >> needle1Move;
stream >> needle2Move;
stream >> needle3Move;
stream >> font;
}
}
/**
@ -126,6 +128,7 @@ QByteArray AirspeedGadgetConfiguration::saveState() const
stream << needle1Move;
stream << needle2Move;
stream << needle3Move;
stream << font;
return bytes;
}

View File

@ -66,6 +66,8 @@ public:
void setN1Move( QString move) { needle1Move = move; }
void setN2Move( QString move) { needle2Move = move; }
void setN3Move( QString move) { needle3Move = move; }
void setFont(QString text) { font = text; }
//get dial configuration functions
QString dialFile() {return m_defaultDial;}
@ -92,6 +94,7 @@ public:
QString getN1Move() { return needle1Move; }
QString getN2Move() { return needle2Move; }
QString getN3Move() { return needle3Move; }
QString getFont() { return font;}
QByteArray saveState() const;
IUAVGadgetConfiguration *clone();
@ -115,6 +118,9 @@ private:
double needle2Factor;
double needle3Factor;
// The font used for the dial
QString font;
QString needle1DataObject;
QString needle1ObjectField;
QString needle2DataObject;

View File

@ -143,6 +143,8 @@ QWidget *AirspeedGadgetOptionsPage::createPage(QWidget *parent)
connect(options_page->uavObject3, SIGNAL(currentIndexChanged(QString)), this, SLOT(on_uavObject3_currentIndexChanged(QString)));
connect(options_page->loadFile, SIGNAL(clicked()), this, SLOT(on_loadFile_clicked()));
connect(options_page->fontPicker, SIGNAL(clicked()), this, SLOT(on_fontPicker_clicked()));
return optionsPageWidget;
}
@ -178,8 +180,21 @@ void AirspeedGadgetOptionsPage::apply()
m_config->setN1Move(options_page->moveNeedle1->currentText());
m_config->setN2Move(options_page->moveNeedle2->currentText());
m_config->setN3Move(options_page->moveNeedle3->currentText());
m_config->setFont(font.toString());
}
/**
* Opens a font picker.
*
*/
void AirspeedGadgetOptionsPage::on_fontPicker_clicked()
{
bool ok;
font = QFontDialog::getFont(&ok, QFont("Arial", 12), qobject_cast<QWidget*>(this));
}
/*
Fills in the field1 combo box when value is changed in the
object1 field

View File

@ -32,6 +32,8 @@
#include "QString"
#include <QStringList>
#include <QDebug>
#include <QFont>
#include <QFontDialog>
namespace Core {
class IUAVGadgetConfiguration;
@ -58,9 +60,11 @@ public:
private:
Ui::AirspeedGadgetOptionsPage *options_page;
AirspeedGadgetConfiguration *m_config;
QFont font;
private slots:
void on_loadFile_clicked();
void on_fontPicker_clicked();
void on_uavObject1_currentIndexChanged(QString val);
void on_uavObject2_currentIndexChanged(QString val);
void on_uavObject3_currentIndexChanged(QString val);

View File

@ -28,7 +28,7 @@
<height>331</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
@ -105,6 +105,40 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<property name="topMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="label_28">
<property name="text">
<string>Dial font:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fontPicker">
<property name="text">
<string>Select...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
@ -114,7 +148,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">

View File

@ -1,457 +1,532 @@
/**
******************************************************************************
*
* @file airspeedgadgetwidget.cpp
* @author Edouard Lafargue and David Carlson Copyright (C) 2010.
* @brief
* @see The GNU Public License (GPL) Version 3
* @defgroup dialplugin
* @{
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "airspeedgadgetwidget.h"
#include <iostream>
#include <QDebug>
AirspeedGadgetWidget::AirspeedGadgetWidget(QWidget *parent) : QGraphicsView(parent)
{
// TODO: create a proper "needle" object instead of hardcoding all this
// which is ugly (but easy).
setMinimumSize(64,64);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
setScene(new QGraphicsScene(this));
setRenderHints(QPainter::Antialiasing);
m_renderer = new QSvgRenderer();
obj1 = NULL;
obj2 = NULL;
obj3 = NULL;
needle1Target = 0;
needle2Target = 0;
needle3Target = 0;
// This timer mechanism makes needles rotate smoothly
connect(&dialTimer, SIGNAL(timeout()), this, SLOT(rotateNeedles()));
}
AirspeedGadgetWidget::~AirspeedGadgetWidget()
{
// Do nothing
}
/*!
\brief Connects the widget to the relevant UAVObjects
*/
void AirspeedGadgetWidget::connectNeedles(QString object1, QString nfield1,
QString object2, QString nfield2,
QString object3, QString nfield3) {
if (obj1 != NULL)
disconnect(obj1,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle1(UAVObject*)));
if (obj2 != NULL)
disconnect(obj2,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle2(UAVObject*)));
if (obj3 != NULL)
disconnect(obj3,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle3(UAVObject*)));
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
// Check validity of arguments first, reject empty args and unknown fields.
if (!(object1.isEmpty() || nfield1.isEmpty())) {
obj1 = dynamic_cast<UAVDataObject*>( objManager->getObject(object1) );
if (obj1 != NULL ) {
std::cout << "Connected Object 1 (" << object1.toStdString() << ")." << std::endl;
connect(obj1, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle1(UAVObject*)));
field1 = nfield1;
} else {
std::cout << "Error: Object is unknown (" << object1.toStdString() << ")." << std::endl;
}
}
// And do the same for the second needle.
if (!(object2.isEmpty() || nfield2.isEmpty())) {
obj2 = dynamic_cast<UAVDataObject*>( objManager->getObject(object2) );
if (obj2 != NULL ) {
std::cout << "Connected Object 2 (" << object2.toStdString() << ")." << std::endl;
connect(obj2, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle2(UAVObject*)));
field2 = nfield2;
} else {
std::cout << "Error: Object is unknown (" << object2.toStdString() << ")." << std::endl;
}
}
// And do the same for the third needle.
if (!(object3.isEmpty() || nfield3.isEmpty())) {
obj3 = dynamic_cast<UAVDataObject*>( objManager->getObject(object3) );
if (obj3 != NULL ) {
std::cout << "Connected Object 3 (" << object3.toStdString() << ")." << std::endl;
connect(obj3, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle3(UAVObject*)));
field3 = nfield3;
} else {
std::cout << "Error: Object is unknown (" << object3.toStdString() << ")." << std::endl;
}
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle1(UAVObject *object1) {
// Double check that the field exists:
UAVObjectField* field = object1->getField(field1);
if (field) {
setNeedle1(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle2(UAVObject *object2) {
UAVObjectField* field = object2->getField(field2);
if (field) {
setNeedle2(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle3(UAVObject *object3) {
UAVObjectField* field = object3->getField(field3);
if (field) {
setNeedle3(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*
Initializes the dial file, and does all the one-time calculations for
display later.
*/
void AirspeedGadgetWidget::setDialFile(QString dfn, QString bg, QString fg, QString n1, QString n2,
QString n3, QString n1Move, QString n2Move, QString n3Move)
{
if (QFile::exists(dfn))
{
m_renderer->load(dfn);
if(m_renderer->isValid())
{
fgenabled = false;
n2enabled = false;
n3enabled = false;
QGraphicsScene *l_scene = scene();
l_scene->clear(); // Deletes all items contained in the scene as well.
m_background = new QGraphicsSvgItem();
// All other items will be clipped to the shape of the background
m_background->setFlags(QGraphicsItem::ItemClipsChildrenToShape|
QGraphicsItem::ItemClipsToShape);
m_foreground = new QGraphicsSvgItem();
m_needle1 = new QGraphicsSvgItem();
m_needle2 = new QGraphicsSvgItem();
m_needle3 = new QGraphicsSvgItem();
m_needle1->setParentItem(m_background);
m_needle2->setParentItem(m_background);
m_needle3->setParentItem(m_background);
m_foreground->setParentItem(m_background);
// We assume the dial contains at least the background
// and needle1
m_background->setSharedRenderer(m_renderer);
m_background->setElementId(bg);
l_scene->addItem(m_background);
m_needle1->setSharedRenderer(m_renderer);
m_needle1->setElementId(n1);
l_scene->addItem(m_needle1);
// The dial gadget allows Needle1 and Needle2 to be
// the same element, for combined movement. Needle3
// is always independent.
if (n1 == n2) {
m_needle2 = m_needle1;
n2enabled = true;
} else {
if (m_renderer->elementExists(n2)) {
m_needle2->setSharedRenderer(m_renderer);
m_needle2->setElementId(n2);
l_scene->addItem(m_needle2);
n2enabled = true;
}
}
if (m_renderer->elementExists(n3)) {
m_needle3->setSharedRenderer(m_renderer);
m_needle3->setElementId(n3);
l_scene->addItem(m_needle3);
n3enabled = true;
}
if (m_renderer->elementExists(fg)) {
m_foreground->setSharedRenderer(m_renderer);
m_foreground->setElementId(fg);
l_scene->addItem(m_foreground);
fgenabled = true;
}
rotateN1 = false;
horizN1 = false;
vertN1 = false;
rotateN2 = false;
horizN2 = false;
vertN2 = false;
rotateN3 = false;
horizN3 = false;
vertN3 = false;
// Now setup the rotation/translation settings:
// this is UGLY UGLY UGLY, sorry...
if (n1Move.contains("Rotate")) {
rotateN1 = true;
} else if (n1Move.contains("Horizontal")) {
horizN1 = true;
} else if (n1Move.contains("Vertical")) {
vertN1 = true;
}
if (n2Move.contains("Rotate")) {
rotateN2 = true;
} else if (n2Move.contains("Horizontal")) {
horizN2 = true;
} else if (n2Move.contains("Vertical")) {
vertN2 = true;
}
if (n3Move.contains("Rotate")) {
rotateN3 = true;
} else if (n3Move.contains("Horizontal")) {
horizN3 = true;
} else if (n3Move.contains("Vertical")) {
vertN3 = true;
}
l_scene->setSceneRect(m_background->boundingRect());
// Now Initialize the center for all transforms of the dial needles to the
// center of the background:
// - Move the center of the needle to the center of the background.
QRectF rectB = m_background->boundingRect();
QRectF rectN = m_needle1->boundingRect();
m_needle1->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
// - Put the transform origin point of the needle at its center.
m_needle1->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
if ((n1 != n2) && n2enabled) {
// Only do it for needle1 if it is not the same as n2
rectN = m_needle2->boundingRect();
m_needle2->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
m_needle2->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
}
if (n3enabled) {
rectN = m_needle3->boundingRect();
m_needle3->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
m_needle3->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
}
// Last: we just loaded the dial file which is by default valid for a "zero" value
// of the needles, so we have to reset the needles too upon dial file loading, otherwise
// we would end up with an offset when we change a dial file and the needle value
// is not zero at that time.
needle1Value = 0;
needle2Value = 0;
needle3Value = 0;
if (!dialTimer.isActive())
dialTimer.start();
}
}
else
{ std::cout<<"no file: "<<std::endl; }
}
void AirspeedGadgetWidget::paint()
{
update();
}
void AirspeedGadgetWidget::paintEvent(QPaintEvent *event)
{
// Skip painting until the dial file is loaded
if (! m_renderer->isValid()) {
std::cout<<"Dial file not loaded, not rendering"<<std::endl;
return;
}
QGraphicsView::paintEvent(event);
}
// This event enables the dial to be dynamically resized
// whenever the gadget is resized, taking advantage of the vector
// nature of SVG dials.
void AirspeedGadgetWidget::resizeEvent(QResizeEvent *event)
{
fitInView(m_background, Qt::KeepAspectRatio );
}
// Converts the value into an angle:
// this enables smooth rotation in rotateNeedles below
void AirspeedGadgetWidget::setNeedle1(double value) {
if (rotateN1) {
needle1Target = 360*value*n1Factor/(n1MaxValue-n1MinValue);
}
if (horizN1) {
needle1Target = value*n1Factor/(n1MaxValue-n1MinValue);
}
if (vertN1) {
needle1Target = value*n1Factor/(n1MaxValue-n1MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
}
void AirspeedGadgetWidget::setNeedle2(double value) {
if (rotateN2) {
needle2Target = 360*value*n2Factor/(n2MaxValue-n2MinValue);
}
if (horizN2) {
needle2Target = value*n2Factor/(n2MaxValue-n2MinValue);
}
if (vertN2) {
needle2Target = value*n2Factor/(n2MaxValue-n2MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
}
void AirspeedGadgetWidget::setNeedle3(double value) {
if (rotateN3) {
needle3Target = 360*value*n3Factor/(n3MaxValue-n3MinValue);
}
if (horizN3) {
needle3Target = value*n3Factor/(n3MaxValue-n3MinValue);
}
if (vertN3) {
needle3Target = value*n3Factor/(n3MaxValue-n3MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
}
// Take an input value and rotate the dial accordingly
// Rotation is smooth, starts fast and slows down when
// approaching the target.
// We aim for a 0.5 degree precision.
//
// Note: this code is valid even if needle1 and needle2 point
// to the same element.
void AirspeedGadgetWidget::rotateNeedles()
{
int dialRun = 3;
if (n2enabled) {
double needle2Diff;
if (abs((needle2Value-needle2Target)*10) > 5) {
needle2Diff =(needle2Target - needle2Value)/5;
} else {
needle2Diff = needle2Target - needle2Value;
dialRun--;
}
if (rotateN2) {
m_needle2->setRotation(m_needle2->rotation()+needle2Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN2) {
opd = QPointF(needle2Diff,0);
}
if (vertN2) {
opd = QPointF(0,needle2Diff);
}
m_needle2->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
// Since we have moved the needle, we need to move
// the transform origin point the opposite way
// so that it keeps rotating from the same point.
// (this is only useful if needle1 and needle2 are the
// same object, for combined movement such as attitude indicator).
QPointF oop = m_needle2->transformOriginPoint();
m_needle2->setTransformOriginPoint(oop.x()-opd.x(),oop.y()-opd.y());
}
needle2Value += needle2Diff;
} else {
dialRun--;
}
// We assume that needle1 always exists!
double needle1Diff;
if ((abs((needle1Value-needle1Target)*10) > 5)) {
needle1Diff = (needle1Target - needle1Value)/5;
} else {
needle1Diff = needle1Target - needle1Value;
dialRun--;
}
if (rotateN1) {
m_needle1->setRotation(m_needle1->rotation()+needle1Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN1) {
opd = QPointF(needle1Diff,0);
}
if (vertN1) {
opd = QPointF(0,needle1Diff);
}
m_needle1->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
QPointF oop = m_needle1->transformOriginPoint();
m_needle1->setTransformOriginPoint((oop.x()-opd.x()),(oop.y()-opd.y()));
}
needle1Value += needle1Diff;
if (n3enabled) {
double needle3Diff;
if ((abs((needle3Value-needle3Target)*10) > 5)) {
needle3Diff = (needle3Target - needle3Value)/5;
} else {
needle3Diff = needle3Target - needle3Value;
dialRun--;
}
if (rotateN3) {
m_needle3->setRotation(m_needle3->rotation()+needle3Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN3) {
opd = QPointF(needle3Diff,0);
}
if (vertN3) {
opd = QPointF(0,needle3Diff);
}
m_needle3->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
QPointF oop = m_needle3->transformOriginPoint();
m_needle3->setTransformOriginPoint((oop.x()-opd.x()),(oop.y()-opd.y()));
}
needle3Value += needle3Diff;
} else {
dialRun--;
}
//update();
// Now check: if dialRun is now zero, we should
// just stop the timer since all needles have finished moving
if (!dialRun) dialTimer.stop();
}
/**
******************************************************************************
*
* @file airspeedgadgetwidget.cpp
* @author Edouard Lafargue and David Carlson Copyright (C) 2010.
* @brief
* @see The GNU Public License (GPL) Version 3
* @defgroup dialplugin
* @{
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "airspeedgadgetwidget.h"
#include <iostream>
#include <QDebug>
AirspeedGadgetWidget::AirspeedGadgetWidget(QWidget *parent) : QGraphicsView(parent)
{
// TODO: create a proper "needle" object instead of hardcoding all this
// which is ugly (but easy).
setMinimumSize(64,64);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
setScene(new QGraphicsScene(this));
setRenderHints(QPainter::Antialiasing);
m_renderer = new QSvgRenderer();
obj1 = NULL;
obj2 = NULL;
obj3 = NULL;
needle1Target = 0;
needle2Target = 0;
needle3Target = 0;
// This timer mechanism makes needles rotate smoothly
connect(&dialTimer, SIGNAL(timeout()), this, SLOT(rotateNeedles()));
}
AirspeedGadgetWidget::~AirspeedGadgetWidget()
{
// Do nothing
}
/*!
\brief Connects the widget to the relevant UAVObjects
*/
void AirspeedGadgetWidget::connectNeedles(QString object1, QString nfield1,
QString object2, QString nfield2,
QString object3, QString nfield3) {
if (obj1 != NULL)
disconnect(obj1,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle1(UAVObject*)));
if (obj2 != NULL)
disconnect(obj2,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle2(UAVObject*)));
if (obj3 != NULL)
disconnect(obj3,SIGNAL(objectUpdated(UAVObject*)),this,SLOT(updateNeedle3(UAVObject*)));
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
// Check validity of arguments first, reject empty args and unknown fields.
if (!(object1.isEmpty() || nfield1.isEmpty())) {
obj1 = dynamic_cast<UAVDataObject*>( objManager->getObject(object1) );
if (obj1 != NULL ) {
std::cout << "Connected Object 1 (" << object1.toStdString() << ")." << std::endl;
connect(obj1, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle1(UAVObject*)));
field1 = nfield1;
} else {
std::cout << "Error: Object is unknown (" << object1.toStdString() << ")." << std::endl;
}
}
// And do the same for the second needle.
if (!(object2.isEmpty() || nfield2.isEmpty())) {
obj2 = dynamic_cast<UAVDataObject*>( objManager->getObject(object2) );
if (obj2 != NULL ) {
std::cout << "Connected Object 2 (" << object2.toStdString() << ")." << std::endl;
connect(obj2, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle2(UAVObject*)));
field2 = nfield2;
} else {
std::cout << "Error: Object is unknown (" << object2.toStdString() << ")." << std::endl;
}
}
// And do the same for the third needle.
if (!(object3.isEmpty() || nfield3.isEmpty())) {
obj3 = dynamic_cast<UAVDataObject*>( objManager->getObject(object3) );
if (obj3 != NULL ) {
std::cout << "Connected Object 3 (" << object3.toStdString() << ")." << std::endl;
connect(obj3, SIGNAL(objectUpdated(UAVObject*)), this, SLOT(updateNeedle3(UAVObject*)));
field3 = nfield3;
} else {
std::cout << "Error: Object is unknown (" << object3.toStdString() << ")." << std::endl;
}
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle1(UAVObject *object1) {
// Double check that the field exists:
UAVObjectField* field = object1->getField(field1);
if (field) {
setNeedle1(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle2(UAVObject *object2) {
UAVObjectField* field = object2->getField(field2);
if (field) {
setNeedle2(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*!
\brief Called by the UAVObject which got updated
*/
void AirspeedGadgetWidget::updateNeedle3(UAVObject *object3) {
UAVObjectField* field = object3->getField(field3);
if (field) {
setNeedle3(field->getDouble());
} else {
std::cout << "Wrong field, maybe an issue with object disconnection ?" << std::endl;
}
}
/*
Initializes the dial file, and does all the one-time calculations for
display later. This is the method which really initializes the dial.
*/
void AirspeedGadgetWidget::setDialFile(QString dfn, QString bg, QString fg, QString n1, QString n2,
QString n3, QString n1Move, QString n2Move, QString n3Move)
{
if (QFile::exists(dfn))
{
m_renderer->load(dfn);
if(m_renderer->isValid())
{
fgenabled = false;
n2enabled = false;
n3enabled = false;
QGraphicsScene *l_scene = scene();
l_scene->clear(); // This also deletes all items contained in the scene.
m_background = new QGraphicsSvgItem();
// All other items will be clipped to the shape of the background
m_background->setFlags(QGraphicsItem::ItemClipsChildrenToShape|
QGraphicsItem::ItemClipsToShape);
m_foreground = new QGraphicsSvgItem();
m_needle1 = new QGraphicsSvgItem();
m_needle2 = new QGraphicsSvgItem();
m_needle3 = new QGraphicsSvgItem();
m_needle1->setParentItem(m_background);
m_needle2->setParentItem(m_background);
m_needle3->setParentItem(m_background);
m_foreground->setParentItem(m_background);
// We assume the dial contains at least the background
// and needle1
m_background->setSharedRenderer(m_renderer);
m_background->setElementId(bg);
l_scene->addItem(m_background);
m_needle1->setSharedRenderer(m_renderer);
m_needle1->setElementId(n1);
// Note: no need to add the item explicitely because it
// is done automatically since it's a child item of the
// background.
//l_scene->addItem(m_needle1);
// The dial gadget allows Needle1 and Needle2 to be
// the same element, for combined movement. Needle3
// is always independent.
if (n1 == n2) {
m_needle2 = m_needle1;
n2enabled = true;
} else {
if (m_renderer->elementExists(n2)) {
m_needle2->setSharedRenderer(m_renderer);
m_needle2->setElementId(n2);
//l_scene->addItem(m_needle2);
n2enabled = true;
}
}
if (m_renderer->elementExists(n3)) {
m_needle3->setSharedRenderer(m_renderer);
m_needle3->setElementId(n3);
//l_scene->addItem(m_needle3);
n3enabled = true;
}
if (m_renderer->elementExists(fg)) {
m_foreground->setSharedRenderer(m_renderer);
m_foreground->setElementId(fg);
//l_scene->addItem(m_foreground);
fgenabled = true;
}
rotateN1 = false;
horizN1 = false;
vertN1 = false;
rotateN2 = false;
horizN2 = false;
vertN2 = false;
rotateN3 = false;
horizN3 = false;
vertN3 = false;
// Now setup the rotation/translation settings:
// this is UGLY UGLY UGLY, sorry...
if (n1Move.contains("Rotate")) {
rotateN1 = true;
} else if (n1Move.contains("Horizontal")) {
horizN1 = true;
} else if (n1Move.contains("Vertical")) {
vertN1 = true;
}
if (n2Move.contains("Rotate")) {
rotateN2 = true;
} else if (n2Move.contains("Horizontal")) {
horizN2 = true;
} else if (n2Move.contains("Vertical")) {
vertN2 = true;
}
if (n3Move.contains("Rotate")) {
rotateN3 = true;
} else if (n3Move.contains("Horizontal")) {
horizN3 = true;
} else if (n3Move.contains("Vertical")) {
vertN3 = true;
}
l_scene->setSceneRect(m_background->boundingRect());
// Now Initialize the center for all transforms of the dial needles to the
// center of the background:
// - Move the center of the needle to the center of the background.
QRectF rectB = m_background->boundingRect();
QRectF rectN = m_needle1->boundingRect();
m_needle1->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
// - Put the transform origin point of the needle at its center.
m_needle1->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
// Check whether the dial also wants display the numeric value:
if (m_renderer->elementExists(n1+"-text")) {
QMatrix textMatrix = m_renderer->matrixForElement(n1+"-text");
qreal startX = textMatrix.mapRect(m_renderer->boundsOnElement(n1+"-text")).x();
qreal startY = textMatrix.mapRect(m_renderer->boundsOnElement(n1+"-text")).y();
QTransform matrix;
matrix.translate(startX,startY);
m_text1 = new QGraphicsTextItem("0.00");
m_text1->setDefaultTextColor(QColor("White"));
m_text1->setTransform(matrix,false);
l_scene->addItem(m_text1);
} else {
m_text1 = NULL;
}
if ((n1 != n2) && n2enabled) {
// Only do it for needle2 if it is not the same as n1
rectN = m_needle2->boundingRect();
m_needle2->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
m_needle2->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
// Check whether the dial also wants display the numeric value:
if (m_renderer->elementExists(n2+"-text")) {
QMatrix textMatrix = m_renderer->matrixForElement(n2+"-text");
qreal startX = textMatrix.mapRect(m_renderer->boundsOnElement(n2+"-text")).x();
qreal startY = textMatrix.mapRect(m_renderer->boundsOnElement(n2+"-text")).y();
QTransform matrix;
matrix.translate(startX,startY);
m_text2 = new QGraphicsTextItem("0.00");
m_text2->setDefaultTextColor(QColor("White"));
m_text2->setTransform(matrix,false);
l_scene->addItem(m_text2);
} else {
m_text2 = NULL;
}
}
if (n3enabled) {
rectN = m_needle3->boundingRect();
m_needle3->setPos(rectB.width()/2-rectN.width()/2,rectB.height()/2-rectN.height()/2);
m_needle3->setTransformOriginPoint(rectN.width()/2,rectN.height()/2);
// Check whether the dial also wants display the numeric value:
if (m_renderer->elementExists(n3+"-text")) {
QMatrix textMatrix = m_renderer->matrixForElement(n3+"-text");
qreal startX = textMatrix.mapRect(m_renderer->boundsOnElement(n3+"-text")).x();
qreal startY = textMatrix.mapRect(m_renderer->boundsOnElement(n3+"-text")).y();
QTransform matrix;
matrix.translate(startX,startY);
m_text3 = new QGraphicsTextItem("0.00");
m_text3->setDefaultTextColor(QColor("White"));
m_text3->setTransform(matrix,false);
l_scene->addItem(m_text3);
} else {
m_text3 = NULL;
}
}
// Last: we just loaded the dial file which is by default positioned on a "zero" value
// of the needles, so we have to reset the needle values too upon dial file loading, otherwise
// we would end up with an offset whenever we change a dial file and the needle value
// is not zero at that time.
needle1Value = 0;
needle2Value = 0;
needle3Value = 0;
if (!dialTimer.isActive())
dialTimer.start();
}
}
else
{ std::cout<<"no file: "<<std::endl; }
}
void AirspeedGadgetWidget::paint()
{
update();
}
void AirspeedGadgetWidget::paintEvent(QPaintEvent *event)
{
// Skip painting until the dial file is loaded
if (! m_renderer->isValid()) {
std::cout<<"Dial file not loaded, not rendering"<<std::endl;
return;
}
QGraphicsView::paintEvent(event);
}
// This event enables the dial to be dynamically resized
// whenever the gadget is resized, taking advantage of the vector
// nature of SVG dials.
void AirspeedGadgetWidget::resizeEvent(QResizeEvent *event)
{
fitInView(m_background, Qt::KeepAspectRatio );
}
void AirspeedGadgetWidget::setDialFont(QString fontProps)
{
QFont font = QFont("Arial",12);
font.fromString(fontProps);
if (m_text1) {
m_text1->setFont(font);
}
}
// Converts the value into an angle:
// this enables smooth rotation in rotateNeedles below
void AirspeedGadgetWidget::setNeedle1(double value) {
if (rotateN1) {
needle1Target = 360*value*n1Factor/(n1MaxValue-n1MinValue);
}
if (horizN1) {
needle1Target = value*n1Factor/(n1MaxValue-n1MinValue);
}
if (vertN1) {
needle1Target = value*n1Factor/(n1MaxValue-n1MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
if (m_text1) {
QString s;
s.sprintf("%.2f",value*n1Factor);
m_text1->setPlainText(s);
}
}
void AirspeedGadgetWidget::setNeedle2(double value) {
if (rotateN2) {
needle2Target = 360*value*n2Factor/(n2MaxValue-n2MinValue);
}
if (horizN2) {
needle2Target = value*n2Factor/(n2MaxValue-n2MinValue);
}
if (vertN2) {
needle2Target = value*n2Factor/(n2MaxValue-n2MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
if (m_text2) {
QString s;
s.sprintf("%.2f",value*n1Factor);
m_text2->setPlainText(s);
}
}
void AirspeedGadgetWidget::setNeedle3(double value) {
if (rotateN3) {
needle3Target = 360*value*n3Factor/(n3MaxValue-n3MinValue);
}
if (horizN3) {
needle3Target = value*n3Factor/(n3MaxValue-n3MinValue);
}
if (vertN3) {
needle3Target = value*n3Factor/(n3MaxValue-n3MinValue);
}
if (!dialTimer.isActive())
dialTimer.start();
if (m_text3) {
QString s;
s.sprintf("%.2f",value*n1Factor);
m_text3->setPlainText(s);
}
}
// Take an input value and rotate the dial accordingly
// Rotation is smooth, starts fast and slows down when
// approaching the target.
// We aim for a 0.5 degree precision.
//
// Note: this code is valid even if needle1 and needle2 point
// to the same element.
void AirspeedGadgetWidget::rotateNeedles()
{
int dialRun = 3;
if (n2enabled) {
double needle2Diff;
if (abs((needle2Value-needle2Target)*10) > 5) {
needle2Diff =(needle2Target - needle2Value)/5;
} else {
needle2Diff = needle2Target - needle2Value;
dialRun--;
}
if (rotateN2) {
m_needle2->setRotation(m_needle2->rotation()+needle2Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN2) {
opd = QPointF(needle2Diff,0);
}
if (vertN2) {
opd = QPointF(0,needle2Diff);
}
m_needle2->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
// Since we have moved the needle, we need to move
// the transform origin point the opposite way
// so that it keeps rotating from the same point.
// (this is only useful if needle1 and needle2 are the
// same object, for combined movement such as attitude indicator).
QPointF oop = m_needle2->transformOriginPoint();
m_needle2->setTransformOriginPoint(oop.x()-opd.x(),oop.y()-opd.y());
}
needle2Value += needle2Diff;
} else {
dialRun--;
}
// We assume that needle1 always exists!
double needle1Diff;
if ((abs((needle1Value-needle1Target)*10) > 5)) {
needle1Diff = (needle1Target - needle1Value)/5;
} else {
needle1Diff = needle1Target - needle1Value;
dialRun--;
}
if (rotateN1) {
m_needle1->setRotation(m_needle1->rotation()+needle1Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN1) {
opd = QPointF(needle1Diff,0);
}
if (vertN1) {
opd = QPointF(0,needle1Diff);
}
m_needle1->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
QPointF oop = m_needle1->transformOriginPoint();
m_needle1->setTransformOriginPoint((oop.x()-opd.x()),(oop.y()-opd.y()));
}
needle1Value += needle1Diff;
if (n3enabled) {
double needle3Diff;
if ((abs((needle3Value-needle3Target)*10) > 5)) {
needle3Diff = (needle3Target - needle3Value)/5;
} else {
needle3Diff = needle3Target - needle3Value;
dialRun--;
}
if (rotateN3) {
m_needle3->setRotation(m_needle3->rotation()+needle3Diff);
} else {
QPointF opd = QPointF(0,0);
if (horizN3) {
opd = QPointF(needle3Diff,0);
}
if (vertN3) {
opd = QPointF(0,needle3Diff);
}
m_needle3->setTransform(QTransform::fromTranslate(opd.x(),opd.y()), true);
QPointF oop = m_needle3->transformOriginPoint();
m_needle3->setTransformOriginPoint((oop.x()-opd.x()),(oop.y()-opd.y()));
}
needle3Value += needle3Diff;
} else {
dialRun--;
}
// Now check: if dialRun is now zero, we should
// just stop the timer since all needles have finished moving
if (!dialRun) dialTimer.stop();
}

View File

@ -67,6 +67,7 @@ public:
void connectNeedles(QString object1, QString field1,
QString object2, QString field2,
QString object3, QString field3);
void setDialFont(QString fontProps);
public slots:
void updateNeedle1(UAVObject *object1); // Called by the UAVObject
@ -88,6 +89,9 @@ private:
QGraphicsSvgItem *m_needle1;
QGraphicsSvgItem *m_needle2;
QGraphicsSvgItem *m_needle3;
QGraphicsTextItem *m_text1;
QGraphicsTextItem *m_text2;
QGraphicsTextItem *m_text3;
bool n3enabled;
bool n2enabled; // Simple flag to skip rendering if the