mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	screen when screen orientation changes. Add DialPopup to allow dials to adjusted with a slider, which is easier on touchscreens. Add TableTapAndHold to support table context menus on touchscreens.
		
			
				
	
	
		
			1176 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1176 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2022 Jon Beniston, M7RCE                                        //
 | 
						|
// Copyright (C) 2020 Edouard Griffiths, F4EXB                                   //
 | 
						|
//                                                                               //
 | 
						|
// This program 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 as version 3 of the License, or                  //
 | 
						|
// (at your option) any later version.                                           //
 | 
						|
//                                                                               //
 | 
						|
// This program 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 V3 for more details.                               //
 | 
						|
//                                                                               //
 | 
						|
// You should have received a copy of the GNU General Public License             //
 | 
						|
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#include "feature/featureuiset.h"
 | 
						|
#include "gui/basicfeaturesettingsdialog.h"
 | 
						|
#include "gui/flowlayout.h"
 | 
						|
#include "gui/scidoublespinbox.h"
 | 
						|
#include "gui/dialogpositioner.h"
 | 
						|
 | 
						|
#include "ui_remotecontrolgui.h"
 | 
						|
#include "remotecontrol.h"
 | 
						|
#include "remotecontrolgui.h"
 | 
						|
#include "remotecontrolsettingsdialog.h"
 | 
						|
 | 
						|
RemoteControlGUI* RemoteControlGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
 | 
						|
{
 | 
						|
    RemoteControlGUI* gui = new RemoteControlGUI(pluginAPI, featureUISet, feature);
 | 
						|
    return gui;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::destroy()
 | 
						|
{
 | 
						|
    delete this;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::resetToDefaults()
 | 
						|
{
 | 
						|
    m_settings.resetToDefaults();
 | 
						|
    displaySettings();
 | 
						|
    applySettings(true);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray RemoteControlGUI::serialize() const
 | 
						|
{
 | 
						|
    return m_settings.serialize();
 | 
						|
}
 | 
						|
 | 
						|
bool RemoteControlGUI::deserialize(const QByteArray& data)
 | 
						|
{
 | 
						|
    if (m_settings.deserialize(data))
 | 
						|
    {
 | 
						|
        m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
 | 
						|
        displaySettings();
 | 
						|
        applySettings(true);
 | 
						|
        on_update_clicked();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        resetToDefaults();
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool RemoteControlGUI::handleMessage(const Message& message)
 | 
						|
{
 | 
						|
    if (RemoteControl::MsgConfigureRemoteControl::match(message))
 | 
						|
    {
 | 
						|
        qDebug("RemoteControlGUI::handleMessage: RemoteControl::MsgConfigureRemoteControl");
 | 
						|
        const RemoteControl::MsgConfigureRemoteControl& cfg = (RemoteControl::MsgConfigureRemoteControl&) message;
 | 
						|
        m_settings = cfg.getSettings();
 | 
						|
        blockApplySettings(true);
 | 
						|
        displaySettings();
 | 
						|
        blockApplySettings(false);
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RemoteControl::MsgDeviceStatus::match(message))
 | 
						|
    {
 | 
						|
        const RemoteControl::MsgDeviceStatus& msg = (RemoteControl::MsgDeviceStatus&) message;
 | 
						|
        deviceUpdated(msg.getProtocol(), msg.getDeviceId(), msg.getStatus());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RemoteControl::MsgDeviceError::match(message))
 | 
						|
    {
 | 
						|
        const RemoteControl::MsgDeviceError& msg = (RemoteControl::MsgDeviceError&) message;
 | 
						|
        QMessageBox::critical(this,  "Remote Control Error", msg.getErrorMessage());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RemoteControl::MsgDeviceUnavailable::match(message))
 | 
						|
    {
 | 
						|
        const RemoteControl::MsgDeviceUnavailable& msg = (RemoteControl::MsgDeviceUnavailable&) message;
 | 
						|
        deviceUnavailable(msg.getProtocol(), msg.getDeviceId());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::handleInputMessages()
 | 
						|
{
 | 
						|
    Message* message;
 | 
						|
 | 
						|
    while ((message = getInputMessageQueue()->pop()))
 | 
						|
    {
 | 
						|
        if (handleMessage(*message)) {
 | 
						|
            delete message;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::onWidgetRolled(QWidget* widget, bool rollDown)
 | 
						|
{
 | 
						|
    (void) widget;
 | 
						|
    (void) rollDown;
 | 
						|
 | 
						|
    getRollupContents()->saveState(m_rollupState);
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
RemoteControlGUI::RemoteControlGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
 | 
						|
    FeatureGUI(parent),
 | 
						|
    ui(new Ui::RemoteControlGUI),
 | 
						|
    m_pluginAPI(pluginAPI),
 | 
						|
    m_featureUISet(featureUISet),
 | 
						|
    m_doApplySettings(true)
 | 
						|
{
 | 
						|
    m_feature = feature;
 | 
						|
    setAttribute(Qt::WA_DeleteOnClose, true);
 | 
						|
    m_helpURL = "plugins/feature/remotecontrol/readme.md";
 | 
						|
    RollupContents *rollupContents = getRollupContents();
 | 
						|
	ui->setupUi(rollupContents);
 | 
						|
    rollupContents->arrangeRollups();
 | 
						|
	connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
 | 
						|
 | 
						|
    ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"
 | 
						|
                                 "QToolButton:checked { background-color : green; }"
 | 
						|
                                 "QToolButton:disabled { background-color : gray; }");
 | 
						|
 | 
						|
    m_startStopIcon.addFile(":/play.png", QSize(16, 16), QIcon::Normal, QIcon::Off);
 | 
						|
    m_startStopIcon.addFile(":/stop.png", QSize(16, 16), QIcon::Normal, QIcon::On);
 | 
						|
 | 
						|
    m_remoteControl = reinterpret_cast<RemoteControl*>(feature);
 | 
						|
    m_remoteControl->setMessageQueueToGUI(&m_inputMessageQueue);
 | 
						|
 | 
						|
    m_settings.setRollupState(&m_rollupState);
 | 
						|
 | 
						|
    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
 | 
						|
    connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
						|
 | 
						|
    displaySettings();
 | 
						|
    applySettings(true);
 | 
						|
    makeUIConnections();
 | 
						|
}
 | 
						|
 | 
						|
RemoteControlGUI::~RemoteControlGUI()
 | 
						|
{
 | 
						|
    qDeleteAll(m_deviceGUIs);
 | 
						|
    m_deviceGUIs.clear();
 | 
						|
    delete ui;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::setWorkspaceIndex(int index)
 | 
						|
{
 | 
						|
    m_settings.m_workspaceIndex = index;
 | 
						|
    m_feature->setWorkspaceIndex(index);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::blockApplySettings(bool block)
 | 
						|
{
 | 
						|
    m_doApplySettings = !block;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::displaySettings()
 | 
						|
{
 | 
						|
    setTitleColor(m_settings.m_rgbColor);
 | 
						|
    setWindowTitle(m_settings.m_title);
 | 
						|
    setTitle(m_settings.m_title);
 | 
						|
    createGUI();
 | 
						|
    blockApplySettings(true);
 | 
						|
    getRollupContents()->restoreState(m_rollupState);
 | 
						|
    blockApplySettings(false);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::onMenuDialogCalled(const QPoint &p)
 | 
						|
{
 | 
						|
    if (m_contextMenuType == ContextMenuChannelSettings)
 | 
						|
    {
 | 
						|
        BasicFeatureSettingsDialog dialog(this);
 | 
						|
        dialog.setTitle(m_settings.m_title);
 | 
						|
        dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
 | 
						|
        dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
 | 
						|
        dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
 | 
						|
        dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
 | 
						|
        dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
 | 
						|
        dialog.setDefaultTitle(m_displayedName);
 | 
						|
 | 
						|
        dialog.move(p);
 | 
						|
        new DialogPositioner(&dialog, false);
 | 
						|
        dialog.exec();
 | 
						|
 | 
						|
        m_settings.m_title = dialog.getTitle();
 | 
						|
        m_settings.m_useReverseAPI = dialog.useReverseAPI();
 | 
						|
        m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
 | 
						|
        m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
 | 
						|
        m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
 | 
						|
        m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
 | 
						|
 | 
						|
        setTitle(m_settings.m_title);
 | 
						|
        setTitleColor(m_settings.m_rgbColor);
 | 
						|
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
 | 
						|
    resetContextMenuType();
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::createControls(RemoteControlDeviceGUI *gui, QBoxLayout *vBox, FlowLayout *flow, int &widgetCnt)
 | 
						|
{
 | 
						|
    // Create buttons to control the device
 | 
						|
    QGridLayout *controlsGrid = nullptr;
 | 
						|
 | 
						|
    if (gui->m_rcDevice->m_verticalControls)
 | 
						|
    {
 | 
						|
        controlsGrid = new QGridLayout();
 | 
						|
        vBox->addLayout(controlsGrid);
 | 
						|
    }
 | 
						|
    else if (!flow)
 | 
						|
    {
 | 
						|
        flow = new FlowLayout(2, 6, 6);
 | 
						|
        vBox->addItem(flow);
 | 
						|
    }
 | 
						|
 | 
						|
    int row = 0;
 | 
						|
    for (auto const &control : gui->m_rcDevice->m_controls)
 | 
						|
    {
 | 
						|
        if (!gui->m_rcDevice->m_verticalControls && (widgetCnt > 0))
 | 
						|
        {
 | 
						|
            QFrame *line = new QFrame();
 | 
						|
            line->setFrameShape(QFrame::VLine);
 | 
						|
            line->setFrameShadow(QFrame::Sunken);
 | 
						|
            flow->addWidget(line);
 | 
						|
        }
 | 
						|
 | 
						|
        DeviceDiscoverer::ControlInfo *info = gui->m_rcDevice->m_info.getControl(control.m_id);
 | 
						|
        if (!info)
 | 
						|
        {
 | 
						|
            qDebug() << "RemoteControlGUI::createControls: Info missing for " << control.m_id;
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!control.m_labelLeft.isEmpty())
 | 
						|
        {
 | 
						|
            QLabel *controlLabelLeft = new QLabel(control.m_labelLeft);
 | 
						|
            if (gui->m_rcDevice->m_verticalControls)
 | 
						|
            {
 | 
						|
                controlsGrid->addWidget(controlLabelLeft, row, 0);
 | 
						|
                controlsGrid->setColumnStretch(row, 0);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                flow->addWidget(controlLabelLeft);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        QList<QWidget *> widgets;
 | 
						|
        QWidget *widget = nullptr;
 | 
						|
 | 
						|
        switch (info->m_type)
 | 
						|
        {
 | 
						|
        case DeviceDiscoverer::BOOL:
 | 
						|
            {
 | 
						|
                ButtonSwitch *button = new ButtonSwitch();
 | 
						|
                button->setToolTip("Start/stop " + info->m_name);
 | 
						|
                button->setIcon(m_startStopIcon);
 | 
						|
                button->setStyleSheet("QToolButton { background-color : blue; }"
 | 
						|
                                      "QToolButton:checked { background-color : green; }"
 | 
						|
                                      "QToolButton:disabled { background-color : gray; }");
 | 
						|
                connect(button, &ButtonSwitch::toggled,
 | 
						|
                    [=] (bool toggled)
 | 
						|
                    {
 | 
						|
                        RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                 gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                 control.m_id,
 | 
						|
                                                                                                 toggled);
 | 
						|
                        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                    }
 | 
						|
                );
 | 
						|
                widgets.append(button);
 | 
						|
                widget = button;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case DeviceDiscoverer::INT:
 | 
						|
            {
 | 
						|
                QSpinBox *spinBox = new QSpinBox();
 | 
						|
 | 
						|
                spinBox->setToolTip("Set value for " + info->m_name);
 | 
						|
                spinBox->setMinimum((int)info->m_min);
 | 
						|
                spinBox->setMaximum((int)info->m_max);
 | 
						|
                connect(spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
 | 
						|
                    [=] (int value)
 | 
						|
                    {
 | 
						|
                        RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                 gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                 control.m_id,
 | 
						|
                                                                                                 value);
 | 
						|
                        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                    }
 | 
						|
                );
 | 
						|
                widgets.append(spinBox);
 | 
						|
                widget = spinBox;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case DeviceDiscoverer::FLOAT:
 | 
						|
            {
 | 
						|
                switch (info->m_widgetType)
 | 
						|
                {
 | 
						|
                case DeviceDiscoverer::SPIN_BOX:
 | 
						|
                    {
 | 
						|
                        QDoubleSpinBox *spinBox = new SciDoubleSpinBox();
 | 
						|
 | 
						|
                        spinBox->setToolTip("Set value for " + info->m_name);
 | 
						|
                        spinBox->setMinimum(info->m_min);
 | 
						|
                        spinBox->setMaximum(info->m_max);
 | 
						|
                        spinBox->setDecimals(info->m_precision);
 | 
						|
                        connect(spinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
 | 
						|
                            [=] (double value)
 | 
						|
                            {
 | 
						|
                                RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                         gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                         control.m_id,
 | 
						|
                                                                                                         (float)value * info->m_scale);
 | 
						|
                                m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                            }
 | 
						|
                        );
 | 
						|
                        widgets.append(spinBox);
 | 
						|
                        widget = spinBox;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case DeviceDiscoverer::DIAL:
 | 
						|
                    {
 | 
						|
                        widget = new QWidget();
 | 
						|
                        QHBoxLayout *layout = new QHBoxLayout();
 | 
						|
                        layout->setContentsMargins(0, 0, 0, 0);
 | 
						|
                        widget->setLayout(layout);
 | 
						|
 | 
						|
                        QDial *dial = new QDial();
 | 
						|
                        dial->setMaximumSize(24, 24);
 | 
						|
                        dial->setToolTip("Set value for " + info->m_name);
 | 
						|
                        dial->setMinimum(info->m_min);
 | 
						|
                        dial->setMaximum(info->m_max);
 | 
						|
 | 
						|
                        connect(dial, static_cast<void (QDial::*)(int)>(&QDial::valueChanged),
 | 
						|
                            [=] (int value)
 | 
						|
                            {
 | 
						|
                                RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                         gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                         control.m_id,
 | 
						|
                                                                                                         ((float)value) * info->m_scale);
 | 
						|
                                m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                            }
 | 
						|
                        );
 | 
						|
                        widgets.append(dial);
 | 
						|
                        layout->addWidget(dial);
 | 
						|
 | 
						|
                        QLabel *label = new QLabel(QString::number(dial->value()));
 | 
						|
                        label->setToolTip("Value for " + info->m_name);
 | 
						|
                        widgets.append(label);
 | 
						|
                        layout->addWidget(label);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case DeviceDiscoverer::SLIDER:
 | 
						|
                    {
 | 
						|
                        widget = new QWidget();
 | 
						|
                        QHBoxLayout *layout = new QHBoxLayout();
 | 
						|
                        layout->setContentsMargins(0, 0, 0, 0);
 | 
						|
                        widget->setLayout(layout);
 | 
						|
 | 
						|
                        QSlider *slider = new QSlider(Qt::Horizontal);
 | 
						|
                        slider->setToolTip("Set value for " + info->m_name);
 | 
						|
                        slider->setMinimum(info->m_min);
 | 
						|
                        slider->setMaximum(info->m_max);
 | 
						|
 | 
						|
                        connect(slider, static_cast<void (QSlider::*)(int)>(&QSlider::valueChanged),
 | 
						|
                            [=] (int value)
 | 
						|
                            {
 | 
						|
                                RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                         gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                         control.m_id,
 | 
						|
                                                                                                         ((float)value) * info->m_scale);
 | 
						|
                                m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                            }
 | 
						|
                        );
 | 
						|
                        widgets.append(slider);
 | 
						|
                        layout->addWidget(slider);
 | 
						|
 | 
						|
                        QLabel *label = new QLabel(QString::number(slider->value()));
 | 
						|
                        label->setToolTip("Value for " + info->m_name);
 | 
						|
                        widgets.append(label);
 | 
						|
                        layout->addWidget(label);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                }
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case DeviceDiscoverer::STRING:
 | 
						|
            {
 | 
						|
                QLineEdit *lineEdit = new QLineEdit();
 | 
						|
 | 
						|
                lineEdit->setToolTip("Set value for " + info->m_name);
 | 
						|
 | 
						|
                connect(lineEdit, &QLineEdit::editingFinished,
 | 
						|
                    [=] ()
 | 
						|
                    {
 | 
						|
                        QString text = lineEdit->text();
 | 
						|
                        RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                 gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                 control.m_id,
 | 
						|
                                                                                                 text);
 | 
						|
                        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                    }
 | 
						|
                );
 | 
						|
                widgets.append(lineEdit);
 | 
						|
                widget = lineEdit;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case DeviceDiscoverer::LIST:
 | 
						|
            {
 | 
						|
                QComboBox *combo = new QComboBox();
 | 
						|
 | 
						|
                combo->setToolTip("Set value for " + info->m_name);
 | 
						|
                combo->insertItems(0, info->m_values);
 | 
						|
                connect(combo, &QComboBox::currentTextChanged,
 | 
						|
                    [=] (const QString &text)
 | 
						|
                    {
 | 
						|
                        RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                 gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                 control.m_id,
 | 
						|
                                                                                                 text);
 | 
						|
                        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                    }
 | 
						|
                );
 | 
						|
                widgets.append(combo);
 | 
						|
                widget = combo;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        case DeviceDiscoverer::BUTTON:
 | 
						|
            {
 | 
						|
                QString label = info->m_name;
 | 
						|
                if (info->m_values.size() > 0) {
 | 
						|
                    label = info->m_values[0];
 | 
						|
                }
 | 
						|
                QToolButton *button = new QToolButton();
 | 
						|
                button->setText(label);
 | 
						|
                button->setToolTip("Trigger " + info->m_name);
 | 
						|
 | 
						|
                connect(button, &QToolButton::clicked,
 | 
						|
                    [=] (bool checked)
 | 
						|
                    {
 | 
						|
                        (void) checked;
 | 
						|
                        RemoteControl::MsgDeviceSetState *message = RemoteControl::MsgDeviceSetState::create(gui->m_rcDevice->m_protocol,
 | 
						|
                                                                                                 gui->m_rcDevice->m_info.m_id,
 | 
						|
                                                                                                 control.m_id,
 | 
						|
                                                                                                 1);
 | 
						|
                        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
                    }
 | 
						|
                );
 | 
						|
                widgets.append(button);
 | 
						|
                widget = button;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
 | 
						|
        default:
 | 
						|
            qDebug() << "RemoteControlGUI::createControls: Unexpected type for control.";
 | 
						|
            break;
 | 
						|
 | 
						|
        }
 | 
						|
        gui->m_controls.insert(control.m_id, widgets);
 | 
						|
        if (gui->m_rcDevice->m_verticalControls) {
 | 
						|
            controlsGrid->addWidget(widget, row, 1);
 | 
						|
        } else {
 | 
						|
            flow->addWidget(widget);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!control.m_labelRight.isEmpty())
 | 
						|
        {
 | 
						|
            QLabel *controlLabelRight = new QLabel(control.m_labelRight);
 | 
						|
            if (gui->m_rcDevice->m_verticalControls)
 | 
						|
            {
 | 
						|
                controlsGrid->addWidget(controlLabelRight, row, 2);
 | 
						|
                controlsGrid->setColumnStretch(row, 2);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                flow->addWidget(controlLabelRight);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        widgetCnt++;
 | 
						|
        row++;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::createChart(RemoteControlDeviceGUI *gui, QVBoxLayout *vBox, const QString &id, const QString &units)
 | 
						|
{
 | 
						|
    if (gui->m_chart == nullptr)
 | 
						|
    {
 | 
						|
        // Create a chart to plot the sensor data
 | 
						|
        gui->m_chart = new QChart();
 | 
						|
        gui->m_chart->setTitle("");
 | 
						|
        gui->m_chart->legend()->hide();
 | 
						|
        gui->m_chart->layout()->setContentsMargins(0, 0, 0, 0);
 | 
						|
        gui->m_chart->setMargins(QMargins(1, 1, 1, 1));
 | 
						|
        gui->m_chart->setTheme(QChart::ChartThemeDark);
 | 
						|
        QLineSeries *series = new QLineSeries();
 | 
						|
        gui->m_series.insert(id, series);
 | 
						|
        QLineSeries *onePointSeries = new QLineSeries();
 | 
						|
        gui->m_onePointSeries.insert(id, onePointSeries);
 | 
						|
        gui->m_chart->addSeries(series);
 | 
						|
        QValueAxis *yAxis = new QValueAxis();
 | 
						|
        QDateTimeAxis *xAxis = new QDateTimeAxis();
 | 
						|
        xAxis->setFormat(QString("hh:mm:ss"));
 | 
						|
        yAxis->setTitleText(units);
 | 
						|
        gui->m_chart->addAxis(xAxis, Qt::AlignBottom);
 | 
						|
        gui->m_chart->addAxis(yAxis, Qt::AlignLeft);
 | 
						|
        series->attachAxis(xAxis);
 | 
						|
        series->attachAxis(yAxis);
 | 
						|
        gui->m_chartView = new QChartView();
 | 
						|
        gui->m_chartView->setChart(gui->m_chart);
 | 
						|
        if (m_settings.m_chartHeightFixed)
 | 
						|
        {
 | 
						|
            gui->m_chartView->setMinimumSize(300, m_settings.m_chartHeightPixels);
 | 
						|
            gui->m_chartView->setMaximumSize(16777215, m_settings.m_chartHeightPixels);
 | 
						|
            gui->m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            gui->m_chartView->setMinimumSize(300, 130); // 130 is enough to display axis labels
 | 
						|
            gui->m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 | 
						|
            gui->m_chartView->setSceneRect(0, 0, 300, 130); // This determines m_chartView->sizeHint() - default is 640x480, which is a bit big
 | 
						|
        }
 | 
						|
        QBoxLayout *chartLayout = new QVBoxLayout();
 | 
						|
        gui->m_chartView->setLayout(chartLayout);
 | 
						|
 | 
						|
        vBox->addWidget(gui->m_chartView);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Add new series
 | 
						|
        QLineSeries *series = new QLineSeries();
 | 
						|
        gui->m_series.insert(id, series);
 | 
						|
        QLineSeries *onePointSeries = new QLineSeries();
 | 
						|
        gui->m_onePointSeries.insert(id, onePointSeries);
 | 
						|
        gui->m_chart->addSeries(series);
 | 
						|
        if (!gui->m_rcDevice->m_commonYAxis)
 | 
						|
        {
 | 
						|
            // Use per series Y axis
 | 
						|
            QValueAxis *yAxis = new QValueAxis();
 | 
						|
            yAxis->setTitleText(units);
 | 
						|
            gui->m_chart->addAxis(yAxis, Qt::AlignRight);
 | 
						|
            series->attachAxis(yAxis);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Use common y axis
 | 
						|
            QAbstractAxis *yAxis = gui->m_chart->axes(Qt::Vertical)[0];
 | 
						|
            // Only display units if all the same
 | 
						|
            if (yAxis->titleText() != units) {
 | 
						|
                yAxis->setTitleText("");
 | 
						|
            }
 | 
						|
            series->attachAxis(yAxis);
 | 
						|
        }
 | 
						|
        series->attachAxis(gui->m_chart->axes(Qt::Horizontal)[0]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::createSensors(RemoteControlDeviceGUI *gui, QVBoxLayout *vBox, FlowLayout *flow, int &widgetCnt, bool &hasCharts)
 | 
						|
{
 | 
						|
    // Table doesn't seem to expand in a QHBoxLayout, so we have to use a GridLayout
 | 
						|
    QGridLayout *grid = nullptr;
 | 
						|
    QTableWidget *table = nullptr;
 | 
						|
    if (gui->m_rcDevice->m_verticalSensors)
 | 
						|
    {
 | 
						|
        grid = new QGridLayout();
 | 
						|
        grid->setColumnStretch(0, 1);
 | 
						|
        vBox->addLayout(grid);
 | 
						|
        table = new QTableWidget(gui->m_rcDevice->m_sensors.size(), 3);
 | 
						|
        table->verticalHeader()->setVisible(false);
 | 
						|
        table->horizontalHeader()->setVisible(false);
 | 
						|
        table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
 | 
						|
        table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
 | 
						|
        table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
 | 
						|
        table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
 | 
						|
        table->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);  // Needed so table->sizeHint matches minimumSize set below
 | 
						|
    }
 | 
						|
    else if (!flow)
 | 
						|
    {
 | 
						|
        flow = new FlowLayout(2, 6, 6);
 | 
						|
        vBox->addItem(flow);
 | 
						|
    }
 | 
						|
 | 
						|
    int row = 0;
 | 
						|
    bool hasUnits = false;
 | 
						|
    for (auto const &sensor : gui->m_rcDevice->m_sensors)
 | 
						|
    {
 | 
						|
        // For vertical layout, we use a table
 | 
						|
        // For horizontal, we use HBox of labels separated with bars
 | 
						|
        if (gui->m_rcDevice->m_verticalSensors)
 | 
						|
        {
 | 
						|
            if (!sensor.m_labelLeft.isEmpty())
 | 
						|
            {
 | 
						|
                QTableWidgetItem *sensorLabel = new QTableWidgetItem(sensor.m_labelLeft);
 | 
						|
                sensorLabel->setFlags(Qt::ItemIsEnabled);
 | 
						|
                table->setItem(row, COL_LABEL, sensorLabel);
 | 
						|
            }
 | 
						|
            QTableWidgetItem *valueItem = new QTableWidgetItem("-");
 | 
						|
            table->setItem(row, COL_VALUE, valueItem);
 | 
						|
            valueItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
 | 
						|
            valueItem->setFlags(Qt::ItemIsEnabled);
 | 
						|
            if (!sensor.m_labelRight.isEmpty())
 | 
						|
            {
 | 
						|
                QTableWidgetItem *unitsItem = new QTableWidgetItem(sensor.m_labelRight);
 | 
						|
                unitsItem->setFlags(Qt::ItemIsEnabled);
 | 
						|
                table->setItem(row, COL_UNITS, unitsItem);
 | 
						|
                hasUnits = true;
 | 
						|
            }
 | 
						|
            gui->m_sensorValueItems.insert(sensor.m_id, valueItem);
 | 
						|
            grid->addWidget(table, 0, 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if (widgetCnt > 0)
 | 
						|
            {
 | 
						|
                QFrame *line = new QFrame();
 | 
						|
                line->setFrameShape(QFrame::VLine);
 | 
						|
                line->setFrameShadow(QFrame::Sunken);
 | 
						|
                flow->addWidget(line);
 | 
						|
            }
 | 
						|
            if (!sensor.m_labelLeft.isEmpty())
 | 
						|
            {
 | 
						|
                QLabel *sensorLabel = new QLabel(sensor.m_labelLeft);
 | 
						|
                flow->addWidget(sensorLabel);
 | 
						|
            }
 | 
						|
            QLabel *sensorValue = new QLabel("-");
 | 
						|
            flow->addWidget(sensorValue);
 | 
						|
            if (!sensor.m_labelRight.isEmpty())
 | 
						|
            {
 | 
						|
                QLabel *sensorUnits = new QLabel(sensor.m_labelRight);
 | 
						|
                flow->addWidget(sensorUnits);
 | 
						|
            }
 | 
						|
            gui->m_sensorValueLabels.insert(sensor.m_id, sensorValue);
 | 
						|
        }
 | 
						|
 | 
						|
        if (sensor.m_plot)
 | 
						|
        {
 | 
						|
            createChart(gui, vBox, sensor.m_id, sensor.m_labelRight);
 | 
						|
            hasCharts = true;
 | 
						|
        }
 | 
						|
 | 
						|
        widgetCnt++;
 | 
						|
        row++;
 | 
						|
    }
 | 
						|
 | 
						|
    if (table)
 | 
						|
    {
 | 
						|
        table->resizeColumnToContents(COL_LABEL);
 | 
						|
        if (hasUnits) {
 | 
						|
            table->resizeColumnToContents(COL_UNITS);
 | 
						|
        } else {
 | 
						|
            table->hideColumn(COL_UNITS);
 | 
						|
        }
 | 
						|
 | 
						|
        int tableWidth = 0;
 | 
						|
        for (int i = 0; i < table->columnCount(); i++){
 | 
						|
           tableWidth += table->columnWidth(i);
 | 
						|
        }
 | 
						|
        int tableHeight = 0;
 | 
						|
        for (int i = 0; i < table->rowCount(); i++){
 | 
						|
           tableHeight += table->rowHeight(i);
 | 
						|
        }
 | 
						|
        table->setMinimumWidth(tableWidth);
 | 
						|
        table->setMinimumHeight(tableHeight+2);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
RemoteControlGUI::RemoteControlDeviceGUI *RemoteControlGUI::createDeviceGUI(RemoteControlDevice *rcDevice)
 | 
						|
{
 | 
						|
    // Create the UI for the device
 | 
						|
    RemoteControlDeviceGUI *gui = new RemoteControlDeviceGUI(rcDevice);
 | 
						|
 | 
						|
    bool hasCharts = false;
 | 
						|
 | 
						|
    gui->m_container = new QWidget(getRollupContents());
 | 
						|
    gui->m_container->setWindowTitle(gui->m_rcDevice->m_label);
 | 
						|
    bool vertical = gui->m_rcDevice->m_verticalControls || gui->m_rcDevice->m_verticalSensors;
 | 
						|
    QVBoxLayout *vBox = new QVBoxLayout();
 | 
						|
    vBox->setContentsMargins(2, 2, 2, 2);
 | 
						|
    FlowLayout *flow = nullptr;
 | 
						|
 | 
						|
    if (!vertical)
 | 
						|
    {
 | 
						|
        flow = new FlowLayout(2, 6, 6);
 | 
						|
        vBox->addItem(flow);
 | 
						|
    }
 | 
						|
    int widgetCnt = 0;
 | 
						|
 | 
						|
    // Create buttons to control the device
 | 
						|
    createControls(gui, vBox, flow, widgetCnt);
 | 
						|
 | 
						|
    if (gui->m_rcDevice->m_verticalControls) {
 | 
						|
        widgetCnt = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    // Create widgets to display the sensor label and its value
 | 
						|
    createSensors(gui, vBox, flow, widgetCnt, hasCharts);
 | 
						|
 | 
						|
    gui->m_container->setLayout(vBox);
 | 
						|
 | 
						|
    if (hasCharts && !m_settings.m_chartHeightFixed) {
 | 
						|
        gui->m_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 | 
						|
    }
 | 
						|
 | 
						|
    gui->m_container->show();
 | 
						|
    return gui;
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::createGUI()
 | 
						|
{
 | 
						|
    // Delete existing elements
 | 
						|
    for (auto gui : m_deviceGUIs)
 | 
						|
    {
 | 
						|
        delete gui->m_container;
 | 
						|
        gui->m_container = nullptr;
 | 
						|
    }
 | 
						|
    qDeleteAll(m_deviceGUIs);
 | 
						|
    m_deviceGUIs.clear();
 | 
						|
 | 
						|
    // Create new GUIs for each device
 | 
						|
    bool expanding = false;
 | 
						|
    for (auto device : m_settings.m_devices)
 | 
						|
    {
 | 
						|
        RemoteControlDeviceGUI *gui = createDeviceGUI(device);
 | 
						|
        m_deviceGUIs.append(gui);
 | 
						|
        if (gui->m_container->sizePolicy().verticalPolicy() == QSizePolicy::Expanding) {
 | 
						|
            expanding = true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (expanding)
 | 
						|
    {
 | 
						|
        getRollupContents()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 | 
						|
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        getRollupContents()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
 | 
						|
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
 | 
						|
    }
 | 
						|
 | 
						|
    // FIXME: Why are these three steps needed to get the window
 | 
						|
    // to resize to the newly added widgets?
 | 
						|
    getRollupContents()->arrangeRollups(); // Recalc rollup size
 | 
						|
    layout()->activate(); // Get QMdiSubWindow to recalc its sizeHint
 | 
						|
    resize(sizeHint());
 | 
						|
 | 
						|
    // Need to do it twice when FlowLayout is used!
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
    layout()->activate();
 | 
						|
    resize(sizeHint());
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::on_startStop_toggled(bool checked)
 | 
						|
{
 | 
						|
    if (m_doApplySettings)
 | 
						|
    {
 | 
						|
        RemoteControl::MsgStartStop *message = RemoteControl::MsgStartStop::create(checked);
 | 
						|
        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::on_update_clicked()
 | 
						|
{
 | 
						|
    RemoteControl::MsgDeviceGetState *message = RemoteControl::MsgDeviceGetState::create();
 | 
						|
    m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::on_settings_clicked()
 | 
						|
{
 | 
						|
    // Display settings dialog
 | 
						|
    RemoteControlSettingsDialog dialog(&m_settings);
 | 
						|
    if (dialog.exec() == QDialog::Accepted)
 | 
						|
    {
 | 
						|
        createGUI();
 | 
						|
        applySettings();
 | 
						|
        on_update_clicked();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::on_clearData_clicked()
 | 
						|
{
 | 
						|
    // Clear data in all charts
 | 
						|
    for (auto deviceGUI : m_deviceGUIs)
 | 
						|
    {
 | 
						|
        for (auto series : deviceGUI->m_series) {
 | 
						|
            series->clear();
 | 
						|
        }
 | 
						|
        for (auto series : deviceGUI->m_onePointSeries) {
 | 
						|
            series->clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Update a control widget with latest state value
 | 
						|
void RemoteControlGUI::updateControl(QWidget *widget, const DeviceDiscoverer::ControlInfo *controlInfo, const QString &key, const QVariant &value)
 | 
						|
{
 | 
						|
    if (ButtonSwitch *button = qobject_cast<ButtonSwitch *>(widget))
 | 
						|
    {
 | 
						|
        if ((QMetaType::Type)value.type() == QMetaType::QString)
 | 
						|
        {
 | 
						|
            if (value.toString() == "unavailable")
 | 
						|
            {
 | 
						|
                button->setStyleSheet("QToolButton { background-color : gray; }"
 | 
						|
                                      "QToolButton:checked { background-color : gray; }"
 | 
						|
                                      "QToolButton:disabled { background-color : gray; }");
 | 
						|
            }
 | 
						|
            else if (value.toString() == "error")
 | 
						|
            {
 | 
						|
                button->setStyleSheet("QToolButton { background-color : red; }"
 | 
						|
                                      "QToolButton:checked { background-color : red; }"
 | 
						|
                                      "QToolButton:disabled { background-color : red; }");
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "RemoteControlGUI::updateControl: String value for button " << key << value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            int state = value.toInt();
 | 
						|
            int prev = button->blockSignals(true);
 | 
						|
            button->setChecked(state != 0);
 | 
						|
            button->blockSignals(prev);
 | 
						|
            button->setStyleSheet("QToolButton { background-color : blue; }"
 | 
						|
                                  "QToolButton:checked { background-color : green; }"
 | 
						|
                                  "QToolButton:disabled { background-color : gray; }");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (QSpinBox *spinBox = qobject_cast<QSpinBox *>(widget))
 | 
						|
    {
 | 
						|
        int prev = spinBox->blockSignals(true);
 | 
						|
        if (value.toString() == "unavailable")
 | 
						|
        {
 | 
						|
            spinBox->setStyleSheet("QSpinBox { background-color : gray; }");
 | 
						|
        }
 | 
						|
        else if (value.toString() == "error")
 | 
						|
        {
 | 
						|
            spinBox->setStyleSheet("QSpinBox { background-color : red; }");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            int state = value.toInt();
 | 
						|
            bool outOfRange = (state < spinBox->minimum()) || (state > spinBox->maximum());
 | 
						|
            spinBox->setValue(state);
 | 
						|
            if (outOfRange) {
 | 
						|
                spinBox->setStyleSheet("QSpinBox { background-color : red; }");
 | 
						|
            } else {
 | 
						|
                spinBox->setStyleSheet("");
 | 
						|
            }
 | 
						|
        }
 | 
						|
        spinBox->blockSignals(prev);
 | 
						|
    }
 | 
						|
    else if (QDoubleSpinBox *spinBox = qobject_cast<QDoubleSpinBox *>(widget))
 | 
						|
    {
 | 
						|
        int prev = spinBox->blockSignals(true);
 | 
						|
        if (value.toString() == "unavailable")
 | 
						|
        {
 | 
						|
            spinBox->setStyleSheet("QDoubleSpinBox { background-color : gray; }");
 | 
						|
        }
 | 
						|
        else if (value.toString() == "error")
 | 
						|
        {
 | 
						|
            spinBox->setStyleSheet("QDoubleSpinBox { background-color : red; }");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            double state = value.toDouble();
 | 
						|
            if (controlInfo) {
 | 
						|
                state = state / controlInfo->m_scale;
 | 
						|
            }
 | 
						|
            bool outOfRange = (state < spinBox->minimum()) || (state > spinBox->maximum());
 | 
						|
            spinBox->setValue(state);
 | 
						|
            if (outOfRange) {
 | 
						|
                spinBox->setStyleSheet("QDoubleSpinBox { background-color : red; }");
 | 
						|
            } else {
 | 
						|
                spinBox->setStyleSheet("");
 | 
						|
            }
 | 
						|
        }
 | 
						|
        spinBox->blockSignals(prev);
 | 
						|
    }
 | 
						|
    else if (QDial *dial = qobject_cast<QDial *>(widget))
 | 
						|
    {
 | 
						|
        int prev = dial->blockSignals(true);
 | 
						|
        if (value.toString() == "unavailable")
 | 
						|
        {
 | 
						|
            dial->setStyleSheet("QDial { background-color : gray; }");
 | 
						|
        }
 | 
						|
        else if (value.toString() == "error")
 | 
						|
        {
 | 
						|
            dial->setStyleSheet("QDial { background-color : red; }");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            double state = value.toDouble();
 | 
						|
            if (controlInfo) {
 | 
						|
                state = state / controlInfo->m_scale;
 | 
						|
            }
 | 
						|
            bool outOfRange = (state < dial->minimum()) || (state > dial->maximum());
 | 
						|
            dial->setValue(state);
 | 
						|
            if (outOfRange) {
 | 
						|
                dial->setStyleSheet("QDial { background-color : red; }");
 | 
						|
            } else {
 | 
						|
                dial->setStyleSheet("");
 | 
						|
            }
 | 
						|
        }
 | 
						|
        dial->blockSignals(prev);
 | 
						|
    }
 | 
						|
    else if (QSlider *slider = qobject_cast<QSlider *>(widget))
 | 
						|
    {
 | 
						|
        int prev = slider->blockSignals(true);
 | 
						|
        if (value.toString() == "unavailable")
 | 
						|
        {
 | 
						|
            slider->setStyleSheet("QSlider { background-color : gray; }");
 | 
						|
        }
 | 
						|
        else if (value.toString() == "error")
 | 
						|
        {
 | 
						|
            slider->setStyleSheet("QSlider { background-color : red; }");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            double state = value.toDouble();
 | 
						|
            if (controlInfo) {
 | 
						|
                state = state / controlInfo->m_scale;
 | 
						|
            }
 | 
						|
            bool outOfRange = (state < slider->minimum()) || (state > slider->maximum());
 | 
						|
            slider->setValue(state);
 | 
						|
            if (outOfRange) {
 | 
						|
                slider->setStyleSheet("QSlider { background-color : red; }");
 | 
						|
            } else {
 | 
						|
                slider->setStyleSheet("");
 | 
						|
            }
 | 
						|
        }
 | 
						|
        slider->blockSignals(prev);
 | 
						|
    }
 | 
						|
    else if (QComboBox *comboBox = qobject_cast<QComboBox *>(widget))
 | 
						|
    {
 | 
						|
        int prev = comboBox->blockSignals(true);
 | 
						|
        QString string = value.toString();
 | 
						|
        int index = comboBox->findText(string);
 | 
						|
        if (index != -1)
 | 
						|
        {
 | 
						|
            comboBox->setCurrentIndex(index);
 | 
						|
            comboBox->setStyleSheet("");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            comboBox->setStyleSheet("QComboBox { background-color : red; }");
 | 
						|
        }
 | 
						|
        comboBox->blockSignals(prev);
 | 
						|
    }
 | 
						|
    else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget))
 | 
						|
    {
 | 
						|
        lineEdit->setText(value.toString());
 | 
						|
    }
 | 
						|
    else if (QLabel *label = qobject_cast<QLabel *>(widget))
 | 
						|
    {
 | 
						|
        label->setText(value.toString());
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "RemoteControlGUI::updateControl: Unexpected widget type";
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::updateChart(RemoteControlDeviceGUI *deviceGUI, const QString &key, const QVariant &value)
 | 
						|
{
 | 
						|
    // Format the value for display
 | 
						|
    bool ok = false;
 | 
						|
    double d = value.toDouble(&ok);
 | 
						|
    bool iOk = false;
 | 
						|
    int iValue = value.toInt(&iOk);
 | 
						|
    QString formattedValue;
 | 
						|
    RemoteControlSensor *sensor = deviceGUI->m_rcDevice->getSensor(key);
 | 
						|
    QString format = sensor->m_format.trimmed();
 | 
						|
    if (format.contains("%s"))
 | 
						|
    {
 | 
						|
        formattedValue = QString::asprintf(format.toUtf8(), value.toString().toUtf8().data());
 | 
						|
    }
 | 
						|
    else if (format.contains("%d") || format.contains("%u") || format.contains("%x") || format.contains("%X"))
 | 
						|
    {
 | 
						|
        formattedValue = QString::asprintf(format.toUtf8(), value.toInt());
 | 
						|
    }
 | 
						|
    else if (((QMetaType::Type)value.type() == QMetaType::Double) || ((QMetaType::Type)value.type() == QMetaType::Float))
 | 
						|
    {
 | 
						|
        if (format.isEmpty()) {
 | 
						|
            format = "%.1f";
 | 
						|
        }
 | 
						|
        formattedValue = QString::asprintf(format.toUtf8(), value.toDouble());
 | 
						|
    }
 | 
						|
    else if (iOk)
 | 
						|
    {
 | 
						|
        formattedValue = QString::asprintf("%d", iValue);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        formattedValue = value.toString();
 | 
						|
    }
 | 
						|
 | 
						|
    // Update sensor value widget to display the latest value
 | 
						|
    if (deviceGUI->m_sensorValueLabels.contains(key)) {
 | 
						|
        deviceGUI->m_sensorValueLabels.value(key)->setText(formattedValue);
 | 
						|
    } else {
 | 
						|
        deviceGUI->m_sensorValueItems.value(key)->setText(formattedValue);
 | 
						|
    }
 | 
						|
 | 
						|
    // Plot value on chart
 | 
						|
    if (deviceGUI->m_series.contains(key))
 | 
						|
    {
 | 
						|
        QLineSeries *onePointSeries = deviceGUI->m_onePointSeries.value(key);
 | 
						|
        QLineSeries *series = deviceGUI->m_series.value(key);
 | 
						|
        QDateTime dt = QDateTime::currentDateTime();
 | 
						|
        if (ok)
 | 
						|
        {
 | 
						|
            // Charts aren't displayed properly if series has only one point,
 | 
						|
            // so we save the first point in an additional series: onePointSeries
 | 
						|
            if (onePointSeries->count() == 0)
 | 
						|
            {
 | 
						|
                onePointSeries->append(dt.toMSecsSinceEpoch(), d);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                if (series->count() == 0) {
 | 
						|
                    series->append(onePointSeries->at(0));
 | 
						|
                }
 | 
						|
                series->append(dt.toMSecsSinceEpoch(), d);
 | 
						|
                QList<QAbstractAxis *> axes = deviceGUI->m_chart->axes(Qt::Horizontal, series);
 | 
						|
                QDateTimeAxis *dtAxis = (QDateTimeAxis *)axes[0];
 | 
						|
                QDateTime start = QDateTime::fromMSecsSinceEpoch(series->at(0).x());
 | 
						|
                QDateTime end = QDateTime::fromMSecsSinceEpoch(series->at(series->count() - 1).x());
 | 
						|
                if (start.date() == end.date())
 | 
						|
                {
 | 
						|
                    if (start.secsTo(end) < 60*5) {
 | 
						|
                        dtAxis->setFormat(QString("hh:mm:ss"));
 | 
						|
                    } else {
 | 
						|
                        dtAxis->setFormat(QString("hh:mm"));
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    dtAxis->setFormat(QString("%1 hh:mm").arg(QLocale::system().dateFormat(QLocale::ShortFormat)));
 | 
						|
                }
 | 
						|
                dtAxis->setRange(start, end);
 | 
						|
                axes = deviceGUI->m_chart->axes(Qt::Vertical, series);
 | 
						|
                QValueAxis *yAxis = (QValueAxis *)axes[0];
 | 
						|
                if (series->count() == 2)
 | 
						|
                {
 | 
						|
                    double y1 = series->at(0).y();
 | 
						|
                    double y2 = series->at(1).y();
 | 
						|
                    double yMin = std::min(y1, y2);
 | 
						|
                    double yMax = std::max(y1, y2);
 | 
						|
                    double min = (yMin >= 0.0) ? yMin * 0.9 : yMin * 1.1;
 | 
						|
                    double max = (yMax >= 0.0) ? yMax * 1.1 : yMax * 0.9;
 | 
						|
                    yAxis->setRange(min, max);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    double min = (d >= 0.0) ? d * 0.9 : d * 1.1;
 | 
						|
                    double max = (d >= 0.0) ? d * 1.1 : d * 0.9;
 | 
						|
                    if (min < yAxis->min()) {
 | 
						|
                        yAxis->setMin(min);
 | 
						|
                    }
 | 
						|
                    if (max > yAxis->max()) {
 | 
						|
                        yAxis->setMax(max);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RemoteControlGUI::deviceUpdated: Error converting " << key << value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::deviceUpdated(const QString &protocol, const QString &deviceId, const QHash<QString, QVariant> &status)
 | 
						|
{
 | 
						|
    for (auto deviceGUI : m_deviceGUIs)
 | 
						|
    {
 | 
						|
        if (   (protocol == deviceGUI->m_rcDevice->m_protocol)
 | 
						|
            && (deviceId == deviceGUI->m_rcDevice->m_info.m_id))
 | 
						|
        {
 | 
						|
            deviceGUI->m_container->setEnabled(true);
 | 
						|
 | 
						|
            QHashIterator<QString, QVariant> itr(status);
 | 
						|
 | 
						|
            while (itr.hasNext())
 | 
						|
            {
 | 
						|
                itr.next();
 | 
						|
                QString key = itr.key();
 | 
						|
                QVariant value = itr.value();
 | 
						|
 | 
						|
                if (deviceGUI->m_controls.contains(key))
 | 
						|
                {
 | 
						|
                    // Update control(s) to display latest state
 | 
						|
                    QList<QWidget *> widgets = deviceGUI->m_controls.value(key);
 | 
						|
                    DeviceDiscoverer::ControlInfo *control = deviceGUI->m_rcDevice->m_info.getControl(key);
 | 
						|
 | 
						|
                    for (auto widget : widgets) {
 | 
						|
                        updateControl(widget, control, key, value);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else if (deviceGUI->m_sensorValueLabels.contains(key) || deviceGUI->m_sensorValueItems.contains(key))
 | 
						|
                {
 | 
						|
                    // Plot on chart
 | 
						|
                    updateChart(deviceGUI, key, value);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    qDebug() << "RemoteControlGUI::deviceUpdated: Unexpected status key " << key << value;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::deviceUnavailable(const QString &protocol, const QString &deviceId)
 | 
						|
{
 | 
						|
    for (auto deviceGUI : m_deviceGUIs)
 | 
						|
    {
 | 
						|
        if (   (protocol == deviceGUI->m_rcDevice->m_protocol)
 | 
						|
            && (deviceId == deviceGUI->m_rcDevice->m_info.m_id))
 | 
						|
        {
 | 
						|
            deviceGUI->m_container->setEnabled(false);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::applySettings(bool force)
 | 
						|
{
 | 
						|
    if (m_doApplySettings)
 | 
						|
    {
 | 
						|
        RemoteControl::MsgConfigureRemoteControl* message = RemoteControl::MsgConfigureRemoteControl::create(m_settings, force);
 | 
						|
        m_remoteControl->getInputMessageQueue()->push(message);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RemoteControlGUI::makeUIConnections()
 | 
						|
{
 | 
						|
    QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteControlGUI::on_startStop_toggled);
 | 
						|
    QObject::connect(ui->update, &QToolButton::clicked, this, &RemoteControlGUI::on_update_clicked);
 | 
						|
    QObject::connect(ui->settings, &QToolButton::clicked, this, &RemoteControlGUI::on_settings_clicked);
 | 
						|
    QObject::connect(ui->clearData, &QToolButton::clicked, this, &RemoteControlGUI::on_clearData_clicked);
 | 
						|
}
 |