QGCParamWidget.cc 17.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 80
void QGCParamWidget::layoutWidget()
{
81

lm's avatar
lm committed
82
    statusLabel->setAutoFillBackground(true);
pixhawk's avatar
pixhawk committed
83 84

    // Set tree widget as widget onto this component
85
    QGridLayout* horizontalLayout;
pixhawk's avatar
pixhawk committed
86
    //form->setAutoFillBackground(false);
87
    horizontalLayout = new QGridLayout(this);
88 89
    horizontalLayout->setHorizontalSpacing(6);
    horizontalLayout->setVerticalSpacing(6);
pixhawk's avatar
pixhawk committed
90
    horizontalLayout->setMargin(0);
91 92
    horizontalLayout->setSizeConstraint(QLayout::SetMinimumSize);
    //horizontalLayout->setSizeConstraint( QLayout::SetFixedSize );
pixhawk's avatar
pixhawk committed
93

lm's avatar
lm committed
94
    // Parameter tree
95
    horizontalLayout->addWidget(tree, 0, 0, 1, 3);
lm's avatar
lm committed
96 97

    // Status line
lm's avatar
lm committed
98
    statusLabel->setText(tr("Click refresh to download parameters"));
lm's avatar
lm committed
99 100 101
    horizontalLayout->addWidget(statusLabel, 1, 0, 1, 3);

    // BUTTONS
Jessica's avatar
Jessica committed
102
    QPushButton* refreshButton = new QPushButton(tr("Get"));
103 104
    refreshButton->setToolTip(tr("Load parameters currently in non-permanent memory of aircraft."));
    refreshButton->setWhatsThis(tr("Load parameters currently in non-permanent memory of aircraft."));
105 106
    connect(refreshButton, SIGNAL(clicked()),
            this, SLOT(requestOnboardParamsUpdate()));
lm's avatar
lm committed
107
    horizontalLayout->addWidget(refreshButton, 2, 0);
108

Jessica's avatar
Jessica committed
109
    QPushButton* setButton = new QPushButton(tr("Set"));
110 111
    setButton->setToolTip(tr("Set current parameters in non-permanent onboard memory"));
    setButton->setWhatsThis(tr("Set current parameters in non-permanent onboard memory"));
tstellanova's avatar
tstellanova committed
112 113
    connect(setButton, SIGNAL(clicked()),
            this, SLOT(sendPendingParameters()));
lm's avatar
lm committed
114
    horizontalLayout->addWidget(setButton, 2, 1);
115

116
    QPushButton* writeButton = new QPushButton(tr("Write (ROM)"));
117 118
    writeButton->setToolTip(tr("Copy current parameters in non-permanent memory of the aircraft to permanent memory. Transmit your parameters first to write these."));
    writeButton->setWhatsThis(tr("Copy current parameters in non-permanent memory of the aircraft to permanent memory. Transmit your parameters first to write these."));
119 120
    connect(writeButton, SIGNAL(clicked()),
            paramMgr, SLOT(copyVolatileParamsToPersistent()));
lm's avatar
lm committed
121
    horizontalLayout->addWidget(writeButton, 2, 2);
122

123
    QPushButton* loadFileButton = new QPushButton(tr("Load File"));
124 125
    loadFileButton->setToolTip(tr("Load parameters from a file on this computer in the view. To write them to the aircraft, use transmit after loading them."));
    loadFileButton->setWhatsThis(tr("Load parameters from a file on this computer in the view. To write them to the aircraft, use transmit after loading them."));
126 127
    connect(loadFileButton, SIGNAL(clicked()),
            this, SLOT(loadParametersFromFile()));
lm's avatar
lm committed
128
    horizontalLayout->addWidget(loadFileButton, 3, 0);
129 130

    QPushButton* saveFileButton = new QPushButton(tr("Save File"));
131 132
    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."));
133 134
    connect(saveFileButton, SIGNAL(clicked()),
            this, SLOT(saveParametersToFile()));
lm's avatar
lm committed
135 136 137 138 139
    horizontalLayout->addWidget(saveFileButton, 3, 1);

    QPushButton* readButton = new QPushButton(tr("Read (ROM)"));
    readButton->setToolTip(tr("Copy parameters from permanent memory to non-permanent current 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 permanent memory to non-permanent current memory of aircraft. DOES NOT update the parameters in this view, click refresh after copying them to get them."));
140 141
    connect(readButton, SIGNAL(clicked()),
            paramMgr, SLOT(copyPersistentParamsToVolatile()));
lm's avatar
lm committed
142
    horizontalLayout->addWidget(readButton, 3, 2);
143

144 145 146 147 148 149
    // Set correct vertical scaling
    horizontalLayout->setRowStretch(0, 100);
    horizontalLayout->setRowStretch(1, 10);
    horizontalLayout->setRowStretch(2, 10);
    horizontalLayout->setRowStretch(3, 10);

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

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

163
    tree->setVisible(true);
pixhawk's avatar
pixhawk committed
164 165
}

tstellanova's avatar
tstellanova committed
166

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

170 171
    QString compLine = QString("%1 (#%2)").arg(compName).arg(compId);

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

tstellanova's avatar
tstellanova committed
175
    if (componentItems->contains(compId)) {
176 177
        // Update existing component item
        componentItems->value(compId)->setData(0, Qt::DisplayRole, compLine);
178
        //components->value(component)->setData(1, Qt::DisplayRole, QString::number(component));
tstellanova's avatar
tstellanova committed
179
        componentItems->value(compId)->setFirstColumnSpanned(true);
180
    } else {
181 182 183 184 185 186
        // 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
187
        paramGroups.insert(compId, new QMap<QString, QTreeWidgetItem*>());
188
        tree->addTopLevelItem(compItem);
pixhawk's avatar
pixhawk committed
189 190
        tree->update();
    }
191

pixhawk's avatar
pixhawk committed
192 193
}

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

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

206 207 208 209 210 211 212 213 214 215
    QTreeWidgetItem* paramItem = updateParameterDisplay(compId,paramName,value);
    if (isPending) {
        paramItem->setBackground(0, QBrush(QColor(QGC::colorOrange)));
        paramItem->setBackground(1, QBrush(QColor(QGC::colorOrange)));
    }
    else {
        paramItem->setBackground(0, Qt::NoBrush);
        paramItem->setBackground(1, Qt::NoBrush);
    }

216 217
    updatingParamNameLock.clear();

218
}
tstellanova's avatar
tstellanova committed
219

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

232

233
void QGCParamWidget::handleOnboardParameterListUpToDate()
tstellanova's avatar
tstellanova committed
234
{
235 236
    //turn off updates while we refresh the entire list
    tree->setUpdatesEnabled(false);
237 238 239

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

    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++) {
247
            updatingParamNameLock = j.key();
248
            updateParameterDisplay(compId, j.key(),j.value());
249
            updatingParamNameLock.clear();
250 251 252
        }
    }

tstellanova's avatar
tstellanova committed
253 254
    // Expand visual tree
    tree->expandItem(tree->topLevelItem(0));
255 256 257
    tree->setUpdatesEnabled(true);
    tree->update();

lm's avatar
lm committed
258 259
}

260
QTreeWidgetItem* QGCParamWidget::findChildWidgetItemForParam(QTreeWidgetItem* parentItem, const QString& paramName)
tstellanova's avatar
tstellanova committed
261
{
262 263 264 265 266 267 268 269 270
    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
271
    }
272

273 274 275 276 277
    return childItem;
}

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

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

            compParamGroups->insert(parentStr, groupItem);

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

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

307 308
    return parentItem;
}
309

310 311
QTreeWidgetItem* QGCParamWidget::updateParameterDisplay(int compId, QString parameterName, QVariant value)
{
312
    //qDebug() << "QGCParamWidget::updateParameterDisplay" << parameterName;
313 314

    // Reference to item in tree
315
    QTreeWidgetItem* paramItem = NULL;
316 317 318 319 320 321 322 323 324 325

    // 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) {
326 327
        paramItem = findChildWidgetItemForParam(parentItem,parameterName);
        if (!paramItem) {
328 329 330
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
331
            // CREATE PARAMETER ITEM
332
            paramItem = new QTreeWidgetItem(plist);
333
            // CONFIGURE PARAMETER ITEM
tstellanova's avatar
tstellanova committed
334
            if (value.type() == QVariant::Char) {
335
                paramItem->setData(1, Qt::DisplayRole, value.toUInt());
336
            }
tstellanova's avatar
tstellanova committed
337
            else {
338
                paramItem->setData(1, Qt::DisplayRole, value);
339
            }
340
            paramItem->setFlags(paramItem->flags() | Qt::ItemIsEditable);
341

342
            //TODO insert alphabetically
343
            parentItem->addChild(paramItem);
344 345

            //only add the tooltip when the parameter item is first added
346
            QString paramDesc = paramMgr->dataModel()->getParamDescription(parameterName);
347 348
            if (!paramDesc.isEmpty()) {
                QString tooltipFormat;
349
                if (paramMgr->dataModel()->isParamDefaultKnown(parameterName)) {
350
                    tooltipFormat = tr("Default: %1, %2");
351
                    double paramDefValue = paramMgr->dataModel()->getParamDefault(parameterName);
352 353 354 355 356
                    tooltipFormat = tooltipFormat.arg(paramDefValue).arg(paramDesc);
                }
                else {
                    tooltipFormat = paramDesc;
                }
357 358
                paramItem->setToolTip(0, tooltipFormat);
                paramItem->setToolTip(1, tooltipFormat);
359 360 361
            }
        }

362
        //update the parameterItem's data
363
        if (value.type() == QVariant::Char) {
364
            paramItem->setData(1, Qt::DisplayRole, value.toUInt());
365 366
        }
        else {
367
            paramItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
368
        }
369
    }
370

371
    if (paramItem) {
372
        // Reset background color
373 374
        paramItem->setBackground(0, Qt::NoBrush);
        paramItem->setBackground(1, Qt::NoBrush);
375

376 377 378 379 380 381 382
        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
        }
383

384
    }
385

386
    return paramItem;
pixhawk's avatar
pixhawk committed
387 388
}

lm's avatar
lm committed
389

390

391
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* paramItem, int column)
lm's avatar
lm committed
392
{
393
    if (paramItem && column > 0) {
394

395
        QString key = paramItem->data(0, Qt::DisplayRole).toString();
396
        //qDebug() << "parameterItemChanged:" << key << "with updatingParamNameLock:" << updatingParamNameLock;
397 398

        if (key == updatingParamNameLock) {
399
            //qDebug() << "parameterItemChanged ignoring bounce from " << key;
400 401
            return;
        }
402 403 404
        else {
            updatingParamNameLock = key;
        }
405 406

        QTreeWidgetItem* parent = paramItem->parent();
407
        while (parent->parent() != NULL) {
408 409 410
            parent = parent->parent();
        }
        // Parent is now top-level component
411
        int componentId = componentItems->key(parent);
412
        QVariant value = paramItem->data(1, Qt::DisplayRole);
413

414

415
        bool pending = paramMgr->dataModel()->updatePendingParamWithValue(componentId,key,value);
416

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

422 423
            paramItem->setBackground(0, QBrush(QColor(QGC::colorOrange)));
            paramItem->setBackground(1, QBrush(QColor(QGC::colorOrange)));
424
        }
425
        else {
426
            QMap<QString , QVariant>* pendingParams = paramMgr->dataModel()->getPendingParamsForComponent(componentId);
427 428
            int pendingCount = pendingParams->count();
            statusLabel->setText(tr("Pending items: %1").arg(pendingCount));
429 430
            paramItem->setBackground(0, Qt::NoBrush);
            paramItem->setBackground(1, Qt::NoBrush);
431
        }
432

433 434 435 436 437 438

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

439
        updatingParamNameLock.clear();
440 441 442
    }
}

443

tstellanova's avatar
tstellanova committed
444
void QGCParamWidget::setParameterStatusMsg(const QString& msg)
445
{
tstellanova's avatar
tstellanova committed
446 447 448
    statusLabel->setText(msg);
}

449

450
void QGCParamWidget::clearOnboardParamDisplay()
451
{
452 453
    tree->clear();
    componentItems->clear();
454 455
}

456
void QGCParamWidget::clearPendingParamDisplay()
pixhawk's avatar
pixhawk committed
457 458
{
    tree->clear();
459
    componentItems->clear();
pixhawk's avatar
pixhawk committed
460
}
tstellanova's avatar
tstellanova committed
461

462

tstellanova's avatar
tstellanova committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
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);
}