mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 13:00:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1176 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1176 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| ///////////////////////////////////////////////////////////////////////////////////
 | |
| // Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com>                //
 | |
| //                                                                               //
 | |
| // 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();
 | |
|     m_resizer.enableChildMouseTracking();
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 |