2 ******************************************************************************
4 * @file lineardialgadgetwidget.cpp
5 * @author Edouard Lafargue Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup LinearDialPlugin Linear Dial Plugin
10 * @brief Implements a gadget that displays linear gauges and generic indicators
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "lineardialgadgetwidget.h"
29 #include <utils/stylehelper.h>
30 #include <QFileDialog>
31 #include <QOpenGLWidget>
34 LineardialGadgetWidget::LineardialGadgetWidget(QWidget
*parent
) : QGraphicsView(parent
)
36 setMinimumSize(32, 32);
37 setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
38 setScene(new QGraphicsScene(this));
39 setRenderHints(QPainter::Antialiasing
| QPainter::TextAntialiasing
);
40 m_renderer
= new QSvgRenderer();
53 // This timer mechanism makes the index rotate smoothly
54 connect(&dialTimer
, SIGNAL(timeout()), this, SLOT(moveIndex()));
58 LineardialGadgetWidget::~LineardialGadgetWidget()
64 \brief Enables/Disables OpenGL
66 void LineardialGadgetWidget::enableOpenGL(bool flag
)
69 setViewport(new QOpenGLWidget()); // QGLFormat(QGL::SampleBuffers)));
71 setViewport(new QWidget
);
76 \brief Connects the widget to the relevant UAVObjects
78 void LineardialGadgetWidget::connectInput(QString object1
, QString nfield1
)
81 disconnect(obj1
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateIndex(UAVObject
*)));
83 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
84 UAVObjectManager
*objManager
= pm
->getObject
<UAVObjectManager
>();
86 // qDebug() << "Lineardial Connect needles - " << object1 << "-"<< nfield1;
88 // Check validity of arguments first, reject empty args and unknown fields.
89 if (!(object1
.isEmpty() || nfield1
.isEmpty())) {
90 obj1
= dynamic_cast<UAVDataObject
*>(objManager
->getObject(object1
));
92 connect(obj1
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateIndex(UAVObject
*)));
93 if (nfield1
.contains("-")) {
94 QStringList fieldSubfield
= nfield1
.split("-", QString::SkipEmptyParts
);
95 field1
= fieldSubfield
.at(0);
96 subfield1
= fieldSubfield
.at(1);
100 haveSubField1
= false;
103 fieldName
->setPlainText(nfield1
);
107 qDebug() << "Error: Object is unknown (" << object1
<< ") this should not happen.";
113 \brief Called by the UAVObject which got updated
115 Updates the numeric value and/or the icon if the dial wants this.
117 void LineardialGadgetWidget::updateIndex(UAVObject
*object1
)
119 // Double check that the field exists:
120 UAVObjectField
*field
= object1
->getField(field1
);
124 if (field
->isNumeric()) {
127 int indexOfSubField
= field
->getElementNames().indexOf(QRegExp(subfield1
, Qt::CaseSensitive
, QRegExp::FixedString
));
128 v
= field
->getDouble(indexOfSubField
) * factor
;
130 v
= field
->getDouble() * factor
;
133 s
.sprintf("%.*f", places
, v
);
135 if (field
->isText()) {
136 s
= field
->getValue().toString();
138 // If we defined a symbol, we will look for a matching
139 // SVG element to display:
140 if (m_renderer
->elementExists("symbol-" + s
)) {
141 fieldSymbol
->setElementId("symbol-" + s
);
143 fieldSymbol
->setElementId("symbol");
149 fieldValue
->setPlainText(s
);
152 if (index
&& !dialTimer
.isActive()) {
156 qDebug() << "Wrong field, maybe an issue with object disconnection ?";
161 \brief Setup dial using its master SVG template.
163 Should only be called after the min/max ranges have been set.
166 void LineardialGadgetWidget::setDialFile(QString dfn
)
168 QGraphicsScene
*l_scene
= scene();
170 setBackgroundBrush(QBrush(Utils::StyleHelper::baseColor()));
171 if (QFile::exists(dfn
) && m_renderer
->load(dfn
) && m_renderer
->isValid()) {
172 l_scene
->clear(); // Beware: clear also deletes all objects
173 // which are currently in the scene
174 background
= new QGraphicsSvgItem();
175 background
->setSharedRenderer(m_renderer
);
176 background
->setElementId("background");
177 background
->setFlags(QGraphicsItem::ItemClipsChildrenToShape
|
178 QGraphicsItem::ItemClipsToShape
);
179 l_scene
->addItem(background
);
181 // The red/yellow/green zones are optional, we just
182 // test on the presence of "red"
183 if (m_renderer
->elementExists("red")) {
184 // Order is important: red, then yellow then green
185 // overlayed on top of each other
186 red
= new QGraphicsSvgItem();
187 red
->setSharedRenderer(m_renderer
);
188 red
->setElementId("red");
189 red
->setParentItem(background
);
190 yellow
= new QGraphicsSvgItem();
191 yellow
->setSharedRenderer(m_renderer
);
192 yellow
->setElementId("yellow");
193 yellow
->setParentItem(background
);
194 green
= new QGraphicsSvgItem();
195 green
->setSharedRenderer(m_renderer
);
196 green
->setElementId("green");
197 green
->setParentItem(background
);
198 // In order to properly render the Green/Yellow/Red graphs, we need to find out
199 // the starting location of the bargraph rendering area:
200 QMatrix textMatrix
= m_renderer
->matrixForElement("bargraph");
201 qreal bgX
= textMatrix
.mapRect(m_renderer
->boundsOnElement("bargraph")).x();
202 qreal bgY
= textMatrix
.mapRect(m_renderer
->boundsOnElement("bargraph")).y();
203 bargraphSize
= textMatrix
.mapRect(m_renderer
->boundsOnElement("bargraph")).width();
204 // Detect if the bargraph is vertical or horizontal.
205 qreal bargraphHeight
= textMatrix
.mapRect(m_renderer
->boundsOnElement("bargraph")).height();
206 if (bargraphHeight
> bargraphSize
) {
208 bargraphSize
= bargraphHeight
;
210 verticalDial
= false;
212 // Now adjust the red/yellow/green zones:
213 double range
= maxValue
- minValue
;
215 green
->resetTransform();
216 double greenScale
= (greenMax
- greenMin
) / range
;
217 double greenStart
= verticalDial
? (maxValue
- greenMax
) / range
* green
->boundingRect().height() :
218 (greenMin
- minValue
) / range
* green
->boundingRect().width();
222 matrix
.scale(1, greenScale
);
223 matrix
.translate(bgX
, (greenStart
+ bgY
) / greenScale
);
225 matrix
.scale(greenScale
, 1);
226 matrix
.translate((greenStart
+ bgX
) / greenScale
, bgY
);
228 green
->setTransform(matrix
, false);
230 yellow
->resetTransform();
231 double yellowScale
= (yellowMax
- yellowMin
) / range
;
232 double yellowStart
= verticalDial
? (maxValue
- yellowMax
) / range
* yellow
->boundingRect().height() :
233 (yellowMin
- minValue
) / range
* yellow
->boundingRect().width();
236 matrix
.scale(1, yellowScale
);
237 matrix
.translate(bgX
, (yellowStart
+ bgY
) / yellowScale
);
239 matrix
.scale(yellowScale
, 1);
240 matrix
.translate((yellowStart
+ bgX
) / yellowScale
, bgY
);
242 yellow
->setTransform(matrix
, false);
244 red
->resetTransform();
245 double redScale
= (redMax
- redMin
) / range
;
246 double redStart
= verticalDial
? (maxValue
- redMax
) / range
* red
->boundingRect().height() :
247 (redMin
- minValue
) / range
* red
->boundingRect().width();
250 matrix
.scale(1, redScale
);
251 matrix
.translate(bgX
, (redStart
+ bgY
) / redScale
);
253 matrix
.scale(redScale
, 1);
254 matrix
.translate((redStart
+ bgX
) / redScale
, bgY
);
256 red
->setTransform(matrix
, false);
263 // Check whether the dial wants to display a moving index:
264 if (m_renderer
->elementExists("needle")) {
265 QMatrix textMatrix
= m_renderer
->matrixForElement("needle");
266 QRectF nRect
= textMatrix
.mapRect(m_renderer
->boundsOnElement("needle"));
270 matrix
.translate(startX
, startY
);
271 index
= new QGraphicsSvgItem();
272 index
->setSharedRenderer(m_renderer
);
273 index
->setElementId("needle");
274 index
->setTransform(matrix
, false);
275 index
->setParentItem(background
);
280 // Check whether the dial wants display its field name:
281 if (m_renderer
->elementExists("field")) {
282 QMatrix textMatrix
= m_renderer
->matrixForElement("field");
283 QRectF rect
= textMatrix
.mapRect(m_renderer
->boundsOnElement("field"));
284 qreal startX
= rect
.x();
285 qreal startY
= rect
.y();
286 qreal elHeight
= rect
.height();
288 matrix
.translate(startX
, startY
- elHeight
/ 2);
289 fieldName
= new QGraphicsTextItem("field");
290 fieldName
->setFont(QFont("Arial", (int)elHeight
));
291 fieldName
->setDefaultTextColor(QColor("White"));
292 fieldName
->setTransform(matrix
, false);
293 fieldName
->setParentItem(background
);
298 // Check whether the dial wants display the numeric value:
299 if (m_renderer
->elementExists("value")) {
300 QMatrix textMatrix
= m_renderer
->matrixForElement("value");
301 QRectF nRect
= textMatrix
.mapRect(m_renderer
->boundsOnElement("value"));
302 qreal startX
= nRect
.x();
303 qreal startY
= nRect
.y();
304 qreal elHeight
= nRect
.height();
306 matrix
.translate(startX
, startY
- elHeight
/ 2);
307 fieldValue
= new QGraphicsTextItem("0.00");
308 fieldValue
->setFont(QFont("Arial", (int)elHeight
));
309 fieldValue
->setDefaultTextColor(QColor("White"));
310 fieldValue
->setTransform(matrix
, false);
311 fieldValue
->setParentItem(background
);
316 // Check whether the dial wants to display the value as a
317 // symbol (only works for text values):
318 if (m_renderer
->elementExists("symbol")) {
319 QMatrix textMatrix
= m_renderer
->matrixForElement("symbol");
320 qreal startX
= textMatrix
.mapRect(m_renderer
->boundsOnElement("symbol")).x();
321 qreal startY
= textMatrix
.mapRect(m_renderer
->boundsOnElement("symbol")).y();
323 matrix
.translate(startX
, startY
);
324 fieldSymbol
= new QGraphicsSvgItem();
325 fieldSymbol
->setElementId("symbol");
326 fieldSymbol
->setSharedRenderer(m_renderer
);
327 fieldSymbol
->setTransform(matrix
, false);
328 fieldSymbol
->setParentItem(background
);
333 if (m_renderer
->elementExists("foreground")) {
334 foreground
= new QGraphicsSvgItem();
335 foreground
->setSharedRenderer(m_renderer
);
336 foreground
->setElementId("foreground");
337 foreground
->setParentItem(background
);
343 l_scene
->setSceneRect(background
->boundingRect());
345 // Reset the current index value:
347 if (!dialTimer
.isActive() && index
) {
351 qDebug() << "no file ";
352 m_renderer
->load(QString(":/lineardial/images/empty.svg"));
353 l_scene
->clear(); // This also deletes all items contained in the scene.
354 background
= new QGraphicsSvgItem();
355 background
->setSharedRenderer(m_renderer
);
356 l_scene
->addItem(background
);
365 void LineardialGadgetWidget::setDialFont(QString fontProps
)
367 // Note: a bit of juggling to preserve the automatic
368 // font size which was calculated upon dial initialization.
369 QFont font
= QFont("Arial", 12);
371 font
.fromString(fontProps
);
373 int fieldSize
= fieldName
->font().pointSize();
374 font
.setPointSize(fieldSize
);
375 fieldName
->setFont(font
);
378 int fieldSize
= fieldValue
->font().pointSize();
379 font
.setPointSize(fieldSize
);
380 fieldValue
->setFont(font
);
385 void LineardialGadgetWidget::paint()
390 void LineardialGadgetWidget::paintEvent(QPaintEvent
*event
)
392 // Skip painting until the dial file is loaded
393 if (!m_renderer
->isValid()) {
394 qDebug() << "Dial file not loaded, not rendering";
397 QGraphicsView::paintEvent(event
);
400 // This event enables the dial to be dynamically resized
401 // whenever the gadget is resized, taking advantage of the vector
402 // nature of SVG dials.
403 void LineardialGadgetWidget::resizeEvent(QResizeEvent
*event
)
406 fitInView(background
, Qt::KeepAspectRatio
);
409 // Converts the value into an percentage:
410 // this enables smooth movement in moveIndex below
411 void LineardialGadgetWidget::setIndex(double value
)
414 indexTarget
= 100 * (maxValue
- value
) / (maxValue
- minValue
);
416 indexTarget
= 100 * (value
- minValue
) / (maxValue
- minValue
);
420 // Take an input value and move the index accordingly
421 // Move is smooth, starts fast and slows down when
422 // approaching the target.
423 void LineardialGadgetWidget::moveIndex()
425 if (!index
) { // Safeguard
429 if ((abs(int((indexValue
- indexTarget
) * 10)) > 3)) {
430 indexValue
+= (indexTarget
- indexValue
) / 5;
432 indexValue
= indexTarget
;
436 index
->resetTransform();
437 qreal trans
= indexValue
* bargraphSize
/ 100;
439 matrix
.translate(startX
, trans
+ startY
);
441 matrix
.translate(trans
+ startX
, startY
);
443 index
->setTransform(matrix
, false);