QGCParamWidget.cc 18.5 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
 */
pixhawk's avatar
pixhawk committed
52
QGCParamWidget::QGCParamWidget(UASInterface* uas, QWidget *parent) :
53
    QGCUASParamManager(uas, parent),
54
    componentItems(new QMap<int, QTreeWidgetItem*>())
pixhawk's avatar
pixhawk committed
55
{
lm's avatar
lm committed
56

57 58 59 60 61 62 63 64 65 66 67 68
    layoutWidget();

    // Connect signals/slots

    // Listen for edits to the tree UI
    connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
            this, SLOT(parameterItemChanged(QTreeWidgetItem*,int)));

    // Listen to updated param signals from the data model
    connect(paramDataModel, SIGNAL(parameterUpdated(int, QString , QVariant )),
            this, SLOT(handleParameterUpdate(int,QString,QVariant)));

69 70 71
    connect(paramDataModel, SIGNAL(pendingParamUpdate(int compId, const QString& paramName, QVariant value, bool isPending)),
            this, SLOT(handlePendingParamUpdate(int compId, const QString& paramName, QVariant value, bool isPending)));

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    // Listen for param list reload finished
    connect(paramCommsMgr, SIGNAL(parameterListUpToDate()),
            this, SLOT(handleParameterListUpToDate()));

    // Listen to communications status messages so we can display them
    connect(paramCommsMgr, SIGNAL(parameterStatusMsgUpdated(QString,int)),
            this, SLOT(handleParamStatusMsgUpdate(QString , int )));

    // Ensure we're receiving the list of params
    requestAllParamsUpdate();

}




void QGCParamWidget::layoutWidget()
{
pixhawk's avatar
pixhawk committed
90 91
    // Create tree widget
    tree = new QTreeWidget(this);
lm's avatar
lm committed
92
    statusLabel = new QLabel();
lm's avatar
lm committed
93
    statusLabel->setAutoFillBackground(true);
pixhawk's avatar
pixhawk committed
94 95

    // Set tree widget as widget onto this component
96
    QGridLayout* horizontalLayout;
pixhawk's avatar
pixhawk committed
97
    //form->setAutoFillBackground(false);
98
    horizontalLayout = new QGridLayout(this);
99 100
    horizontalLayout->setHorizontalSpacing(6);
    horizontalLayout->setVerticalSpacing(6);
pixhawk's avatar
pixhawk committed
101
    horizontalLayout->setMargin(0);
102 103
    horizontalLayout->setSizeConstraint(QLayout::SetMinimumSize);
    //horizontalLayout->setSizeConstraint( QLayout::SetFixedSize );
pixhawk's avatar
pixhawk committed
104

lm's avatar
lm committed
105
    // Parameter tree
106
    horizontalLayout->addWidget(tree, 0, 0, 1, 3);
lm's avatar
lm committed
107 108

    // Status line
lm's avatar
lm committed
109
    statusLabel->setText(tr("Click refresh to download parameters"));
lm's avatar
lm committed
110 111 112
    horizontalLayout->addWidget(statusLabel, 1, 0, 1, 3);

    // BUTTONS
Jessica's avatar
Jessica committed
113
    QPushButton* refreshButton = new QPushButton(tr("Get"));
114 115
    refreshButton->setToolTip(tr("Load parameters currently in non-permanent memory of aircraft."));
    refreshButton->setWhatsThis(tr("Load parameters currently in non-permanent memory of aircraft."));
116
    connect(refreshButton, SIGNAL(clicked()), this, SLOT(requestAllParamsUpdate()));
lm's avatar
lm committed
117
    horizontalLayout->addWidget(refreshButton, 2, 0);
118

Jessica's avatar
Jessica committed
119
    QPushButton* setButton = new QPushButton(tr("Set"));
120 121
    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
122 123
    connect(setButton, SIGNAL(clicked()),
            this, SLOT(sendPendingParameters()));
lm's avatar
lm committed
124
    horizontalLayout->addWidget(setButton, 2, 1);
125

126
    QPushButton* writeButton = new QPushButton(tr("Write (ROM)"));
127 128
    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."));
129
    connect(writeButton, SIGNAL(clicked()), this, SLOT(writeParameters()));
lm's avatar
lm committed
130
    horizontalLayout->addWidget(writeButton, 2, 2);
131

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

    QPushButton* saveFileButton = new QPushButton(tr("Save File"));
139 140
    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."));
141
    connect(saveFileButton, SIGNAL(clicked()), this, SLOT(saveParametersToFile()));
lm's avatar
lm committed
142 143 144 145 146 147 148
    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."));
    connect(readButton, SIGNAL(clicked()), this, SLOT(readParameters()));
    horizontalLayout->addWidget(readButton, 3, 2);
149

150 151 152 153 154 155
    // Set correct vertical scaling
    horizontalLayout->setRowStretch(0, 100);
    horizontalLayout->setRowStretch(1, 10);
    horizontalLayout->setRowStretch(2, 10);
    horizontalLayout->setRowStretch(3, 10);

156
    // Set layout
pixhawk's avatar
pixhawk committed
157 158 159 160 161 162 163 164
    this->setLayout(horizontalLayout);

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

169
    tree->setVisible(true);
pixhawk's avatar
pixhawk committed
170 171
}

tstellanova's avatar
tstellanova committed
172

tstellanova's avatar
tstellanova committed
173
void QGCParamWidget::addComponentItem(int compId, QString compName)
pixhawk's avatar
pixhawk committed
174
{
tstellanova's avatar
tstellanova committed
175

176 177
    QString compLine = QString("%1 (#%2)").arg(compName).arg(compId);

tstellanova's avatar
tstellanova committed
178
    QString ptrStr = QString().sprintf("%8p", this);
179 180
    qDebug() <<  "QGCParamWidget" << ptrStr << "addComponentItem:" << compLine;

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

tstellanova's avatar
tstellanova committed
198 199
    //TODO it seems unlikely that the UI would know about a component before the data model...
    paramDataModel->addComponent(compId);
200

pixhawk's avatar
pixhawk committed
201 202
}

203 204 205 206 207 208 209 210 211 212 213 214 215
void QGCParamWidget::handlePendingParamUpdate(int compId, const QString& paramName, QVariant value, bool isPending)
{
    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);
    }

}
tstellanova's avatar
tstellanova committed
216

tstellanova's avatar
tstellanova committed
217
void QGCParamWidget::handleParameterUpdate(int componentId, const QString& paramName, QVariant value)
lm's avatar
lm committed
218
{
tstellanova's avatar
tstellanova committed
219 220
    updateParameterDisplay(componentId, paramName, value);
}
221

222

tstellanova's avatar
tstellanova committed
223
void QGCParamWidget::handleParameterListUpToDate()
tstellanova's avatar
tstellanova committed
224
{
225 226 227

    //turn off updates while we refresh the entire list
    tree->setUpdatesEnabled(false);
228 229 230

    //rewrite the component item tree after receiving the full list
    QMap<int, QMap<QString, QVariant>*>::iterator i;
231
    QMap<int, QMap<QString, QVariant>*>* onboardParams = paramDataModel->getAllOnboardParams();
232 233 234 235 236 237 238 239 240 241

    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++) {
            updateParameterDisplay(compId, j.key(),j.value());
        }
    }

tstellanova's avatar
tstellanova committed
242 243
    // Expand visual tree
    tree->expandItem(tree->topLevelItem(0));
244 245 246
    tree->setUpdatesEnabled(true);
    tree->update();

lm's avatar
lm committed
247 248
}

249
QTreeWidgetItem* QGCParamWidget::findChildWidgetItemForParam(QTreeWidgetItem* parentItem, const QString& paramName)
tstellanova's avatar
tstellanova committed
250
{
251 252 253 254 255 256 257 258 259
    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
260
    }
261

262 263 264 265 266
    return childItem;
}

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

pixhawk's avatar
pixhawk committed
269 270
    QString splitToken = "_";
    // Check if auto-grouping can work
271 272
    if (paramName.contains(splitToken)) {
        QString parentStr = paramName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
tstellanova's avatar
tstellanova committed
273 274
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(compId);
        if (!compParamGroups->contains(parentStr)) {
pixhawk's avatar
pixhawk committed
275 276
            // Insert group item
            QStringList glist;
tstellanova's avatar
tstellanova committed
277
            glist.append(parentStr);
278
            QTreeWidgetItem* groupItem = new QTreeWidgetItem(glist);
tstellanova's avatar
tstellanova committed
279 280 281 282 283 284 285

            compParamGroups->insert(parentStr, groupItem);

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

288
        //parent item for this parameter item will be a group widget item
289 290 291
        parentItem = compParamGroups->value(parentStr);
    }
    else  {
292
        //parent item for this parameter will be the top level (component) widget item
293 294 295
        parentItem = componentItems->value(compId);
    }

296 297
    return parentItem;
}
298

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
QTreeWidgetItem* QGCParamWidget::updateParameterDisplay(int compId, QString parameterName, QVariant value)
{
//    qDebug() << "QGCParamWidget::updateParameterDisplay" << parameterName;

    // Reference to item in tree
    QTreeWidgetItem* parameterItem = NULL;

    // 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) {
        parameterItem = findChildWidgetItemForParam(parentItem,parameterName);
        if (!parameterItem) {
317 318 319
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
320
            // CREATE PARAMETER ITEM
321
            parameterItem = new QTreeWidgetItem(plist);
322
            // CONFIGURE PARAMETER ITEM
tstellanova's avatar
tstellanova committed
323
            if (value.type() == QVariant::Char) {
324 325
                parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
            }
tstellanova's avatar
tstellanova committed
326
            else {
327 328
                parameterItem->setData(1, Qt::DisplayRole, value);
            }
lm's avatar
lm committed
329
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
330
            //TODO insert alphabetically
331
            parentItem->addChild(parameterItem);
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

            //only add the tooltip when the parameter item is first added
            QString paramDesc = paramDataModel->getParamDescription(parameterName);
            if (!paramDesc.isEmpty()) {
                QString tooltipFormat;
                if (paramDataModel->isParamDefaultKnown(parameterName)) {
                    tooltipFormat = tr("Default: %1, %2");
                    double paramDefValue = paramDataModel->getParamDefault(parameterName);
                    tooltipFormat = tooltipFormat.arg(paramDefValue).arg(paramDesc);
                }
                else {
                    tooltipFormat = paramDesc;
                }
                parameterItem->setToolTip(0, tooltipFormat);
                parameterItem->setToolTip(1, tooltipFormat);
            }
        }

        //update the parameterItem's data
        if (value.type() == QVariant::Char) {
            parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
        }
        else {
            parameterItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
356
        }
357
    }
358

359
    // Reset background color
360
    parameterItem->setBackground(0, Qt::NoBrush);
361
    parameterItem->setBackground(1, Qt::NoBrush);
362 363 364 365

    parameterItem->setTextColor(0, QGC::colorDarkWhite);
    parameterItem->setTextColor(1, QGC::colorDarkWhite);

366
    return parameterItem;
367

pixhawk's avatar
pixhawk committed
368 369
}

lm's avatar
lm committed
370

371

372
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column)
lm's avatar
lm committed
373
{
374
    if (current && column > 0) {
375
        QTreeWidgetItem* parent = current->parent();
376
        while (parent->parent() != NULL) {
377 378 379
            parent = parent->parent();
        }
        // Parent is now top-level component
380
        int componentId = componentItems->key(parent);
381

382 383
        QString key = current->data(0, Qt::DisplayRole).toString();
        QVariant value = current->data(1, Qt::DisplayRole);
384

385
        bool pending = paramDataModel->updatePendingParamWithValue(componentId,key,value);
386

387 388
        // If the value will result in an update
        if (pending) {
389
            // Set parameter on changed list to be transmitted to MAV
390
            statusLabel->setText(tr("Pending: %1:%2: %3").arg(componentId).arg(key).arg(value.toFloat(), 5, 'f', 1, QChar(' ')));
391 392 393 394 395

            if (current == tree->currentItem()) {
                //need to unset current item to clear highlighting (green by default)
                tree->setCurrentItem(NULL); //clear the selected line
            }
396 397
            current->setBackground(0, QBrush(QColor(QGC::colorOrange)));
            current->setBackground(1, QBrush(QColor(QGC::colorOrange)));
398
        }
399
        else {
400
            QMap<QString , QVariant>* pendingParams = paramDataModel->getPendingParamsForComponent(componentId);
401 402 403 404 405
            int pendingCount = pendingParams->count();
            statusLabel->setText(tr("Pending items: %1").arg(pendingCount));
            current->setBackground(0, Qt::NoBrush);
            current->setBackground(1, Qt::NoBrush);
        }
406

407 408 409
    }
}

410 411


412
void QGCParamWidget::saveParametersToFile()
413
{
414
    if (!mav) return;
415 416
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "./parameters.txt", tr("Parameter File (*.txt)"));
    QFile file(fileName);
417
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
418 419 420
        return;
    }

421
    QTextStream outstream(&file);
422
    paramDataModel->writeOnboardParamsToStream(outstream,mav->getUASName());
423 424 425
    file.close();
}

426 427

void QGCParamWidget::loadParametersFromFile()
428
{
429
    if (!mav) return;
430 431 432 433 434 435
    QString fileName = QFileDialog::getOpenFileName(this, tr("Load File"), ".", tr("Parameter file (*.txt)"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QTextStream in(&file);
436
    paramDataModel->readUpdateParamsFromStream(in);
437
    file.close();
lm's avatar
lm committed
438 439
}

tstellanova's avatar
tstellanova committed
440
void QGCParamWidget::setParameterStatusMsg(const QString& msg)
441
{
tstellanova's avatar
tstellanova committed
442 443 444
    statusLabel->setText(msg);
}

445
void QGCParamWidget::requestAllParamsUpdate()
tstellanova's avatar
tstellanova committed
446 447 448
{
    if (!mav) {
        return;
449
    }
tstellanova's avatar
tstellanova committed
450 451 452 453 454

    // Clear view and request param list
    clear();

    requestParameterList();
455 456
}

lm's avatar
lm committed
457

tstellanova's avatar
tstellanova committed
458

459
/**
tstellanova's avatar
tstellanova committed
460 461
 * Write the current onboard parameters from RAM into
 * permanent storage, e.g. EEPROM or harddisk
462
 */
tstellanova's avatar
tstellanova committed
463
void QGCParamWidget::writeParameters()
464
{
tstellanova's avatar
tstellanova committed
465 466 467
    int changedParamCount = 0;

    QMap<int, QMap<QString, QVariant>*>::iterator i;
468
    QMap<int, QMap<QString, QVariant>*>* changedValues = paramDataModel->getAllPendingParams();
tstellanova's avatar
tstellanova committed
469

470
    for (i = changedValues->begin(); (i != changedValues->end()) && (0 == changedParamCount);  ++i) {
tstellanova's avatar
tstellanova committed
471
        // Iterate through the pending parameters of the component, break on the first changed parameter
tstellanova's avatar
tstellanova committed
472 473
        QMap<QString, QVariant>* compPending = i.value();
        changedParamCount += compPending->count();
tstellanova's avatar
tstellanova committed
474
    }
475

tstellanova's avatar
tstellanova committed
476
    if (changedParamCount > 0) {
tstellanova's avatar
tstellanova committed
477 478 479 480
        QMessageBox msgBox;
        msgBox.setText(tr("There are locally changed parameters. Please transmit them first (<TRANSMIT>) or update them with the onboard values (<REFRESH>) before storing onboard from RAM to ROM."));
        msgBox.exec();
    }
tstellanova's avatar
tstellanova committed
481 482
    else {
        paramCommsMgr->writeParamsToPersistentStorage();
tstellanova's avatar
tstellanova committed
483 484
    }
}
485

486

487 488
void QGCParamWidget::readParameters()
{
489
    if (!mav) return;
490
    mav->readParametersFromStorage();
491 492
}

493 494 495
/**
 * Clear all data in the parameter widget
 */
pixhawk's avatar
pixhawk committed
496 497 498
void QGCParamWidget::clear()
{
    tree->clear();
499
    componentItems->clear();
pixhawk's avatar
pixhawk committed
500
}
tstellanova's avatar
tstellanova committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

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);

}