QGCParamWidget.cc 18.1 KB
Newer Older
1 2 3 4
/*=====================================================================

QGroundControl Open Source Ground Control Station

pixhawk's avatar
pixhawk committed
5
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL 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.

    QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/
/**
 * @file
 *   @brief Implementation of class QGCParamWidget
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
 */
Lorenz Meier's avatar
Lorenz Meier committed
28 29
#include <cmath>
#include <float.h>
tstellanova's avatar
tstellanova committed
30 31
#include <QApplication>
#include <QDebug>
32
#include <QFile>
tstellanova's avatar
tstellanova committed
33 34 35
#include <QFileDialog>
#include <QGridLayout>

36
#include <QList>
37
#include <QMessageBox>
tstellanova's avatar
tstellanova committed
38 39 40
#include <QPushButton>
#include <QSettings>
#include <QTime>
pixhawk's avatar
pixhawk committed
41

Lorenz Meier's avatar
Lorenz Meier committed
42
#include "MainWindow.h"
43
#include "QGC.h"
tstellanova's avatar
tstellanova committed
44 45 46
#include "QGCParamWidget.h"
#include "UASInterface.h"
#include "UASParameterCommsMgr.h"
pixhawk's avatar
pixhawk committed
47

48 49 50 51
/**
 * @param uas MAV to set the parameters on
 * @param parent Parent widget
 */
52 53
QGCParamWidget::QGCParamWidget(QWidget *parent) :
    QGCBaseParamWidget(parent),
54 55 56
    componentItems(new QMap<int, QTreeWidgetItem*>()),
    statusLabel(new QLabel(this)),
    tree(new QTreeWidget(this))
pixhawk's avatar
pixhawk committed
57
{
lm's avatar
lm committed
58

59

60 61 62
}


63

64 65 66 67
void QGCParamWidget::disconnectViewSignalsAndSlots()
{
    disconnect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
            this, SLOT(parameterItemChanged(QTreeWidgetItem*,int)));
68
}
69 70


71 72
void QGCParamWidget::connectViewSignalsAndSlots()
{
73 74 75 76 77
    // Listen for edits to the tree UI
    connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
            this, SLOT(parameterItemChanged(QTreeWidgetItem*,int)));
}

78

79
void QGCParamWidget::addActionButtonsToLayout(QGridLayout* layout)
80
{
Jessica's avatar
Jessica committed
81
    QPushButton* refreshButton = new QPushButton(tr("Get"));
82 83
    refreshButton->setToolTip(tr("Fetch parameters currently in volatile memory of aircraft."));
    refreshButton->setWhatsThis(tr("Fetch parameters currently in volatile memory of aircraft."));
84 85
    connect(refreshButton, SIGNAL(clicked()),
            this, SLOT(requestOnboardParamsUpdate()));
86
    layout->addWidget(refreshButton, 2, 0);
87

Jessica's avatar
Jessica committed
88
    QPushButton* setButton = new QPushButton(tr("Set"));
89 90
    setButton->setToolTip(tr("Send pending parameters to volatile onboard memory"));
    setButton->setWhatsThis(tr("Send pending parameters to volatile onboard memory"));
tstellanova's avatar
tstellanova committed
91
    connect(setButton, SIGNAL(clicked()),
92
            paramMgr, SLOT(sendPendingParameters()));
93
    layout->addWidget(setButton, 2, 1);
94

95
    QPushButton* writeButton = new QPushButton(tr("Write (ROM)"));
96 97
    writeButton->setToolTip(tr("Copy parameters in volatile memory of the aircraft to persistent memory. Transmit your parameters first to write these."));
    writeButton->setWhatsThis(tr("Copy parameters in volatile memory of the aircraft to persistent memory. Transmit your parameters first to write these."));
98 99
    connect(writeButton, SIGNAL(clicked()),
            paramMgr, SLOT(copyVolatileParamsToPersistent()));
100
    layout->addWidget(writeButton, 2, 2);
101

102
    QPushButton* loadFileButton = new QPushButton(tr("Load File"));
103 104
    loadFileButton->setToolTip(tr("Load parameters from a file into qgroundcontrol. To write these to the aircraft, use transmit after loading them."));
    loadFileButton->setWhatsThis(tr("Load parameters from a file into qgroundcontrol. To write these to the aircraft, use transmit after loading them."));
105 106
    connect(loadFileButton, SIGNAL(clicked()),
            this, SLOT(loadParametersFromFile()));
107
    layout->addWidget(loadFileButton, 3, 0);
108 109

    QPushButton* saveFileButton = new QPushButton(tr("Save File"));
110 111
    saveFileButton->setToolTip(tr("Save parameters in this view to a file on this computer."));
    saveFileButton->setWhatsThis(tr("Save parameters in this view to a file on this computer."));
112 113
    connect(saveFileButton, SIGNAL(clicked()),
            this, SLOT(saveParametersToFile()));
114
    layout->addWidget(saveFileButton, 3, 1);
lm's avatar
lm committed
115 116

    QPushButton* readButton = new QPushButton(tr("Read (ROM)"));
117 118
    readButton->setToolTip(tr("Copy parameters from persistent onboard memory to volatile onboard memory of aircraft. DOES NOT update the parameters in this view: click refresh after copying them to get them."));
    readButton->setWhatsThis(tr("Copy parameters from persistent onboard memory to volatile onboard memory of aircraft. DOES NOT update the parameters in this view: click refresh after copying them to get them."));
119 120
    connect(readButton, SIGNAL(clicked()),
            paramMgr, SLOT(copyPersistentParamsToVolatile()));
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    layout->addWidget(readButton, 3, 2);

}

void QGCParamWidget::layoutWidget()
{

    statusLabel->setAutoFillBackground(true);

    QGridLayout* layout = new QGridLayout(this);
    layout->setHorizontalSpacing(6);
    layout->setVerticalSpacing(6);
    layout->setMargin(0);
    layout->setSizeConstraint(QLayout::SetMinimumSize);

    // Parameter tree
    layout->addWidget(tree, 0, 0, 1, 3);

    // Status line
    statusLabel->setText(tr("Click refresh to download parameters"));
    layout->addWidget(statusLabel, 1, 0, 1, 3);

    // BUTTONS
    addActionButtonsToLayout(layout);
145

146
    // Set correct vertical scaling
147 148 149 150
    layout->setRowStretch(0, 100);
    layout->setRowStretch(1, 10);
    layout->setRowStretch(2, 10);
    layout->setRowStretch(3, 10);
151

152
    // Set layout
153
    this->setLayout(layout);
pixhawk's avatar
pixhawk committed
154 155 156 157 158 159 160

    // Set header
    QStringList headerItems;
    headerItems.append("Parameter");
    headerItems.append("Value");
    tree->setHeaderLabels(headerItems);
    tree->setColumnCount(2);
tstellanova's avatar
tstellanova committed
161
    tree->setColumnWidth(0,200);
162
    tree->setColumnWidth(1,120);
163
    tree->setExpandsOnDoubleClick(true);
164

165
    tree->setVisible(true);
pixhawk's avatar
pixhawk committed
166 167
}

tstellanova's avatar
tstellanova committed
168

tstellanova's avatar
tstellanova committed
169
void QGCParamWidget::addComponentItem(int compId, QString compName)
pixhawk's avatar
pixhawk committed
170
{
tstellanova's avatar
tstellanova committed
171

172 173
    QString compLine = QString("%1 (#%2)").arg(compName).arg(compId);

174 175
    //QString ptrStr = QString().sprintf("%8p", this);
    //qDebug() <<  "QGCParamWidget" << ptrStr << "addComponentItem:" << compLine;
176

tstellanova's avatar
tstellanova committed
177
    if (componentItems->contains(compId)) {
178 179
        // Update existing component item
        componentItems->value(compId)->setData(0, Qt::DisplayRole, compLine);
180
        //components->value(component)->setData(1, Qt::DisplayRole, QString::number(component));
tstellanova's avatar
tstellanova committed
181
        componentItems->value(compId)->setFirstColumnSpanned(true);
182
    } else {
183 184 185 186 187 188
        // Add new component item
        QStringList list(compLine);
        QTreeWidgetItem* compItem = new QTreeWidgetItem(list);
        compItem->setFirstColumnSpanned(true);
        componentItems->insert(compId, compItem);
        // Create parameter grouping for this component and update maps
tstellanova's avatar
tstellanova committed
189
        paramGroups.insert(compId, new QMap<QString, QTreeWidgetItem*>());
190
        tree->addTopLevelItem(compItem);
pixhawk's avatar
pixhawk committed
191 192
        tree->update();
    }
193

pixhawk's avatar
pixhawk committed
194 195
}

196 197
void QGCParamWidget::handlePendingParamUpdate(int compId, const QString& paramName, QVariant value, bool isPending)
{
198
    //qDebug() << "handlePendingParamUpdate:" << paramName << "with updatingParamNameLock:" << updatingParamNameLock;
199

200
    if (updatingParamNameLock == paramName) {
201
        //qDebug() << "ignoring bounce from " << paramName;
202 203 204 205
        return;
    }
    else {
        updatingParamNameLock = paramName;
206 207
    }

208 209 210 211
    QTreeWidgetItem* paramItem = updateParameterDisplay(compId,paramName,value);
    if (isPending) {
        paramItem->setBackground(0, QBrush(QColor(QGC::colorOrange)));
        paramItem->setBackground(1, QBrush(QColor(QGC::colorOrange)));
212 213
        //ensure that the adjusted item is visible
        tree->expandItem(paramItem->parent());
214 215 216 217 218 219
    }
    else {
        paramItem->setBackground(0, Qt::NoBrush);
        paramItem->setBackground(1, Qt::NoBrush);
    }

220 221
    updatingParamNameLock.clear();

222
}
tstellanova's avatar
tstellanova committed
223

224
void QGCParamWidget::handleOnboardParamUpdate(int compId, const QString& paramName, QVariant value)
lm's avatar
lm committed
225
{
226
    //qDebug() << "handlePendingParamUpdate:" << paramName << "with updatingParamNameLock:" << updatingParamNameLock;
227
    if (paramName == updatingParamNameLock) {
228
        //qDebug() << "handlePendingParamUpdate ignoring bounce from " << paramName;
229 230
        return;
    }
231
    updatingParamNameLock = paramName;
232
    updateParameterDisplay(compId, paramName, value);
233
    updatingParamNameLock.clear();
tstellanova's avatar
tstellanova committed
234
}
235

236

237
void QGCParamWidget::handleOnboardParameterListUpToDate()
tstellanova's avatar
tstellanova committed
238
{
239 240
    //turn off updates while we refresh the entire list
    tree->setUpdatesEnabled(false);
241 242 243

    //rewrite the component item tree after receiving the full list
    QMap<int, QMap<QString, QVariant>*>::iterator i;
244
    QMap<int, QMap<QString, QVariant>*>* onboardParams = paramMgr->dataModel()->getAllOnboardParams();
245 246 247 248 249 250

    for (i = onboardParams->begin(); i != onboardParams->end(); ++i) {
        int compId = i.key();
        QMap<QString, QVariant>* paramPairs = onboardParams->value(compId);
        QMap<QString, QVariant>::iterator j;
        for (j = paramPairs->begin(); j != paramPairs->end(); j++) {
251
            updatingParamNameLock = j.key();
252
            updateParameterDisplay(compId, j.key(),j.value());
253
            updatingParamNameLock.clear();
254 255 256
        }
    }

tstellanova's avatar
tstellanova committed
257 258
    // Expand visual tree
    tree->expandItem(tree->topLevelItem(0));
259 260 261
    tree->setUpdatesEnabled(true);
    tree->update();

lm's avatar
lm committed
262 263
}

264
QTreeWidgetItem* QGCParamWidget::findChildWidgetItemForParam(QTreeWidgetItem* parentItem, const QString& paramName)
tstellanova's avatar
tstellanova committed
265
{
266 267 268 269 270 271 272 273 274
    QTreeWidgetItem* childItem = NULL;

    for (int i = 0; i < parentItem->childCount(); i++) {
        QTreeWidgetItem* child = parentItem->child(i);
        QString key = child->data(0, Qt::DisplayRole).toString();
        if (key == paramName)  {
            childItem = child;
            break;
        }
pixhawk's avatar
pixhawk committed
275
    }
276

277 278 279 280 281
    return childItem;
}

QTreeWidgetItem* QGCParamWidget::getParentWidgetItemForParam(int compId, const QString& paramName)
{
282 283
    QTreeWidgetItem* parentItem = componentItems->value(compId);

pixhawk's avatar
pixhawk committed
284 285
    QString splitToken = "_";
    // Check if auto-grouping can work
286 287
    if (paramName.contains(splitToken)) {
        QString parentStr = paramName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
tstellanova's avatar
tstellanova committed
288 289
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(compId);
        if (!compParamGroups->contains(parentStr)) {
pixhawk's avatar
pixhawk committed
290 291
            // Insert group item
            QStringList glist;
tstellanova's avatar
tstellanova committed
292
            glist.append(parentStr);
293
            QTreeWidgetItem* groupItem = new QTreeWidgetItem(glist);
tstellanova's avatar
tstellanova committed
294 295 296 297 298 299 300

            compParamGroups->insert(parentStr, groupItem);

            // insert new group alphabetized
            QList<QString> groupKeys = compParamGroups->uniqueKeys();
            int insertIdx = groupKeys.indexOf(parentStr);
            componentItems->value(compId)->insertChild(insertIdx,groupItem);
301
        }
302

303
        //parent item for this parameter item will be a group widget item
304 305 306
        parentItem = compParamGroups->value(parentStr);
    }
    else  {
307
        //parent item for this parameter will be the top level (component) widget item
308 309 310
        parentItem = componentItems->value(compId);
    }

311 312
    return parentItem;
}
313

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
void QGCParamWidget::insertParamAlphabetical(int indexLowerBound, int indexUpperBound, QTreeWidgetItem* parentItem, QTreeWidgetItem* paramItem)
{
    if (indexLowerBound >= indexUpperBound)
    {
        if (paramItem->text(0).compare(parentItem->child(indexLowerBound)->text(0)) < 0) {
            parentItem->insertChild(indexLowerBound, paramItem);
        }
        else
        {
            if (indexLowerBound < parentItem->childCount() - 1) {
                parentItem->insertChild(indexLowerBound + 1, paramItem);
            }
            else
            {
                parentItem->addChild(paramItem);
            }
        }
    }
    else
    {
        int midpoint = indexLowerBound + floor(indexUpperBound - indexLowerBound)/2;

        if (paramItem->text(0).compare(parentItem->child(midpoint)->text(0)) < 0)
        {
            insertParamAlphabetical(indexLowerBound, midpoint - 1, parentItem, paramItem);
        } else
        {
            insertParamAlphabetical(midpoint + 1, indexUpperBound, parentItem, paramItem);
        }

    }
}

347 348
QTreeWidgetItem* QGCParamWidget::updateParameterDisplay(int compId, QString parameterName, QVariant value)
{
349
    //qDebug() << "QGCParamWidget::updateParameterDisplay" << parameterName;
350 351

    // Reference to item in tree
352
    QTreeWidgetItem* paramItem = NULL;
353 354 355 356 357 358 359 360 361 362

    // Add component item if necessary
    if (!componentItems->contains(compId)) {
        QString componentName = tr("Component #%1").arg(compId);
        addComponentItem(compId, componentName);
    }

    //default parent item for this parameter widget item will be the top level component item
    QTreeWidgetItem* parentItem = getParentWidgetItemForParam(compId,parameterName);
    if (parentItem) {
363 364
        paramItem = findChildWidgetItemForParam(parentItem,parameterName);
        if (!paramItem) {
365 366 367
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
368
            // CREATE PARAMETER ITEM
369
            paramItem = new QTreeWidgetItem(plist);
370
            // CONFIGURE PARAMETER ITEM
tstellanova's avatar
tstellanova committed
371
            if (value.type() == QVariant::Char) {
372
                paramItem->setData(1, Qt::DisplayRole, value.toUInt());
373
            }
tstellanova's avatar
tstellanova committed
374
            else {
375
                paramItem->setData(1, Qt::DisplayRole, value);
376
            }
377
            paramItem->setFlags(paramItem->flags() | Qt::ItemIsEditable);
378

379
            //Insert alphabetically
380 381 382
            if (parentItem->childCount() > 0) {
                insertParamAlphabetical(0, parentItem->childCount() - 1, parentItem, paramItem);
            } else
383 384 385
            {
                parentItem->addChild(paramItem);
            }
386 387

            //only add the tooltip when the parameter item is first added
388
            QString paramDesc = paramMgr->dataModel()->getParamDescription(parameterName);
389 390
            if (!paramDesc.isEmpty()) {
                QString tooltipFormat;
391
                if (paramMgr->dataModel()->isParamDefaultKnown(parameterName)) {
392
                    tooltipFormat = tr("Default: %1, %2");
393
                    double paramDefValue = paramMgr->dataModel()->getParamDefault(parameterName);
394 395 396 397 398
                    tooltipFormat = tooltipFormat.arg(paramDefValue).arg(paramDesc);
                }
                else {
                    tooltipFormat = paramDesc;
                }
399 400
                paramItem->setToolTip(0, tooltipFormat);
                paramItem->setToolTip(1, tooltipFormat);
401 402 403
            }
        }

404
        //update the parameterItem's data
405
        if (value.type() == QVariant::Char) {
406
            paramItem->setData(1, Qt::DisplayRole, value.toUInt());
407 408
        }
        else {
409
            paramItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
410
        }
411

412
    }
413

414
    if (paramItem) {
415
        // Reset background color
416 417
        paramItem->setBackground(0, Qt::NoBrush);
        paramItem->setBackground(1, Qt::NoBrush);
418

419 420 421 422 423 424 425
        paramItem->setTextColor(0, QGC::colorDarkWhite);
        paramItem->setTextColor(1, QGC::colorDarkWhite);

        if (paramItem == tree->currentItem()) {
            //need to unset current item to clear highlighting (green by default)
            tree->setCurrentItem(NULL); //clear the selected line
        }
426

427
    }
428

429
    return paramItem;
pixhawk's avatar
pixhawk committed
430 431
}

lm's avatar
lm committed
432

433

434
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* paramItem, int column)
lm's avatar
lm committed
435
{
436
    if (paramItem && column > 0) {
437

438
        QString key = paramItem->data(0, Qt::DisplayRole).toString();
439
        //qDebug() << "parameterItemChanged:" << key << "with updatingParamNameLock:" << updatingParamNameLock;
440 441

        if (key == updatingParamNameLock) {
442
            //qDebug() << "parameterItemChanged ignoring bounce from " << key;
443 444
            return;
        }
445 446 447
        else {
            updatingParamNameLock = key;
        }
448 449

        QTreeWidgetItem* parent = paramItem->parent();
450
        while (parent->parent() != NULL) {
451 452 453
            parent = parent->parent();
        }
        // Parent is now top-level component
454
        int componentId = componentItems->key(parent);
455
        QVariant value = paramItem->data(1, Qt::DisplayRole);
456

457

458
        bool pending = paramMgr->dataModel()->updatePendingParamWithValue(componentId,key,value);
459

460 461
        // If the value will result in an update
        if (pending) {
462
            // Set parameter on changed list to be transmitted to MAV
463
            statusLabel->setText(tr("Pending: %1:%2: %3").arg(componentId).arg(key).arg(value.toFloat(), 5, 'f', 1, QChar(' ')));
464

465 466
            paramItem->setBackground(0, QBrush(QColor(QGC::colorOrange)));
            paramItem->setBackground(1, QBrush(QColor(QGC::colorOrange)));
467
        }
468
        else {
469
            QMap<QString , QVariant>* pendingParams = paramMgr->dataModel()->getPendingParamsForComponent(componentId);
470 471
            int pendingCount = pendingParams->count();
            statusLabel->setText(tr("Pending items: %1").arg(pendingCount));
472 473
            paramItem->setBackground(0, Qt::NoBrush);
            paramItem->setBackground(1, Qt::NoBrush);
474
        }
475

476 477 478 479 480 481

        if (paramItem == tree->currentItem()) {
            //need to unset current item to clear highlighting (green by default)
            tree->setCurrentItem(NULL); //clear the selected line
        }

482
        updatingParamNameLock.clear();
483 484 485
    }
}

486

tstellanova's avatar
tstellanova committed
487
void QGCParamWidget::setParameterStatusMsg(const QString& msg)
488
{
tstellanova's avatar
tstellanova committed
489 490 491
    statusLabel->setText(msg);
}

492

493
void QGCParamWidget::clearOnboardParamDisplay()
494
{
495 496
    tree->clear();
    componentItems->clear();
497 498
}

499
void QGCParamWidget::clearPendingParamDisplay()
pixhawk's avatar
pixhawk committed
500 501
{
    tree->clear();
502
    componentItems->clear();
pixhawk's avatar
pixhawk committed
503
}
tstellanova's avatar
tstellanova committed
504

505

tstellanova's avatar
tstellanova committed
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
void QGCParamWidget::handleParamStatusMsgUpdate(QString msg, int level)
{
    QColor bgColor = QGC::colorGreen;
    if ((int)UASParameterCommsMgr::ParamCommsStatusLevel_Warning == level) {
        bgColor = QGC::colorOrange;
    }
    else if ((int)UASParameterCommsMgr::ParamCommsStatusLevel_Error == level) {
        bgColor =  QGC::colorRed;
    }

    QPalette pal = statusLabel->palette();
    pal.setColor(backgroundRole(), bgColor);
    statusLabel->setPalette(pal);
    statusLabel->setText(msg);
}