dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
lineardialgadgetwidget.cpp
Go to the documentation of this file.
1 
13 /*
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  * for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, see <http://www.gnu.org/licenses/>
26  */
27 
28 #include <math.h>
29 
30 #include "lineardialgadgetwidget.h"
31 #include <QFileDialog>
32 #include <QDebug>
33 
35  : QGraphicsView(parent)
36 {
37  setMinimumSize(32, 32);
38  setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
39  setScene(new QGraphicsScene(this));
40  setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
41  m_renderer = new QSvgRenderer();
42  verticalDial = false;
43 
44  paint();
45 
46  obj1 = NULL;
47  fieldName = NULL;
48  fieldValue = NULL;
49  indexTarget = 0;
50  indexValue = 0;
51  places = 0;
52  factor = 1;
53 
54  // This timer mechanism makes the index rotate smoothly
55  connect(&dialTimer, SIGNAL(timeout()), this, SLOT(moveIndex()));
56  dialTimer.start(30);
57 }
58 
60 {
61  // Do nothing
62 }
63 
67 void LineardialGadgetWidget::connectInput(QString object1, QString nfield1)
68 {
69 
70  if (obj1 != NULL)
71  disconnect(obj1, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateIndex(UAVObject *)));
72  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
73  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
74 
75  // qDebug() << "Lineardial Connect needles - " << object1 << "-"<< nfield1;
76 
77  // Check validity of arguments first, reject empty args and unknown fields.
78  if (!(object1.isEmpty() || nfield1.isEmpty())) {
79  obj1 = dynamic_cast<UAVDataObject *>(objManager->getObject(object1));
80  if (obj1 != NULL) {
81  connect(obj1, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateIndex(UAVObject *)));
82  if (nfield1.contains("-")) {
83  QStringList fieldSubfield = nfield1.split("-", QString::SkipEmptyParts);
84  field1 = fieldSubfield.at(0);
85  subfield1 = fieldSubfield.at(1);
86  haveSubField1 = true;
87  } else {
88  field1 = nfield1;
89  haveSubField1 = false;
90  }
91  if (fieldName)
92  fieldName->setPlainText(nfield1);
93  updateIndex(obj1);
94 
95  } else {
96  qDebug() << "Error: Object is unknown (" << object1 << ") this should not happen.";
97  }
98  }
99 }
100 
107 {
108  // Double check that the field exists:
109  UAVObjectField *field = object1->getField(field1);
110  if (field) {
111  QString s;
112  if (field->isNumeric()) {
113  double v;
114  if (haveSubField1) {
115  int indexOfSubField = field->getElementNames().indexOf(
116  QRegExp(subfield1, Qt::CaseSensitive, QRegExp::FixedString));
117  v = field->getDouble(indexOfSubField) * factor;
118  } else
119  v = field->getDouble() * factor;
120  setIndex(v);
121  s.sprintf("%.*f", places, v);
122  }
123  if (field->isText()) {
124  s = field->getValue().toString();
125  if (fieldSymbol) {
126  // If we defined a symbol, we will look for a matching
127  // SVG element to display:
128  if (m_renderer->elementExists("symbol-" + s)) {
129  fieldSymbol->setElementId("symbol-" + s);
130  } else {
131  fieldSymbol->setElementId("symbol");
132  }
133  }
134  }
135 
136  if (fieldValue)
137  fieldValue->setPlainText(s);
138 
139  if (index && !dialTimer.isActive())
140  dialTimer.start();
141  } else {
142  qDebug() << "Wrong field, maybe an issue with object disconnection ?";
143  }
144 }
145 
153 {
154  QGraphicsScene *l_scene = scene();
155  if (QFile::exists(dfn) && m_renderer->load(dfn) && m_renderer->isValid()) {
156  l_scene->clear(); // Beware: clear also deletes all objects
157  // which are currently in the scene
158  background = new QGraphicsSvgItem();
159  background->setSharedRenderer(m_renderer);
160  background->setElementId("background");
161  background->setFlags(QGraphicsItem::ItemClipsChildrenToShape
162  | QGraphicsItem::ItemClipsToShape);
163  l_scene->addItem(background);
164 
165  // The red/yellow/green zones are optional, we just
166  // test on the presence of "red"
167  if (m_renderer->elementExists("red")) {
168  // Order is important: red, then yellow then green
169  // overlayed on top of each other
170  red = new QGraphicsSvgItem();
171  red->setSharedRenderer(m_renderer);
172  red->setElementId("red");
173  red->setParentItem(background);
174  yellow = new QGraphicsSvgItem();
175  yellow->setSharedRenderer(m_renderer);
176  yellow->setElementId("yellow");
177  yellow->setParentItem(background);
178  green = new QGraphicsSvgItem();
179  green->setSharedRenderer(m_renderer);
180  green->setElementId("green");
181  green->setParentItem(background);
182  // In order to properly render the Green/Yellow/Red graphs, we need to find out
183  // the starting location of the bargraph rendering area:
184  QMatrix textMatrix = m_renderer->matrixForElement("bargraph");
185  qreal bgX = textMatrix.mapRect(m_renderer->boundsOnElement("bargraph")).x();
186  qreal bgY = textMatrix.mapRect(m_renderer->boundsOnElement("bargraph")).y();
187  bargraphSize = textMatrix.mapRect(m_renderer->boundsOnElement("bargraph")).width();
188  // Detect if the bargraph is vertical or horizontal.
189  qreal bargraphHeight =
190  textMatrix.mapRect(m_renderer->boundsOnElement("bargraph")).height();
191  if (bargraphHeight > bargraphSize) {
192  verticalDial = true;
193  bargraphSize = bargraphHeight;
194  } else {
195  verticalDial = false;
196  }
197  // Now adjust the red/yellow/green zones:
198  double range = maxValue - minValue;
199 
200  green->resetTransform();
201  double greenScale = (greenMax - greenMin) / range;
202  double greenStart = verticalDial
203  ? (maxValue - greenMax) / range * green->boundingRect().height()
204  : (greenMin - minValue) / range * green->boundingRect().width();
205  QTransform matrix;
206  matrix.reset();
207  if (verticalDial) {
208  matrix.scale(1, greenScale);
209  matrix.translate(bgX, (greenStart + bgY) / greenScale);
210  } else {
211  matrix.scale(greenScale, 1);
212  matrix.translate((greenStart + bgX) / greenScale, bgY);
213  }
214  green->setTransform(matrix, false);
215 
216  yellow->resetTransform();
217  double yellowScale = (yellowMax - yellowMin) / range;
218  double yellowStart = verticalDial
219  ? (maxValue - yellowMax) / range * yellow->boundingRect().height()
220  : (yellowMin - minValue) / range * yellow->boundingRect().width();
221  matrix.reset();
222  if (verticalDial) {
223  matrix.scale(1, yellowScale);
224  matrix.translate(bgX, (yellowStart + bgY) / yellowScale);
225  } else {
226  matrix.scale(yellowScale, 1);
227  matrix.translate((yellowStart + bgX) / yellowScale, bgY);
228  }
229  yellow->setTransform(matrix, false);
230 
231  red->resetTransform();
232  double redScale = (redMax - redMin) / range;
233  double redStart = verticalDial
234  ? (maxValue - redMax) / range * red->boundingRect().height()
235  : (redMin - minValue) / range * red->boundingRect().width();
236  matrix.reset();
237  if (verticalDial) {
238  matrix.scale(1, redScale);
239  matrix.translate(bgX, (redStart + bgY) / redScale);
240  } else {
241  matrix.scale(redScale, 1);
242  matrix.translate((redStart + bgX) / redScale, bgY);
243  }
244  red->setTransform(matrix, false);
245 
246  } else {
247  red = NULL;
248  yellow = NULL;
249  green = NULL;
250  }
251 
252  // Check whether the dial wants to display a moving index:
253  if (m_renderer->elementExists("needle")) {
254  QMatrix textMatrix = m_renderer->matrixForElement("needle");
255  QRectF nRect = textMatrix.mapRect(m_renderer->boundsOnElement("needle"));
256  startX = nRect.x();
257  startY = nRect.y();
258  QTransform matrix;
259  matrix.translate(startX, startY);
260  index = new QGraphicsSvgItem();
261  index->setSharedRenderer(m_renderer);
262  index->setElementId("needle");
263  index->setTransform(matrix, false);
264  index->setParentItem(background);
265  } else {
266  index = NULL;
267  }
268 
269  // Check whether the dial wants display its field name:
270  if (m_renderer->elementExists("field")) {
271  QMatrix textMatrix = m_renderer->matrixForElement("field");
272  QRectF rect = textMatrix.mapRect(m_renderer->boundsOnElement("field"));
273  qreal startX = rect.x();
274  qreal startY = rect.y();
275  qreal elHeight = rect.height();
276  QTransform matrix;
277  matrix.translate(startX, startY - elHeight / 2);
278  fieldName = new QGraphicsTextItem("field");
279  fieldName->setFont(QFont("Arial", (int)elHeight));
280  fieldName->setDefaultTextColor(QColor("White"));
281  fieldName->setTransform(matrix, false);
282  fieldName->setParentItem(background);
283  } else {
284  fieldName = NULL;
285  }
286 
287  // Check whether the dial wants display the numeric value:
288  if (m_renderer->elementExists("value")) {
289  QMatrix textMatrix = m_renderer->matrixForElement("value");
290  QRectF nRect = textMatrix.mapRect(m_renderer->boundsOnElement("value"));
291  qreal startX = nRect.x();
292  qreal startY = nRect.y();
293  qreal elHeight = nRect.height();
294  QTransform matrix;
295  matrix.translate(startX, startY - elHeight / 2);
296  fieldValue = new QGraphicsTextItem("0.00");
297  fieldValue->setFont(QFont("Arial", (int)elHeight));
298  fieldValue->setDefaultTextColor(QColor("White"));
299  fieldValue->setTransform(matrix, false);
300  fieldValue->setParentItem(background);
301  } else {
302  fieldValue = NULL;
303  }
304 
305  // Check whether the dial wants to display the value as a
306  // symbol (only works for text values):
307  if (m_renderer->elementExists("symbol")) {
308  QMatrix textMatrix = m_renderer->matrixForElement("symbol");
309  qreal startX = textMatrix.mapRect(m_renderer->boundsOnElement("symbol")).x();
310  qreal startY = textMatrix.mapRect(m_renderer->boundsOnElement("symbol")).y();
311  QTransform matrix;
312  matrix.translate(startX, startY);
313  fieldSymbol = new QGraphicsSvgItem();
314  fieldSymbol->setElementId("symbol");
315  fieldSymbol->setSharedRenderer(m_renderer);
316  fieldSymbol->setTransform(matrix, false);
317  fieldSymbol->setParentItem(background);
318  } else {
319  fieldSymbol = NULL;
320  }
321 
322  if (m_renderer->elementExists("foreground")) {
323  foreground = new QGraphicsSvgItem();
324  foreground->setSharedRenderer(m_renderer);
325  foreground->setElementId("foreground");
326  foreground->setParentItem(background);
327  fgenabled = true;
328  } else {
329  fgenabled = false;
330  }
331 
332  l_scene->setSceneRect(background->boundingRect());
333 
334  // Reset the current index value:
335  indexValue = 0;
336  if (!dialTimer.isActive() && index)
337  dialTimer.start();
338  } else {
339  qDebug() << "no file ";
340  m_renderer->load(QString(":/lineardial/images/empty.svg"));
341  l_scene->clear(); // This also deletes all items contained in the scene.
342  background = new QGraphicsSvgItem();
343  background->setSharedRenderer(m_renderer);
344  l_scene->addItem(background);
345  fieldName = NULL;
346  fieldValue = NULL;
347  fieldSymbol = NULL;
348  index = NULL;
349  }
350 }
351 
352 void LineardialGadgetWidget::setDialFont(QString fontProps)
353 {
354  // Note: a bit of juggling to preserve the automatic
355  // font size which was calculated upon dial initialization.
356  QFont font = QFont("Arial", 12);
357  font.fromString(fontProps);
358  if (fieldName) {
359  int fieldSize = fieldName->font().pointSize();
360  font.setPointSize(fieldSize);
361  fieldName->setFont(font);
362  }
363  if (fieldValue) {
364  int fieldSize = fieldValue->font().pointSize();
365  font.setPointSize(fieldSize);
366  fieldValue->setFont(font);
367  }
368 }
369 
371 {
372  update();
373 }
374 
375 void LineardialGadgetWidget::paintEvent(QPaintEvent *event)
376 {
377  // Skip painting until the dial file is loaded
378  if (!m_renderer->isValid()) {
379  qDebug() << "Dial file not loaded, not rendering";
380  return;
381  }
382  QGraphicsView::paintEvent(event);
383 }
384 
385 // This event enables the dial to be dynamically resized
386 // whenever the gadget is resized, taking advantage of the vector
387 // nature of SVG dials.
388 void LineardialGadgetWidget::resizeEvent(QResizeEvent *event)
389 {
390  Q_UNUSED(event);
391  fitInView(background, Qt::KeepAspectRatio);
392 }
393 
394 // Converts the value into an percentage:
395 // this enables smooth movement in moveIndex below
397 {
398  if (verticalDial) {
399  indexTarget = 100 * (maxValue - value) / (maxValue - minValue);
400  } else {
401  indexTarget = 100 * (value - minValue) / (maxValue - minValue);
402  }
403 }
404 
405 // Take an input value and move the index accordingly
406 // Move is smooth, starts fast and slows down when
407 // approaching the target.
408 void LineardialGadgetWidget::moveIndex()
409 {
410  if (!index) { // Safeguard
411  dialTimer.stop();
412  return;
413  }
414  if ((fabs((indexValue - indexTarget) * 10) > 3)) {
415  indexValue += (indexTarget - indexValue) / 5;
416  } else {
417  indexValue = indexTarget;
418  dialTimer.stop();
419  }
420  QTransform matrix;
421  index->resetTransform();
422  qreal trans = indexValue * bargraphSize / 100;
423  if (verticalDial) {
424  matrix.translate(startX, trans + startY);
425  } else {
426  matrix.translate(trans + startX, startY);
427  }
428  index->setTransform(matrix, false);
429 
430  update();
431 }
LineardialGadgetWidget(QWidget *parent=nullptr)
void updateIndex(UAVObject *object1)
Called by the UAVObject which got updated.
double getDouble(int index=0) const
Core plugin system that manages the plugins, their life cycle and their registered objects...
Definition: pluginmanager.h:53
QVariant getValue(int index=0) const
bool isText() const
void setDialFont(QString fontProps)
void connectInput(QString obj, QString field)
Connects the widget to the relevant UAVObjects.
void setDialFile(QString dfn)
Setup dial using its master SVG template.
UAVObjectField * getField(const QString &name)
Definition: uavobject.cpp:236
bool isNumeric() const
void resizeEvent(QResizeEvent *event)
void paintEvent(QPaintEvent *event)
QStringList getElementNames() const
x
Definition: OPPlots.m:100
UAVObject * getObject(const QString &name, quint32 instId=0)
y
Definition: OPPlots.m:101