File indexing completed on 2024-05-12 15:27:50
0001 /*************************************************************************** 0002 File : ImportFileWidget.cpp 0003 Project : LabPlot 0004 Description : import file data widget 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2009-2018 Stefan Gerlach (stefan.gerlach@uni.kn) 0007 Copyright : (C) 2009-2020 Alexander Semke (alexander.semke@web.de) 0008 Copyright : (C) 2017-2018 Fabian Kristof (fkristofszabolcs@gmail.com) 0009 Copyright : (C) 2018-2019 Kovacs Ferencz (kferike98@gmail.com) 0010 0011 ***************************************************************************/ 0012 0013 /*************************************************************************** 0014 * * 0015 * This program is free software; you can redistribute it and/or modify * 0016 * it under the terms of the GNU General Public License as published by * 0017 * the Free Software Foundation; either version 2 of the License, or * 0018 * (at your option) any later version. * 0019 * * 0020 * This program is distributed in the hope that it will be useful, * 0021 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0023 * GNU General Public License for more details. * 0024 * * 0025 * You should have received a copy of the GNU General Public License * 0026 * along with this program; if not, write to the Free Software * 0027 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0028 * Boston, MA 02110-1301 USA * 0029 * * 0030 ***************************************************************************/ 0031 0032 #include "ImportFileWidget.h" 0033 #include "FileInfoDialog.h" 0034 #include "backend/datasources/filters/filters.h" 0035 #include "AsciiOptionsWidget.h" 0036 #include "BinaryOptionsWidget.h" 0037 #include "HDF5OptionsWidget.h" 0038 #include "ImageOptionsWidget.h" 0039 #include "NetCDFOptionsWidget.h" 0040 #include "FITSOptionsWidget.h" 0041 #include "JsonOptionsWidget.h" 0042 #include "ROOTOptionsWidget.h" 0043 0044 #include <QCompleter> 0045 #include <QDir> 0046 #include <QDirModel> 0047 #include <QFileDialog> 0048 #include <QInputDialog> 0049 #include <QIntValidator> 0050 #include <QLocalSocket> 0051 #include <QStandardItemModel> 0052 #include <QTableWidget> 0053 #include <QTcpSocket> 0054 #include <QTimer> 0055 #include <QUdpSocket> 0056 #include <QTreeWidgetItem> 0057 0058 #include <KConfigGroup> 0059 #include <KLocalizedString> 0060 #include <KSharedConfig> 0061 #include <KUrlComboBox> 0062 0063 #ifdef HAVE_MQTT 0064 #include "kdefrontend/widgets/MQTTWillSettingsWidget.h" 0065 #include "MQTTConnectionManagerDialog.h" 0066 #include "MQTTSubscriptionWidget.h" 0067 #include <QMqttClient> 0068 #include <QMqttSubscription> 0069 #include <QMqttTopicFilter> 0070 #include <QMqttMessage> 0071 #include <QMessageBox> 0072 #include <QWidgetAction> 0073 #include <QMenu> 0074 #endif 0075 0076 QString absolutePath(const QString& fileName) { 0077 #ifdef HAVE_WINDOWS 0078 if (!fileName.isEmpty() && fileName.at(1) != QLatin1String(":")) 0079 #else 0080 if (!fileName.isEmpty() && fileName.at(0) != QLatin1String("/")) 0081 #endif 0082 return QDir::homePath() + QLatin1String("/") + fileName; 0083 0084 return fileName; 0085 } 0086 0087 /*! 0088 \class ImportFileWidget 0089 \brief Widget for importing data from a file. 0090 0091 \ingroup kdefrontend 0092 */ 0093 ImportFileWidget::ImportFileWidget(QWidget* parent, bool liveDataSource, const QString& fileName) : QWidget(parent), 0094 m_fileName(fileName), 0095 m_liveDataSource(liveDataSource) 0096 #ifdef HAVE_MQTT 0097 , 0098 m_subscriptionWidget(new MQTTSubscriptionWidget(this)) 0099 #endif 0100 { 0101 ui.setupUi(this); 0102 0103 //add supported file types 0104 if (!liveDataSource) { 0105 ui.cbFileType->addItem(i18n("ASCII data"), static_cast<int>(AbstractFileFilter::FileType::Ascii)); 0106 ui.cbFileType->addItem(i18n("Binary data"), static_cast<int>(AbstractFileFilter::FileType::Binary)); 0107 ui.cbFileType->addItem(i18n("Image"), static_cast<int>(AbstractFileFilter::FileType::Image)); 0108 #ifdef HAVE_HDF5 0109 ui.cbFileType->addItem(i18n("Hierarchical Data Format 5 (HDF5)"), static_cast<int>(AbstractFileFilter::FileType::HDF5)); 0110 #endif 0111 #ifdef HAVE_NETCDF 0112 ui.cbFileType->addItem(i18n("Network Common Data Format (NetCDF)"), static_cast<int>(AbstractFileFilter::FileType::NETCDF)); 0113 #endif 0114 #ifdef HAVE_FITS 0115 ui.cbFileType->addItem(i18n("Flexible Image Transport System Data Format (FITS)"), static_cast<int>(AbstractFileFilter::FileType::FITS)); 0116 #endif 0117 ui.cbFileType->addItem(i18n("JSON data"), static_cast<int>(AbstractFileFilter::FileType::JSON)); 0118 #ifdef HAVE_ZIP 0119 ui.cbFileType->addItem(i18n("ROOT (CERN)"), static_cast<int>(AbstractFileFilter::FileType::ROOT)); 0120 #endif 0121 ui.cbFileType->addItem(i18n("Ngspice RAW ASCII"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawAscii)); 0122 ui.cbFileType->addItem(i18n("Ngspice RAW Binary"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawBinary)); 0123 0124 //hide widgets relevant for live data reading only 0125 ui.lRelativePath->hide(); 0126 ui.chbRelativePath->hide(); 0127 ui.lSourceType->hide(); 0128 ui.cbSourceType->hide(); 0129 ui.gbUpdateOptions->hide(); 0130 } else { 0131 ui.cbFileType->addItem(i18n("ASCII data"), static_cast<int>(AbstractFileFilter::FileType::Ascii)); 0132 ui.cbFileType->addItem(i18n("Binary data"), static_cast<int>(AbstractFileFilter::FileType::Binary)); 0133 #ifdef HAVE_ZIP 0134 ui.cbFileType->addItem(i18n("ROOT (CERN)"), static_cast<int>(AbstractFileFilter::FileType::ROOT)); 0135 #endif 0136 ui.cbFileType->addItem(i18n("Ngspice RAW ASCII"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawAscii)); 0137 ui.cbFileType->addItem(i18n("Ngspice RAW Binary"), static_cast<int>(AbstractFileFilter::FileType::NgspiceRawBinary)); 0138 0139 ui.lePort->setValidator( new QIntValidator(ui.lePort) ); 0140 ui.cbBaudRate->addItems(LiveDataSource::supportedBaudRates()); 0141 ui.cbSerialPort->addItems(LiveDataSource::availablePorts()); 0142 0143 ui.tabWidget->removeTab(2); 0144 0145 ui.chbLinkFile->setToolTip(i18n("If this option is checked, only the link to the file is stored in the project file but not its content.")); 0146 ui.chbRelativePath->setToolTip(i18n("If this option is checked, the relative path of the file (relative to project's folder) will be saved.")); 0147 } 0148 0149 QStringList filterItems {i18n("Automatic"), i18n("Custom")}; 0150 ui.cbFilter->addItems(filterItems); 0151 0152 //hide options that will be activated on demand 0153 ui.gbOptions->hide(); 0154 ui.gbUpdateOptions->hide(); 0155 setMQTTVisible(false); 0156 0157 ui.cbReadingType->addItem(i18n("Whole file"), static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 0158 0159 ui.bOpen->setIcon( QIcon::fromTheme(QLatin1String("document-open")) ); 0160 ui.bFileInfo->setIcon( QIcon::fromTheme(QLatin1String("help-about")) ); 0161 ui.bManageFilters->setIcon( QIcon::fromTheme(QLatin1String("configure")) ); 0162 ui.bSaveFilter->setIcon( QIcon::fromTheme(QLatin1String("document-save")) ); 0163 ui.bRefreshPreview->setIcon( QIcon::fromTheme(QLatin1String("view-refresh")) ); 0164 0165 ui.tvJson->header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0166 ui.tvJson->setAlternatingRowColors(true); 0167 showJsonModel(false); 0168 0169 // the table widget for preview 0170 m_twPreview = new QTableWidget(ui.tePreview); 0171 m_twPreview->verticalHeader()->hide(); 0172 m_twPreview->setEditTriggers(QTableWidget::NoEditTriggers); 0173 auto* layout = new QHBoxLayout; 0174 layout->addWidget(m_twPreview); 0175 ui.tePreview->setLayout(layout); 0176 m_twPreview->hide(); 0177 0178 // the combobox for the import path 0179 m_cbFileName = new KUrlComboBox(KUrlComboBox::Mode::Files, this); 0180 m_cbFileName->setMaxItems(7); 0181 auto* gridLayout = dynamic_cast<QGridLayout*>(ui.gbDataSource->layout()); 0182 if (gridLayout) 0183 gridLayout->addWidget(m_cbFileName, 1, 2, 1, 3); 0184 0185 0186 //tooltips 0187 QString info = i18n("Specify how the data source has to be processed on every read:" 0188 "<ul>" 0189 "<li>Continuously fixed - fixed amount of samples is processed starting from the beginning of the newly received data.</li>" 0190 "<li>From End - fixed amount of samples is processed starting from the end of the newly received data.</li>" 0191 "<li>Till the End - all newly received data is processed.</li>" 0192 "<li>Whole file - on every read the whole file is re-read completely and processed. Only available for \"File Or Named Pipe\" data sources.</li>" 0193 "</ul>"); 0194 ui.lReadingType->setToolTip(info); 0195 ui.cbReadingType->setToolTip(info); 0196 0197 info = i18n("Number of samples (lines) to be processed on every read.\n" 0198 "Only needs to be specified for the reading mode \"Continuously Fixed\" and \"From End\"."); 0199 ui.lSampleSize->setToolTip(info); 0200 ui.sbSampleSize->setToolTip(info); 0201 0202 info = i18n("Specify when and how frequently the data source needs to be read:" 0203 "<ul>" 0204 "<li>Periodically - the data source is read periodically with user specified time interval.</li>" 0205 "<li>On New Data - the data source is read when new data arrives.</li>" 0206 "</ul>"); 0207 ui.lUpdateType->setToolTip(info); 0208 ui.cbUpdateType->setToolTip(info); 0209 0210 info = i18n("Specify how frequently the data source has to be read."); 0211 ui.lUpdateInterval->setToolTip(info); 0212 ui.sbUpdateInterval->setToolTip(info); 0213 0214 info = i18n("Specify how many samples need to be kept in memory after reading.\n" 0215 "Use \"All\" if all data has to be kept."); 0216 ui.lKeepLastValues->setToolTip(info); 0217 ui.sbKeepNValues->setToolTip(info); 0218 0219 #ifdef HAVE_MQTT 0220 ui.cbSourceType->addItem(QLatin1String("MQTT")); 0221 m_configPath = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).constFirst() + QLatin1String("MQTT_connections"); 0222 0223 //add subscriptions widget 0224 layout = new QHBoxLayout; 0225 layout->setContentsMargins(0, 0, 0, 0); 0226 layout->setSpacing(0); 0227 layout->addWidget(m_subscriptionWidget); 0228 ui.frameSubscriptions->setLayout(layout); 0229 0230 ui.bManageConnections->setIcon(QIcon::fromTheme(QLatin1String("network-server"))); 0231 ui.bManageConnections->setToolTip(i18n("Manage MQTT connections")); 0232 0233 info = i18n("Specify the 'Last Will and Testament' message (LWT). At least one topic has to be subscribed."); 0234 ui.lLWT->setToolTip(info); 0235 ui.bLWT->setToolTip(info); 0236 ui.bLWT->setEnabled(false); 0237 ui.bLWT->setIcon(ui.bLWT->style()->standardIcon(QStyle::SP_FileDialogDetailedView)); 0238 #endif 0239 0240 //TODO: implement save/load of user-defined settings later and activate these buttons again 0241 ui.bSaveFilter->hide(); 0242 ui.bManageFilters->hide(); 0243 } 0244 0245 void ImportFileWidget::loadSettings() { 0246 m_suppressRefresh = true; 0247 0248 //load last used settings 0249 QString confName; 0250 if (m_liveDataSource) 0251 confName = QLatin1String("LiveDataImport"); 0252 else 0253 confName = QLatin1String("FileImport"); 0254 KConfigGroup conf(KSharedConfig::openConfig(), confName); 0255 0256 //read the source type first since settings in fileNameChanged() depend on this 0257 ui.cbSourceType->setCurrentIndex(conf.readEntry("SourceType").toInt()); 0258 0259 //general settings 0260 AbstractFileFilter::FileType fileType = static_cast<AbstractFileFilter::FileType>(conf.readEntry("Type", 0)); 0261 for (int i = 0; i < ui.cbFileType->count(); ++i) { 0262 if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == fileType) { 0263 if (ui.cbFileType->currentIndex() == i) 0264 initOptionsWidget(); 0265 else 0266 ui.cbFileType->setCurrentIndex(i); 0267 0268 break; 0269 } 0270 } 0271 0272 if (m_fileName.isEmpty()) { 0273 ui.cbFilter->setCurrentIndex(conf.readEntry("Filter", 0)); 0274 m_cbFileName->setUrl(QUrl(conf.readEntry("LastImportedFile", ""))); 0275 QStringList urls = m_cbFileName->urls(); 0276 urls.append(conf.readXdgListEntry("LastImportedFiles")); 0277 m_cbFileName->setUrls(urls); 0278 filterChanged(ui.cbFilter->currentIndex()); // needed if filter is not changed 0279 } else 0280 m_cbFileName->setUrl(QUrl(m_fileName)); 0281 0282 ui.sbPreviewLines->setValue(conf.readEntry("PreviewLines", 100)); 0283 0284 //live data related settings 0285 ui.cbBaudRate->setCurrentIndex(conf.readEntry("BaudRate", 13)); // index for bautrate 19200b/s 0286 ui.cbReadingType->setCurrentIndex(conf.readEntry("ReadingType", static_cast<int>(LiveDataSource::ReadingType::WholeFile))); 0287 ui.cbSerialPort->setCurrentIndex(conf.readEntry("SerialPort").toInt()); 0288 ui.cbUpdateType->setCurrentIndex(conf.readEntry("UpdateType", static_cast<int>(LiveDataSource::UpdateType::NewData))); 0289 updateTypeChanged(ui.cbUpdateType->currentIndex()); 0290 ui.leHost->setText(conf.readEntry("Host","")); 0291 ui.sbKeepNValues->setValue(conf.readEntry("KeepNValues", 0)); // keep all values 0292 ui.lePort->setText(conf.readEntry("Port","")); 0293 ui.sbSampleSize->setValue(conf.readEntry("SampleSize", 1)); 0294 ui.sbUpdateInterval->setValue(conf.readEntry("UpdateInterval", 1000)); 0295 ui.chbLinkFile->setCheckState((Qt::CheckState)conf.readEntry("LinkFile", (int)Qt::CheckState::Unchecked)); 0296 ui.chbRelativePath->setCheckState((Qt::CheckState)conf.readEntry("RelativePath", (int)Qt::CheckState::Unchecked)); 0297 0298 #ifdef HAVE_MQTT 0299 //read available MQTT connections 0300 m_initialisingMQTT = true; 0301 readMQTTConnections(); 0302 ui.cbConnection->setCurrentIndex(ui.cbConnection->findText(conf.readEntry("Connection", ""))); 0303 m_initialisingMQTT = false; 0304 0305 m_willSettings.enabled = conf.readEntry("mqttWillEnabled", m_willSettings.enabled); 0306 m_willSettings.willRetain = conf.readEntry("mqttWillRetain", m_willSettings.willRetain); 0307 m_willSettings.willUpdateType = static_cast<MQTTClient::WillUpdateType>(conf.readEntry("mqttWillUpdateType", (int)m_willSettings.willUpdateType)); 0308 m_willSettings.willMessageType = static_cast<MQTTClient::WillMessageType>(conf.readEntry("mqttWillMessageType", (int)m_willSettings.willMessageType)); 0309 m_willSettings.willQoS = conf.readEntry("mqttWillQoS", (int)m_willSettings.willQoS); 0310 m_willSettings.willOwnMessage = conf.readEntry("mqttWillOwnMessage", m_willSettings.willOwnMessage); 0311 m_willSettings.willTimeInterval = conf.readEntry("mqttWillUpdateInterval", m_willSettings.willTimeInterval); 0312 0313 const QString& willStatistics = conf.readEntry("mqttWillStatistics",""); 0314 const QStringList& statisticsList = willStatistics.split('|', QString::SplitBehavior::SkipEmptyParts); 0315 for (auto value : statisticsList) 0316 m_willSettings.willStatistics[value.toInt()] = true; 0317 #endif 0318 0319 //initialize the slots after all settings were set in order to avoid unneeded refreshes 0320 initSlots(); 0321 0322 //update the status of the widgets 0323 fileTypeChanged(); 0324 sourceTypeChanged(static_cast<int>(currentSourceType())); 0325 readingTypeChanged(ui.cbReadingType->currentIndex()); 0326 0327 //all set now, refresh the content of the file and the preview for the selected dataset 0328 m_suppressRefresh = false; 0329 QTimer::singleShot(100, this, [=] () { 0330 WAIT_CURSOR; 0331 if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) { 0332 QString tempFileName = fileName(); 0333 const QString& fileName = absolutePath(tempFileName); 0334 if (QFile::exists(fileName)) 0335 updateContent(fileName); 0336 } 0337 0338 refreshPreview(); 0339 RESET_CURSOR; 0340 }); 0341 } 0342 0343 ImportFileWidget::~ImportFileWidget() { 0344 // save current settings 0345 QString confName; 0346 if (m_liveDataSource) 0347 confName = QLatin1String("LiveDataImport"); 0348 else 0349 confName = QLatin1String("FileImport"); 0350 KConfigGroup conf(KSharedConfig::openConfig(), confName); 0351 0352 // general settings 0353 conf.writeEntry("Type", (int)currentFileType()); 0354 conf.writeEntry("Filter", ui.cbFilter->currentIndex()); 0355 conf.writeEntry("LastImportedFile", m_cbFileName->currentText()); 0356 conf.writeXdgListEntry("LastImportedFiles", m_cbFileName->urls()); 0357 conf.writeEntry("PreviewLines", ui.sbPreviewLines->value()); 0358 0359 //live data related settings 0360 conf.writeEntry("SourceType", (int)currentSourceType()); 0361 conf.writeEntry("UpdateType", ui.cbUpdateType->currentIndex()); 0362 conf.writeEntry("ReadingType", ui.cbReadingType->currentIndex()); 0363 conf.writeEntry("SampleSize", ui.sbSampleSize->value()); 0364 conf.writeEntry("KeepNValues", ui.sbKeepNValues->value()); 0365 conf.writeEntry("BaudRate", ui.cbBaudRate->currentIndex()); 0366 conf.writeEntry("SerialPort", ui.cbSerialPort->currentIndex()); 0367 conf.writeEntry("Host", ui.leHost->text()); 0368 conf.writeEntry("Port", ui.lePort->text()); 0369 conf.writeEntry("UpdateInterval", ui.sbUpdateInterval->value()); 0370 conf.writeEntry("LinkFile", (int)ui.chbLinkFile->checkState()); 0371 conf.writeEntry("RelativePath", (int)ui.chbRelativePath->checkState()); 0372 0373 #ifdef HAVE_MQTT 0374 delete m_connectTimeoutTimer; 0375 delete m_subscriptionWidget; 0376 0377 //MQTT related settings 0378 conf.writeEntry("Connection", ui.cbConnection->currentText()); 0379 conf.writeEntry("mqttWillMessageType", static_cast<int>(m_willSettings.willMessageType)); 0380 conf.writeEntry("mqttWillUpdateType", static_cast<int>(m_willSettings.willUpdateType)); 0381 conf.writeEntry("mqttWillQoS", QString::number(m_willSettings.willQoS)); 0382 conf.writeEntry("mqttWillOwnMessage", m_willSettings.willOwnMessage); 0383 conf.writeEntry("mqttWillUpdateInterval", QString::number(m_willSettings.willTimeInterval)); 0384 QString willStatistics; 0385 for (int i = 0; i < m_willSettings.willStatistics.size(); ++i) { 0386 if (m_willSettings.willStatistics[i]) 0387 willStatistics += QString::number(i)+ QLatin1Char('|'); 0388 } 0389 conf.writeEntry("mqttWillStatistics", willStatistics); 0390 conf.writeEntry("mqttWillRetain", static_cast<int>(m_willSettings.willRetain)); 0391 conf.writeEntry("mqttWillUse", static_cast<int>(m_willSettings.enabled)); 0392 #endif 0393 0394 // data type specific settings 0395 if (m_asciiOptionsWidget) 0396 m_asciiOptionsWidget->saveSettings(); 0397 if (m_binaryOptionsWidget) 0398 m_binaryOptionsWidget->saveSettings(); 0399 if (m_imageOptionsWidget) 0400 m_imageOptionsWidget->saveSettings(); 0401 if (m_jsonOptionsWidget) 0402 m_jsonOptionsWidget->saveSettings(); 0403 } 0404 0405 void ImportFileWidget::initSlots() { 0406 //SLOTs for the general part of the data source configuration 0407 connect(ui.cbSourceType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), 0408 this, static_cast<void (ImportFileWidget::*) (int)>(&ImportFileWidget::sourceTypeChanged)); 0409 connect(m_cbFileName, &KUrlComboBox::urlActivated, 0410 this, [=](const QUrl &url){fileNameChanged(url.path());}); 0411 connect(ui.leHost, &QLineEdit::textChanged, this, &ImportFileWidget::hostChanged); 0412 connect(ui.lePort, &QLineEdit::textChanged, this, &ImportFileWidget::portChanged); 0413 connect(ui.tvJson, &QTreeView::clicked, this, &ImportFileWidget::refreshPreview); 0414 connect(ui.bOpen, &QPushButton::clicked, this, &ImportFileWidget::selectFile); 0415 connect(ui.bFileInfo, &QPushButton::clicked, this, &ImportFileWidget::fileInfoDialog); 0416 connect(ui.bSaveFilter, &QPushButton::clicked, this, &ImportFileWidget::saveFilter); 0417 connect(ui.bManageFilters, &QPushButton::clicked, this, &ImportFileWidget::manageFilters); 0418 connect(ui.cbFileType, static_cast<void (KComboBox::*) (int)>(&KComboBox::currentIndexChanged), 0419 this, &ImportFileWidget::fileTypeChanged); 0420 connect(ui.cbUpdateType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), 0421 this, &ImportFileWidget::updateTypeChanged); 0422 connect(ui.cbReadingType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), 0423 this, &ImportFileWidget::readingTypeChanged); 0424 connect(ui.cbFilter, static_cast<void (KComboBox::*) (int)>(&KComboBox::activated), this, &ImportFileWidget::filterChanged); 0425 connect(ui.bRefreshPreview, &QPushButton::clicked, this, &ImportFileWidget::refreshPreview); 0426 0427 #ifdef HAVE_MQTT 0428 connect(ui.cbConnection, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), this, &ImportFileWidget::mqttConnectionChanged); 0429 connect(ui.cbFileType, static_cast<void (QComboBox::*) (int)>(&QComboBox::currentIndexChanged), [this]() { 0430 emit checkFileType(); 0431 }); 0432 connect(ui.bManageConnections, &QPushButton::clicked, this, &ImportFileWidget::showMQTTConnectionManager); 0433 connect(ui.bLWT, &QPushButton::clicked, this, &ImportFileWidget::showWillSettings); 0434 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::makeSubscription, this, &ImportFileWidget::subscribeTopic); 0435 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::MQTTUnsubscribeFromTopic, this, &ImportFileWidget::unsubscribeTopic); 0436 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::enableWill, this, &ImportFileWidget::enableWill); 0437 connect(m_subscriptionWidget, &MQTTSubscriptionWidget::subscriptionChanged, this, &ImportFileWidget::refreshPreview); 0438 #endif 0439 } 0440 0441 void ImportFileWidget::showAsciiHeaderOptions(bool b) { 0442 if (m_asciiOptionsWidget) 0443 m_asciiOptionsWidget->showAsciiHeaderOptions(b); 0444 } 0445 0446 void ImportFileWidget::showJsonModel(bool b) { 0447 ui.tvJson->setVisible(b); 0448 ui.lField->setVisible(b); 0449 } 0450 0451 void ImportFileWidget::showOptions(bool b) { 0452 ui.gbOptions->setVisible(b); 0453 0454 if (m_liveDataSource) 0455 ui.gbUpdateOptions->setVisible(b); 0456 0457 resize(layout()->minimumSize()); 0458 } 0459 0460 QString ImportFileWidget::fileName() const { 0461 return m_cbFileName->currentText(); 0462 } 0463 0464 QString ImportFileWidget::selectedObject() const { 0465 const QString& path = fileName(); 0466 0467 //determine the file name only 0468 QString name = path.right(path.length() - path.lastIndexOf('/') - 1); 0469 0470 //strip away the extension if available 0471 if (name.indexOf('.') != -1) 0472 name = name.left(name.lastIndexOf('.')); 0473 0474 //for multi-dimensional formats like HDF, netCDF and FITS add the currently selected object 0475 const auto format = currentFileType(); 0476 if (format == AbstractFileFilter::FileType::HDF5) { 0477 const QStringList& hdf5Names = m_hdf5OptionsWidget->selectedNames(); 0478 if (hdf5Names.size()) 0479 name += hdf5Names.first(); //the names of the selected HDF5 objects already have '/' 0480 } else if (format == AbstractFileFilter::FileType::NETCDF) { 0481 const QStringList& names = m_netcdfOptionsWidget->selectedNames(); 0482 if (names.size()) 0483 name += QLatin1Char('/') + names.first(); 0484 } else if (format == AbstractFileFilter::FileType::FITS) { 0485 const QString& extensionName = m_fitsOptionsWidget->currentExtensionName(); 0486 if (!extensionName.isEmpty()) 0487 name += QLatin1Char('/') + extensionName; 0488 } else if (format == AbstractFileFilter::FileType::ROOT) { 0489 const QStringList& names = m_rootOptionsWidget->selectedNames(); 0490 if (names.size()) 0491 name += QLatin1Char('/') + names.first(); 0492 } 0493 0494 return name; 0495 } 0496 0497 /*! 0498 * returns \c true if the number of lines to be imported from the currently selected file is zero ("file is empty"), 0499 * returns \c false otherwise. 0500 */ 0501 bool ImportFileWidget::isFileEmpty() const { 0502 return m_fileEmpty; 0503 } 0504 0505 QString ImportFileWidget::host() const { 0506 return ui.leHost->text(); 0507 } 0508 0509 QString ImportFileWidget::port() const { 0510 return ui.lePort->text(); 0511 } 0512 0513 QString ImportFileWidget::serialPort() const { 0514 return ui.cbSerialPort->currentText(); 0515 } 0516 0517 int ImportFileWidget::baudRate() const { 0518 return ui.cbBaudRate->currentText().toInt(); 0519 } 0520 0521 /*! 0522 saves the settings to the data source \c source. 0523 */ 0524 void ImportFileWidget::saveSettings(LiveDataSource* source) const { 0525 AbstractFileFilter::FileType fileType = currentFileType(); 0526 auto updateType = static_cast<LiveDataSource::UpdateType>(ui.cbUpdateType->currentIndex()); 0527 LiveDataSource::SourceType sourceType = currentSourceType(); 0528 auto readingType = static_cast<LiveDataSource::ReadingType>(ui.cbReadingType->currentIndex()); 0529 0530 source->setFileType(fileType); 0531 currentFileFilter(); 0532 source->setFilter(m_currentFilter.release()); // pass ownership of the filter to the LiveDataSource 0533 0534 source->setSourceType(sourceType); 0535 0536 switch (sourceType) { 0537 case LiveDataSource::SourceType::FileOrPipe: 0538 source->setFileName(fileName()); 0539 source->setFileLinked(ui.chbLinkFile->isChecked()); 0540 source->setComment(fileName()); 0541 if (m_liveDataSource) 0542 source->setUseRelativePath(ui.chbRelativePath->isChecked()); 0543 break; 0544 case LiveDataSource::SourceType::LocalSocket: 0545 source->setFileName(fileName()); 0546 source->setLocalSocketName(fileName()); 0547 source->setComment(fileName()); 0548 break; 0549 case LiveDataSource::SourceType::NetworkTcpSocket: 0550 case LiveDataSource::SourceType::NetworkUdpSocket: 0551 source->setHost(ui.leHost->text()); 0552 source->setPort((quint16)ui.lePort->text().toInt()); 0553 break; 0554 case LiveDataSource::SourceType::SerialPort: 0555 source->setBaudRate(ui.cbBaudRate->currentText().toInt()); 0556 source->setSerialPort(ui.cbSerialPort->currentText()); 0557 break; 0558 case LiveDataSource::SourceType::MQTT: 0559 break; 0560 default: 0561 break; 0562 } 0563 0564 //reading options 0565 source->setReadingType(readingType); 0566 source->setKeepNValues(ui.sbKeepNValues->value()); 0567 source->setUpdateType(updateType); 0568 if (updateType == LiveDataSource::UpdateType::TimeInterval) 0569 source->setUpdateInterval(ui.sbUpdateInterval->value()); 0570 0571 if (readingType != LiveDataSource::ReadingType::TillEnd) 0572 source->setSampleSize(ui.sbSampleSize->value()); 0573 } 0574 0575 /*! 0576 returns the currently used file type. 0577 */ 0578 AbstractFileFilter::FileType ImportFileWidget::currentFileType() const { 0579 return static_cast<AbstractFileFilter::FileType>(ui.cbFileType->currentData().toInt()); 0580 } 0581 0582 LiveDataSource::SourceType ImportFileWidget::currentSourceType() const { 0583 return static_cast<LiveDataSource::SourceType>(ui.cbSourceType->currentIndex()); 0584 } 0585 0586 /*! 0587 returns the currently used filter. 0588 */ 0589 AbstractFileFilter* ImportFileWidget::currentFileFilter() const { 0590 DEBUG("ImportFileWidget::currentFileFilter()"); 0591 AbstractFileFilter::FileType fileType = currentFileType(); 0592 if (m_currentFilter && m_currentFilter->type() != fileType) 0593 m_currentFilter.reset(); 0594 0595 switch (fileType) { 0596 case AbstractFileFilter::FileType::Ascii: { 0597 DEBUG(" ASCII"); 0598 if (!m_currentFilter) 0599 m_currentFilter.reset(new AsciiFilter); 0600 auto filter = static_cast<AsciiFilter*>(m_currentFilter.get()); 0601 0602 if (ui.cbFilter->currentIndex() == 0) //"automatic" 0603 filter->setAutoModeEnabled(true); 0604 else if (ui.cbFilter->currentIndex() == 1) { //"custom" 0605 filter->setAutoModeEnabled(false); 0606 if (m_asciiOptionsWidget) 0607 m_asciiOptionsWidget->applyFilterSettings(filter); 0608 } else 0609 filter->loadFilterSettings(ui.cbFilter->currentText()); 0610 0611 //save the data portion to import 0612 filter->setStartRow(ui.sbStartRow->value()); 0613 filter->setEndRow(ui.sbEndRow->value()); 0614 filter->setStartColumn(ui.sbStartColumn->value()); 0615 filter->setEndColumn(ui.sbEndColumn->value()); 0616 0617 break; 0618 } 0619 case AbstractFileFilter::FileType::Binary: { 0620 DEBUG(" Binary"); 0621 if (!m_currentFilter) 0622 m_currentFilter.reset(new BinaryFilter); 0623 auto filter = static_cast<BinaryFilter*>(m_currentFilter.get()); 0624 if ( ui.cbFilter->currentIndex() == 0 ) //"automatic" 0625 filter->setAutoModeEnabled(true); 0626 else if (ui.cbFilter->currentIndex() == 1) { //"custom" 0627 filter->setAutoModeEnabled(false); 0628 if (m_binaryOptionsWidget) 0629 m_binaryOptionsWidget->applyFilterSettings(filter); 0630 } else { 0631 //TODO: load filter settings 0632 // filter->setFilterName( ui.cbFilter->currentText() ); 0633 } 0634 0635 filter->setStartRow(ui.sbStartRow->value()); 0636 filter->setEndRow(ui.sbEndRow->value()); 0637 0638 break; 0639 } 0640 case AbstractFileFilter::FileType::Image: { 0641 DEBUG(" Image"); 0642 if (!m_currentFilter) 0643 m_currentFilter.reset(new ImageFilter); 0644 auto filter = static_cast<ImageFilter*>(m_currentFilter.get()); 0645 0646 filter->setImportFormat(m_imageOptionsWidget->currentFormat()); 0647 filter->setStartRow(ui.sbStartRow->value()); 0648 filter->setEndRow(ui.sbEndRow->value()); 0649 filter->setStartColumn(ui.sbStartColumn->value()); 0650 filter->setEndColumn(ui.sbEndColumn->value()); 0651 0652 break; 0653 } 0654 case AbstractFileFilter::FileType::HDF5: { 0655 DEBUG("ImportFileWidget::currentFileFilter(): HDF5"); 0656 if (!m_currentFilter) 0657 m_currentFilter.reset(new HDF5Filter); 0658 auto filter = static_cast<HDF5Filter*>(m_currentFilter.get()); 0659 QStringList names = selectedHDF5Names(); 0660 QDEBUG("ImportFileWidget::currentFileFilter(): selected HDF5 names =" << names); 0661 if (!names.isEmpty()) 0662 filter->setCurrentDataSetName(names[0]); 0663 filter->setStartRow(ui.sbStartRow->value()); 0664 filter->setEndRow(ui.sbEndRow->value()); 0665 filter->setStartColumn(ui.sbStartColumn->value()); 0666 filter->setEndColumn(ui.sbEndColumn->value()); 0667 DEBUG("ImportFileWidget::currentFileFilter(): OK"); 0668 0669 break; 0670 } 0671 case AbstractFileFilter::FileType::NETCDF: { 0672 DEBUG(" NETCDF"); 0673 if (!m_currentFilter) 0674 m_currentFilter.reset(new NetCDFFilter); 0675 auto filter = static_cast<NetCDFFilter*>(m_currentFilter.get()); 0676 0677 if (!selectedNetCDFNames().isEmpty()) 0678 filter->setCurrentVarName(selectedNetCDFNames()[0]); 0679 filter->setStartRow(ui.sbStartRow->value()); 0680 filter->setEndRow(ui.sbEndRow->value()); 0681 filter->setStartColumn(ui.sbStartColumn->value()); 0682 filter->setEndColumn(ui.sbEndColumn->value()); 0683 0684 break; 0685 } 0686 case AbstractFileFilter::FileType::FITS: { 0687 DEBUG(" FITS"); 0688 if (!m_currentFilter) 0689 m_currentFilter.reset(new FITSFilter); 0690 auto filter = static_cast<FITSFilter*>(m_currentFilter.get()); 0691 filter->setStartRow(ui.sbStartRow->value()); 0692 filter->setEndRow(ui.sbEndRow->value()); 0693 filter->setStartColumn(ui.sbStartColumn->value()); 0694 filter->setEndColumn(ui.sbEndColumn->value()); 0695 0696 break; 0697 } 0698 case AbstractFileFilter::FileType::JSON: { 0699 DEBUG(" JSON"); 0700 if (!m_currentFilter) 0701 m_currentFilter.reset(new JsonFilter); 0702 auto filter = static_cast<JsonFilter*>(m_currentFilter.get()); 0703 m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex()); 0704 0705 filter->setStartRow(ui.sbStartRow->value()); 0706 filter->setEndRow(ui.sbEndRow->value()); 0707 filter->setStartColumn(ui.sbStartColumn->value()); 0708 filter->setEndColumn(ui.sbEndColumn->value()); 0709 0710 break; 0711 } 0712 case AbstractFileFilter::FileType::ROOT: { 0713 DEBUG(" ROOT"); 0714 if (!m_currentFilter) 0715 m_currentFilter.reset(new ROOTFilter); 0716 auto filter = static_cast<ROOTFilter*>(m_currentFilter.get()); 0717 QStringList names = selectedROOTNames(); 0718 if (!names.isEmpty()) 0719 filter->setCurrentObject(names.first()); 0720 0721 filter->setStartRow(m_rootOptionsWidget->startRow()); 0722 filter->setEndRow(m_rootOptionsWidget->endRow()); 0723 filter->setColumns(m_rootOptionsWidget->columns()); 0724 0725 break; 0726 } 0727 case AbstractFileFilter::FileType::NgspiceRawAscii: { 0728 DEBUG(" NgspiceRawAscii"); 0729 if (!m_currentFilter) 0730 m_currentFilter.reset(new NgspiceRawAsciiFilter); 0731 auto filter = static_cast<NgspiceRawAsciiFilter*>(m_currentFilter.get()); 0732 filter->setStartRow(ui.sbStartRow->value()); 0733 filter->setEndRow(ui.sbEndRow->value()); 0734 0735 break; 0736 } 0737 case AbstractFileFilter::FileType::NgspiceRawBinary: { 0738 DEBUG(" NgspiceRawBinary"); 0739 if (!m_currentFilter) 0740 m_currentFilter.reset(new NgspiceRawBinaryFilter); 0741 auto filter = static_cast<NgspiceRawBinaryFilter*>(m_currentFilter.get()); 0742 filter->setStartRow(ui.sbStartRow->value()); 0743 filter->setEndRow(ui.sbEndRow->value()); 0744 0745 break; 0746 } 0747 } 0748 0749 return m_currentFilter.get(); 0750 } 0751 0752 /*! 0753 opens a file dialog and lets the user select the file data source. 0754 */ 0755 void ImportFileWidget::selectFile() { 0756 DEBUG("ImportFileWidget::selectFile()") 0757 KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("ImportFileWidget")); 0758 const QString& dir = conf.readEntry(QLatin1String("LastDir"), ""); 0759 const QString& path = QFileDialog::getOpenFileName(this, i18n("Select the File Data Source"), dir); 0760 DEBUG(" dir = " << STDSTRING(dir)) 0761 DEBUG(" path = " << STDSTRING(path)) 0762 if (path.isEmpty()) //cancel was clicked in the file-dialog 0763 return; 0764 0765 int pos = path.lastIndexOf('/'); 0766 if (pos != -1) { 0767 QString newDir = path.left(pos); 0768 if (newDir != dir) 0769 conf.writeEntry(QLatin1String("LastDir"), newDir); 0770 } 0771 0772 //process all events after the FileDialog was closed to repaint the widget 0773 //before we start calculating the preview 0774 QApplication::processEvents(QEventLoop::AllEvents, 0); 0775 0776 QStringList urls = m_cbFileName->urls(); 0777 urls.insert(0, QUrl::fromLocalFile(path).url()); // add type of path 0778 m_cbFileName->setUrls(urls); 0779 m_cbFileName->setCurrentText(urls.first()); 0780 DEBUG(" combobox text = " << STDSTRING(m_cbFileName->currentText())) 0781 fileNameChanged(path); // why do I have to call this function separately 0782 } 0783 0784 /*! 0785 hides the MQTT related items of the widget 0786 */ 0787 void ImportFileWidget::setMQTTVisible(bool visible) { 0788 ui.lConnections->setVisible(visible); 0789 ui.cbConnection->setVisible(visible); 0790 ui.bManageConnections->setVisible(visible); 0791 0792 //topics 0793 if (ui.cbConnection->currentIndex() != -1 && visible) { 0794 ui.lTopics->setVisible(true); 0795 ui.frameSubscriptions->setVisible(true); 0796 #ifdef HAVE_MQTT 0797 m_subscriptionWidget->setVisible(true); 0798 m_subscriptionWidget->makeVisible(true); 0799 #endif 0800 } else { 0801 ui.lTopics->setVisible(false); 0802 ui.frameSubscriptions->setVisible(false); 0803 #ifdef HAVE_MQTT 0804 m_subscriptionWidget->setVisible(false); 0805 m_subscriptionWidget->makeVisible(false); 0806 #endif 0807 } 0808 0809 //will message 0810 ui.lLWT->setVisible(visible); 0811 ui.bLWT->setVisible(visible); 0812 } 0813 0814 /************** SLOTS **************************************************************/ 0815 /*! 0816 called on file name changes. 0817 Determines the file format (ASCII, binary etc.), if the file exists, 0818 and activates the corresponding options. 0819 */ 0820 void ImportFileWidget::fileNameChanged(const QString& name) { 0821 DEBUG("ImportFileWidget::fileNameChanged() : " << STDSTRING(name)) 0822 const QString fileName = absolutePath(name); 0823 0824 bool fileExists = QFile::exists(fileName); 0825 ui.gbOptions->setEnabled(fileExists); 0826 ui.bManageFilters->setEnabled(fileExists); 0827 ui.cbFilter->setEnabled(fileExists); 0828 ui.cbFileType->setEnabled(fileExists); 0829 ui.bFileInfo->setEnabled(fileExists); 0830 ui.gbUpdateOptions->setEnabled(fileExists); 0831 if (!fileExists) { 0832 //file doesn't exist -> delete the content preview that is still potentially 0833 //available from the previously selected file 0834 ui.tePreview->clear(); 0835 m_twPreview->clear(); 0836 initOptionsWidget(); 0837 0838 emit fileNameChanged(); 0839 return; 0840 } 0841 0842 if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) { 0843 const AbstractFileFilter::FileType fileType = AbstractFileFilter::fileType(fileName); 0844 for (int i = 0; i < ui.cbFileType->count(); ++i) { 0845 if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == fileType) { 0846 // automatically select a new file type 0847 if (ui.cbFileType->currentIndex() != i) { 0848 ui.cbFileType->setCurrentIndex(i); // will call the slot fileTypeChanged which updates content and preview 0849 0850 //automatically set the comma separator if a csv file was selected 0851 if (fileType == AbstractFileFilter::FileType::Ascii && name.endsWith(QLatin1String("csv"), Qt::CaseInsensitive)) 0852 m_asciiOptionsWidget->setSeparatingCharacter(QLatin1Char(',')); 0853 0854 emit fileNameChanged(); 0855 return; 0856 } else { 0857 initOptionsWidget(); 0858 0859 //automatically set the comma separator if a csv file was selected 0860 if (fileType == AbstractFileFilter::FileType::Ascii && name.endsWith(QLatin1String("csv"), Qt::CaseInsensitive)) 0861 m_asciiOptionsWidget->setSeparatingCharacter(QLatin1Char(',')); 0862 0863 updateContent(fileName); 0864 break; 0865 } 0866 } 0867 } 0868 } 0869 0870 emit fileNameChanged(); 0871 refreshPreview(); 0872 } 0873 0874 /*! 0875 saves the current filter settings 0876 */ 0877 void ImportFileWidget::saveFilter() { 0878 bool ok; 0879 QString text = QInputDialog::getText(this, i18n("Save Filter Settings as"), 0880 i18n("Filter name:"), QLineEdit::Normal, i18n("new filter"), &ok); 0881 if (ok && !text.isEmpty()) { 0882 //TODO 0883 //AsciiFilter::saveFilter() 0884 } 0885 } 0886 0887 /*! 0888 opens a dialog for managing all available predefined filters. 0889 */ 0890 void ImportFileWidget::manageFilters() { 0891 //TODO 0892 } 0893 0894 /*! 0895 Depending on the selected file type, activates the corresponding options in the data portion tab 0896 and populates the combobox with the available pre-defined filter settings for the selected type. 0897 */ 0898 void ImportFileWidget::fileTypeChanged(int index) { 0899 Q_UNUSED(index); 0900 AbstractFileFilter::FileType fileType = currentFileType(); 0901 DEBUG("ImportFileWidget::fileTypeChanged " << ENUM_TO_STRING(AbstractFileFilter, FileType, fileType)); 0902 initOptionsWidget(); 0903 0904 //default 0905 ui.lFilter->show(); 0906 ui.cbFilter->show(); 0907 0908 //different file types show different number of tabs in ui.tabWidget. 0909 //when switching from the previous file type we re-set the tab widget to its original state 0910 //and remove/add the required tabs further below 0911 for (int i = 0; i<ui.tabWidget->count(); ++i) 0912 ui.tabWidget->removeTab(0); 0913 0914 ui.tabWidget->addTab(ui.tabDataFormat, i18n("Data format")); 0915 ui.tabWidget->addTab(ui.tabDataPreview, i18n("Preview")); 0916 if (!m_liveDataSource) 0917 ui.tabWidget->addTab(ui.tabDataPortion, i18n("Data portion to read")); 0918 0919 ui.lPreviewLines->show(); 0920 ui.sbPreviewLines->show(); 0921 ui.lStartColumn->show(); 0922 ui.sbStartColumn->show(); 0923 ui.lEndColumn->show(); 0924 ui.sbEndColumn->show(); 0925 0926 showJsonModel(false); 0927 0928 switch (fileType) { 0929 case AbstractFileFilter::FileType::Ascii: 0930 break; 0931 case AbstractFileFilter::FileType::Binary: 0932 ui.lStartColumn->hide(); 0933 ui.sbStartColumn->hide(); 0934 ui.lEndColumn->hide(); 0935 ui.sbEndColumn->hide(); 0936 break; 0937 case AbstractFileFilter::FileType::ROOT: 0938 ui.tabWidget->removeTab(1); 0939 // falls through 0940 case AbstractFileFilter::FileType::HDF5: 0941 case AbstractFileFilter::FileType::NETCDF: 0942 case AbstractFileFilter::FileType::FITS: 0943 ui.lFilter->hide(); 0944 ui.cbFilter->hide(); 0945 // hide global preview tab. we have our own 0946 ui.tabWidget->setTabText(0, i18n("Data format && preview")); 0947 ui.tabWidget->removeTab(1); 0948 ui.tabWidget->setCurrentIndex(0); 0949 break; 0950 case AbstractFileFilter::FileType::Image: 0951 ui.lFilter->hide(); 0952 ui.cbFilter->hide(); 0953 ui.lPreviewLines->hide(); 0954 ui.sbPreviewLines->hide(); 0955 break; 0956 case AbstractFileFilter::FileType::NgspiceRawAscii: 0957 case AbstractFileFilter::FileType::NgspiceRawBinary: 0958 ui.lFilter->hide(); 0959 ui.cbFilter->hide(); 0960 ui.lStartColumn->hide(); 0961 ui.sbStartColumn->hide(); 0962 ui.lEndColumn->hide(); 0963 ui.sbEndColumn->hide(); 0964 ui.tabWidget->removeTab(0); 0965 ui.tabWidget->setCurrentIndex(0); 0966 break; 0967 case AbstractFileFilter::FileType::JSON: 0968 ui.lFilter->hide(); 0969 ui.cbFilter->hide(); 0970 showJsonModel(true); 0971 break; 0972 default: 0973 DEBUG("unknown file type"); 0974 } 0975 0976 int lastUsedFilterIndex = ui.cbFilter->currentIndex(); 0977 ui.cbFilter->clear(); 0978 ui.cbFilter->addItem( i18n("Automatic") ); 0979 ui.cbFilter->addItem( i18n("Custom") ); 0980 0981 //TODO: populate the combobox with the available pre-defined filter settings for the selected type 0982 ui.cbFilter->setCurrentIndex(lastUsedFilterIndex); 0983 filterChanged(lastUsedFilterIndex); 0984 0985 if (currentSourceType() == LiveDataSource::SourceType::FileOrPipe) { 0986 QString tempFileName = fileName(); 0987 const QString& fileName = absolutePath(tempFileName); 0988 if (QFile::exists(fileName)) 0989 updateContent(fileName); 0990 } 0991 0992 //for file types other than ASCII and binary we support re-reading the whole file only 0993 //select "read whole file" and deactivate the combobox 0994 if (m_liveDataSource && (fileType != AbstractFileFilter::FileType::Ascii && fileType != AbstractFileFilter::FileType::Binary)) { 0995 ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 0996 ui.cbReadingType->setEnabled(false); 0997 } else 0998 ui.cbReadingType->setEnabled(true); 0999 1000 refreshPreview(); 1001 } 1002 1003 // file type specific option widgets 1004 void ImportFileWidget::initOptionsWidget() { 1005 DEBUG("ImportFileWidget::initOptionsWidget for " << ENUM_TO_STRING(AbstractFileFilter, FileType, currentFileType())); 1006 switch (currentFileType()) { 1007 case AbstractFileFilter::FileType::Ascii: { 1008 if (!m_asciiOptionsWidget) { 1009 QWidget* asciiw = new QWidget(); 1010 m_asciiOptionsWidget = std::unique_ptr<AsciiOptionsWidget>(new AsciiOptionsWidget(asciiw)); 1011 m_asciiOptionsWidget->loadSettings(); 1012 1013 //allow to add timestamp column for live data sources 1014 if (m_liveDataSource) 1015 m_asciiOptionsWidget->showTimestampOptions(true); 1016 ui.swOptions->addWidget(asciiw); 1017 } 1018 1019 ui.swOptions->setCurrentWidget(m_asciiOptionsWidget->parentWidget()); 1020 break; 1021 } 1022 case AbstractFileFilter::FileType::Binary: 1023 if (!m_binaryOptionsWidget) { 1024 QWidget* binaryw = new QWidget(); 1025 m_binaryOptionsWidget = std::unique_ptr<BinaryOptionsWidget>(new BinaryOptionsWidget(binaryw)); 1026 ui.swOptions->addWidget(binaryw); 1027 m_binaryOptionsWidget->loadSettings(); 1028 } 1029 ui.swOptions->setCurrentWidget(m_binaryOptionsWidget->parentWidget()); 1030 break; 1031 case AbstractFileFilter::FileType::Image: 1032 if (!m_imageOptionsWidget) { 1033 QWidget* imagew = new QWidget(); 1034 m_imageOptionsWidget = std::unique_ptr<ImageOptionsWidget>(new ImageOptionsWidget(imagew)); 1035 ui.swOptions->addWidget(imagew); 1036 m_imageOptionsWidget->loadSettings(); 1037 } 1038 ui.swOptions->setCurrentWidget(m_imageOptionsWidget->parentWidget()); 1039 break; 1040 case AbstractFileFilter::FileType::HDF5: 1041 if (!m_hdf5OptionsWidget) { 1042 QWidget* hdf5w = new QWidget(); 1043 m_hdf5OptionsWidget = std::unique_ptr<HDF5OptionsWidget>(new HDF5OptionsWidget(hdf5w, this)); 1044 ui.swOptions->addWidget(hdf5w); 1045 } else 1046 m_hdf5OptionsWidget->clear(); 1047 ui.swOptions->setCurrentWidget(m_hdf5OptionsWidget->parentWidget()); 1048 break; 1049 case AbstractFileFilter::FileType::NETCDF: 1050 if (!m_netcdfOptionsWidget) { 1051 QWidget* netcdfw = new QWidget(); 1052 m_netcdfOptionsWidget = std::unique_ptr<NetCDFOptionsWidget>(new NetCDFOptionsWidget(netcdfw, this)); 1053 ui.swOptions->insertWidget(static_cast<int>(AbstractFileFilter::FileType::NETCDF), netcdfw); 1054 } else 1055 m_netcdfOptionsWidget->clear(); 1056 ui.swOptions->setCurrentWidget(m_netcdfOptionsWidget->parentWidget()); 1057 break; 1058 case AbstractFileFilter::FileType::FITS: 1059 if (!m_fitsOptionsWidget) { 1060 QWidget* fitsw = new QWidget(); 1061 m_fitsOptionsWidget = std::unique_ptr<FITSOptionsWidget>(new FITSOptionsWidget(fitsw, this)); 1062 ui.swOptions->addWidget(fitsw); 1063 } else 1064 m_fitsOptionsWidget->clear(); 1065 ui.swOptions->setCurrentWidget(m_fitsOptionsWidget->parentWidget()); 1066 break; 1067 case AbstractFileFilter::FileType::JSON: 1068 if (!m_jsonOptionsWidget) { 1069 QWidget* jsonw = new QWidget(); 1070 m_jsonOptionsWidget = std::unique_ptr<JsonOptionsWidget>(new JsonOptionsWidget(jsonw, this)); 1071 ui.tvJson->setModel(m_jsonOptionsWidget->model()); 1072 ui.swOptions->addWidget(jsonw); 1073 m_jsonOptionsWidget->loadSettings(); 1074 } else 1075 m_jsonOptionsWidget->clearModel(); 1076 ui.swOptions->setCurrentWidget(m_jsonOptionsWidget->parentWidget()); 1077 showJsonModel(true); 1078 break; 1079 case AbstractFileFilter::FileType::ROOT: 1080 if (!m_rootOptionsWidget) { 1081 QWidget* rootw = new QWidget(); 1082 m_rootOptionsWidget = std::unique_ptr<ROOTOptionsWidget>(new ROOTOptionsWidget(rootw, this)); 1083 ui.swOptions->addWidget(rootw); 1084 } else 1085 m_rootOptionsWidget->clear(); 1086 ui.swOptions->setCurrentWidget(m_rootOptionsWidget->parentWidget()); 1087 break; 1088 case AbstractFileFilter::FileType::NgspiceRawAscii: 1089 case AbstractFileFilter::FileType::NgspiceRawBinary: 1090 break; 1091 } 1092 } 1093 1094 const QStringList ImportFileWidget::selectedHDF5Names() const { 1095 return m_hdf5OptionsWidget->selectedNames(); 1096 } 1097 1098 const QStringList ImportFileWidget::selectedNetCDFNames() const { 1099 return m_netcdfOptionsWidget->selectedNames(); 1100 } 1101 1102 const QStringList ImportFileWidget::selectedFITSExtensions() const { 1103 return m_fitsOptionsWidget->selectedExtensions(); 1104 } 1105 1106 const QStringList ImportFileWidget::selectedROOTNames() const { 1107 return m_rootOptionsWidget->selectedNames(); 1108 } 1109 1110 /*! 1111 shows the dialog with the information about the file(s) to be imported. 1112 */ 1113 void ImportFileWidget::fileInfoDialog() { 1114 QStringList files = fileName().split(';'); 1115 auto* dlg = new FileInfoDialog(this); 1116 dlg->setFiles(files); 1117 dlg->exec(); 1118 } 1119 1120 /*! 1121 enables the options if the filter "custom" was chosen. Disables the options otherwise. 1122 */ 1123 void ImportFileWidget::filterChanged(int index) { 1124 // ignore filter for these formats 1125 AbstractFileFilter::FileType fileType = currentFileType(); 1126 if (fileType != AbstractFileFilter::FileType::Ascii && fileType != AbstractFileFilter::FileType::Binary) { 1127 ui.swOptions->setEnabled(true); 1128 return; 1129 } 1130 1131 if (index == 0) { // "automatic" 1132 ui.swOptions->setEnabled(false); 1133 ui.bSaveFilter->setEnabled(false); 1134 } else if (index == 1) { //custom 1135 ui.swOptions->setEnabled(true); 1136 ui.bSaveFilter->setEnabled(true); 1137 } else { 1138 // predefined filter settings were selected. 1139 //load and show them in the GUI. 1140 //TODO 1141 } 1142 } 1143 1144 void ImportFileWidget::refreshPreview() { 1145 //don't generate any preview if it was explicitly suppressed 1146 //or if the options box together with the preview widget is not visible 1147 if (m_suppressRefresh || !ui.gbOptions->isVisible()) 1148 return; 1149 1150 DEBUG("ImportFileWidget::refreshPreview()"); 1151 WAIT_CURSOR; 1152 1153 QString tempFileName = fileName(); 1154 QString fileName = absolutePath(tempFileName); 1155 AbstractFileFilter::FileType fileType = currentFileType(); 1156 LiveDataSource::SourceType sourceType = currentSourceType(); 1157 int lines = ui.sbPreviewLines->value(); 1158 1159 if (sourceType == LiveDataSource::SourceType::FileOrPipe) 1160 DEBUG(" file name = " << STDSTRING(fileName)); 1161 1162 // generic table widget 1163 if (fileType == AbstractFileFilter::FileType::Ascii || fileType == AbstractFileFilter::FileType::Binary 1164 || fileType == AbstractFileFilter::FileType::JSON || fileType == AbstractFileFilter::FileType::NgspiceRawAscii 1165 || fileType == AbstractFileFilter::FileType::NgspiceRawBinary) 1166 m_twPreview->show(); 1167 else 1168 m_twPreview->hide(); 1169 1170 bool ok = true; 1171 QTableWidget* tmpTableWidget = m_twPreview; 1172 QVector<QStringList> importedStrings; 1173 QStringList vectorNameList; 1174 QVector<AbstractColumn::ColumnMode> columnModes; 1175 DEBUG("Data File Type: " << ENUM_TO_STRING(AbstractFileFilter, FileType, fileType)); 1176 switch (fileType) { 1177 case AbstractFileFilter::FileType::Ascii: { 1178 ui.tePreview->clear(); 1179 1180 auto filter = static_cast<AsciiFilter*>(currentFileFilter()); 1181 1182 DEBUG("Data Source Type: " << ENUM_TO_STRING(LiveDataSource, SourceType, sourceType)); 1183 switch (sourceType) { 1184 case LiveDataSource::SourceType::FileOrPipe: { 1185 importedStrings = filter->preview(fileName, lines); 1186 break; 1187 } 1188 case LiveDataSource::SourceType::LocalSocket: { 1189 QLocalSocket lsocket{this}; 1190 DEBUG("Local socket: CONNECT PREVIEW"); 1191 lsocket.connectToServer(fileName, QLocalSocket::ReadOnly); 1192 if (lsocket.waitForConnected()) { 1193 DEBUG("connected to local socket " << STDSTRING(fileName)); 1194 if (lsocket.waitForReadyRead()) 1195 importedStrings = filter->preview(lsocket); 1196 DEBUG("Local socket: DISCONNECT PREVIEW"); 1197 lsocket.disconnectFromServer(); 1198 // read-only socket is disconnected immediately (no waitForDisconnected()) 1199 } else 1200 DEBUG("failed connect to local socket " << STDSTRING(fileName) << " - " << STDSTRING(lsocket.errorString())); 1201 1202 break; 1203 } 1204 case LiveDataSource::SourceType::NetworkTcpSocket: { 1205 QTcpSocket tcpSocket{this}; 1206 tcpSocket.connectToHost(host(), port().toInt(), QTcpSocket::ReadOnly); 1207 if (tcpSocket.waitForConnected()) { 1208 DEBUG("connected to TCP socket"); 1209 if ( tcpSocket.waitForReadyRead() ) 1210 importedStrings = filter->preview(tcpSocket); 1211 1212 tcpSocket.disconnectFromHost(); 1213 } else 1214 DEBUG("failed to connect to TCP socket " << " - " << STDSTRING(tcpSocket.errorString())); 1215 1216 break; 1217 } 1218 case LiveDataSource::SourceType::NetworkUdpSocket: { 1219 QUdpSocket udpSocket{this}; 1220 DEBUG("UDP Socket: CONNECT PREVIEW, state = " << udpSocket.state()); 1221 udpSocket.bind(QHostAddress(host()), port().toInt()); 1222 udpSocket.connectToHost(host(), 0, QUdpSocket::ReadOnly); 1223 if (udpSocket.waitForConnected()) { 1224 DEBUG(" connected to UDP socket " << STDSTRING(host()) << ':' << port().toInt()); 1225 if (!udpSocket.waitForReadyRead(2000) ) 1226 DEBUG(" ERROR: not ready for read after 2 sec"); 1227 if (udpSocket.hasPendingDatagrams()) { 1228 DEBUG(" has pending data"); 1229 } else { 1230 DEBUG(" has no pending data"); 1231 } 1232 importedStrings = filter->preview(udpSocket); 1233 1234 DEBUG("UDP Socket: DISCONNECT PREVIEW, state = " << udpSocket.state()); 1235 udpSocket.disconnectFromHost(); 1236 } else 1237 DEBUG("failed to connect to UDP socket " << " - " << STDSTRING(udpSocket.errorString())); 1238 1239 break; 1240 } 1241 case LiveDataSource::SourceType::SerialPort: { 1242 #ifdef HAVE_QTSERIALPORT 1243 QSerialPort sPort{this}; 1244 DEBUG(" Port: " << STDSTRING(serialPort()) << ", Settings: " << baudRate() << ',' << sPort.dataBits() 1245 << ',' << sPort.parity() << ',' << sPort.stopBits()); 1246 sPort.setPortName(serialPort()); 1247 sPort.setBaudRate(baudRate()); 1248 1249 if (sPort.open(QIODevice::ReadOnly)) { 1250 if (sPort.waitForReadyRead(2000)) 1251 importedStrings = filter->preview(sPort); 1252 else 1253 DEBUG(" ERROR: not ready for read after 2 sec"); 1254 1255 sPort.close(); 1256 } else 1257 DEBUG(" ERROR: failed to open serial port. error: " << sPort.error()); 1258 #endif 1259 break; 1260 } 1261 case LiveDataSource::SourceType::MQTT: { 1262 #ifdef HAVE_MQTT 1263 //show the preview for the currently selected topic 1264 auto* item = m_subscriptionWidget->currentItem(); 1265 if (item && item->childCount() == 0) { //only preview if the lowest level (i.e. a topic) is selected 1266 const QString& topicName = item->text(0); 1267 auto i = m_lastMessage.find(topicName); 1268 if (i != m_lastMessage.end()) 1269 importedStrings = filter->preview(i.value().payload().data()); 1270 else 1271 importedStrings << QStringList{i18n("No data arrived yet for the selected topic")}; 1272 } 1273 #endif 1274 break; 1275 } 1276 } 1277 1278 vectorNameList = filter->vectorNames(); 1279 columnModes = filter->columnModes(); 1280 break; 1281 } 1282 case AbstractFileFilter::FileType::Binary: { 1283 ui.tePreview->clear(); 1284 auto filter = static_cast<BinaryFilter*>(currentFileFilter()); 1285 importedStrings = filter->preview(fileName, lines); 1286 break; 1287 } 1288 case AbstractFileFilter::FileType::Image: { 1289 ui.tePreview->clear(); 1290 1291 QImage image(fileName); 1292 QTextCursor cursor = ui.tePreview->textCursor(); 1293 cursor.insertImage(image); 1294 RESET_CURSOR; 1295 return; 1296 } 1297 case AbstractFileFilter::FileType::HDF5: { 1298 DEBUG("ImportFileWidget::refreshPreview: HDF5"); 1299 auto filter = static_cast<HDF5Filter*>(currentFileFilter()); 1300 lines = m_hdf5OptionsWidget->lines(); 1301 1302 importedStrings = filter->readCurrentDataSet(fileName, nullptr, ok, AbstractFileFilter::ImportMode::Replace, lines); 1303 tmpTableWidget = m_hdf5OptionsWidget->previewWidget(); 1304 break; 1305 } 1306 case AbstractFileFilter::FileType::NETCDF: { 1307 auto filter = static_cast<NetCDFFilter*>(currentFileFilter()); 1308 lines = m_netcdfOptionsWidget->lines(); 1309 1310 importedStrings = filter->readCurrentVar(fileName, nullptr, AbstractFileFilter::ImportMode::Replace, lines); 1311 tmpTableWidget = m_netcdfOptionsWidget->previewWidget(); 1312 break; 1313 } 1314 case AbstractFileFilter::FileType::FITS: { 1315 auto filter = static_cast<FITSFilter*>(currentFileFilter()); 1316 lines = m_fitsOptionsWidget->lines(); 1317 1318 QString extensionName = m_fitsOptionsWidget->extensionName(&ok); 1319 if (!extensionName.isEmpty()) { 1320 DEBUG(" extension name = " << STDSTRING(extensionName)); 1321 fileName = extensionName; 1322 } 1323 1324 bool readFitsTableToMatrix; 1325 importedStrings = filter->readChdu(fileName, &readFitsTableToMatrix, lines); 1326 emit checkedFitsTableToMatrix(readFitsTableToMatrix); 1327 1328 tmpTableWidget = m_fitsOptionsWidget->previewWidget(); 1329 break; 1330 } 1331 case AbstractFileFilter::FileType::JSON: { 1332 ui.tePreview->clear(); 1333 auto filter = static_cast<JsonFilter*>(currentFileFilter()); 1334 m_jsonOptionsWidget->applyFilterSettings(filter, ui.tvJson->currentIndex()); 1335 importedStrings = filter->preview(fileName, lines); 1336 1337 vectorNameList = filter->vectorNames(); 1338 columnModes = filter->columnModes(); 1339 break; 1340 } 1341 case AbstractFileFilter::FileType::ROOT: { 1342 auto filter = static_cast<ROOTFilter*>(currentFileFilter()); 1343 lines = m_rootOptionsWidget->lines(); 1344 m_rootOptionsWidget->setNRows(filter->rowsInCurrentObject(fileName)); 1345 importedStrings = filter->previewCurrentObject( 1346 fileName, 1347 m_rootOptionsWidget->startRow(), 1348 qMin(m_rootOptionsWidget->startRow() + lines - 1, 1349 m_rootOptionsWidget->endRow()) 1350 ); 1351 tmpTableWidget = m_rootOptionsWidget->previewWidget(); 1352 // the last vector element contains the column names 1353 vectorNameList = importedStrings.last(); 1354 importedStrings.removeLast(); 1355 columnModes = QVector<AbstractColumn::ColumnMode>(vectorNameList.size(), AbstractColumn::ColumnMode::Numeric); 1356 break; 1357 } 1358 case AbstractFileFilter::FileType::NgspiceRawAscii: { 1359 ui.tePreview->clear(); 1360 auto filter = static_cast<NgspiceRawAsciiFilter*>(currentFileFilter()); 1361 importedStrings = filter->preview(fileName, lines); 1362 vectorNameList = filter->vectorNames(); 1363 columnModes = filter->columnModes(); 1364 break; 1365 } 1366 case AbstractFileFilter::FileType::NgspiceRawBinary: { 1367 ui.tePreview->clear(); 1368 auto filter = static_cast<NgspiceRawBinaryFilter*>(currentFileFilter()); 1369 importedStrings = filter->preview(fileName, lines); 1370 vectorNameList = filter->vectorNames(); 1371 columnModes = filter->columnModes(); 1372 break; 1373 } 1374 } 1375 1376 // fill the table widget 1377 tmpTableWidget->setRowCount(0); 1378 tmpTableWidget->setColumnCount(0); 1379 if ( !importedStrings.isEmpty() ) { 1380 if (!ok) { 1381 // show imported strings as error message 1382 tmpTableWidget->setRowCount(1); 1383 tmpTableWidget->setColumnCount(1); 1384 auto* item = new QTableWidgetItem(); 1385 item->setText(importedStrings[0][0]); 1386 tmpTableWidget->setItem(0, 0, item); 1387 } else { 1388 //TODO: maxrows not used 1389 const int rows = qMax(importedStrings.size(), 1); 1390 const int maxColumns = 300; 1391 tmpTableWidget->setRowCount(rows); 1392 1393 for (int i = 0; i < rows; ++i) { 1394 const int cols = importedStrings[i].size() > maxColumns ? maxColumns : importedStrings[i].size(); 1395 if (cols > tmpTableWidget->columnCount()) 1396 tmpTableWidget->setColumnCount(cols); 1397 1398 for (int j = 0; j < cols; ++j) { 1399 auto* item = new QTableWidgetItem(importedStrings[i][j]); 1400 tmpTableWidget->setItem(i, j, item); 1401 } 1402 } 1403 1404 // set header if columnMode available 1405 for (int i = 0; i < qMin(tmpTableWidget->columnCount(), columnModes.size()); ++i) { 1406 QString columnName = QString::number(i+1); 1407 if (i < vectorNameList.size()) 1408 columnName = vectorNameList[i]; 1409 1410 auto* item = new QTableWidgetItem(columnName + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes[i]) + QLatin1String("}")); 1411 item->setTextAlignment(Qt::AlignLeft); 1412 item->setIcon(AbstractColumn::iconForMode(columnModes[i])); 1413 1414 tmpTableWidget->setHorizontalHeaderItem(i, item); 1415 } 1416 } 1417 1418 tmpTableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); 1419 m_fileEmpty = false; 1420 } else 1421 m_fileEmpty = true; 1422 1423 RESET_CURSOR; 1424 } 1425 1426 void ImportFileWidget::updateContent(const QString& fileName) { 1427 if (m_suppressRefresh) 1428 return; 1429 1430 QApplication::processEvents(QEventLoop::AllEvents, 0); 1431 WAIT_CURSOR; 1432 1433 QDEBUG("ImportFileWidget::updateContent(): file name = " << fileName); 1434 if (auto filter = currentFileFilter()) { 1435 switch (filter->type()) { 1436 case AbstractFileFilter::FileType::HDF5: 1437 m_hdf5OptionsWidget->updateContent(static_cast<HDF5Filter*>(filter), fileName); 1438 break; 1439 case AbstractFileFilter::FileType::NETCDF: 1440 m_netcdfOptionsWidget->updateContent(static_cast<NetCDFFilter*>(filter), fileName); 1441 break; 1442 case AbstractFileFilter::FileType::FITS: 1443 #ifdef HAVE_FITS 1444 m_fitsOptionsWidget->updateContent(static_cast<FITSFilter*>(filter), fileName); 1445 #endif 1446 break; 1447 case AbstractFileFilter::FileType::ROOT: 1448 m_rootOptionsWidget->updateContent(static_cast<ROOTFilter*>(filter), fileName); 1449 break; 1450 case AbstractFileFilter::FileType::JSON: 1451 m_jsonOptionsWidget->loadDocument(fileName); 1452 ui.tvJson->setExpanded( m_jsonOptionsWidget->model()->index(0, 0), true); //expand the root node 1453 break; 1454 case AbstractFileFilter::FileType::Ascii: 1455 case AbstractFileFilter::FileType::Binary: 1456 case AbstractFileFilter::FileType::Image: 1457 case AbstractFileFilter::FileType::NgspiceRawAscii: 1458 case AbstractFileFilter::FileType::NgspiceRawBinary: 1459 break; 1460 } 1461 } 1462 RESET_CURSOR; 1463 } 1464 1465 void ImportFileWidget::updateTypeChanged(int idx) { 1466 const auto UpdateType = static_cast<LiveDataSource::UpdateType>(idx); 1467 1468 switch (UpdateType) { 1469 case LiveDataSource::UpdateType::TimeInterval: 1470 ui.lUpdateInterval->show(); 1471 ui.sbUpdateInterval->show(); 1472 break; 1473 case LiveDataSource::UpdateType::NewData: 1474 ui.lUpdateInterval->hide(); 1475 ui.sbUpdateInterval->hide(); 1476 } 1477 } 1478 1479 void ImportFileWidget::readingTypeChanged(int idx) { 1480 const auto readingType = static_cast<LiveDataSource::ReadingType>(idx); 1481 const LiveDataSource::SourceType sourceType = currentSourceType(); 1482 1483 if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket || sourceType == LiveDataSource::SourceType::LocalSocket 1484 || sourceType == LiveDataSource::SourceType::SerialPort 1485 || readingType == LiveDataSource::ReadingType::TillEnd || readingType == LiveDataSource::ReadingType::WholeFile) { 1486 ui.lSampleSize->hide(); 1487 ui.sbSampleSize->hide(); 1488 } else { 1489 ui.lSampleSize->show(); 1490 ui.sbSampleSize->show(); 1491 } 1492 1493 if (readingType == LiveDataSource::ReadingType::WholeFile) { 1494 ui.lKeepLastValues->hide(); 1495 ui.sbKeepNValues->hide(); 1496 } else { 1497 ui.lKeepLastValues->show(); 1498 ui.sbKeepNValues->show(); 1499 } 1500 } 1501 1502 void ImportFileWidget::sourceTypeChanged(int idx) { 1503 const auto sourceType = static_cast<LiveDataSource::SourceType>(idx); 1504 1505 #ifdef HAVE_MQTT 1506 //when switching from mqtt to another source type, make sure we disconnect from 1507 //the current broker, if connected, in order not to get any notification anymore 1508 if (sourceType != LiveDataSource::SourceType::MQTT) 1509 disconnectMqttConnection(); 1510 #endif 1511 1512 // enable/disable "on new data"-option 1513 const auto* model = qobject_cast<const QStandardItemModel*>(ui.cbUpdateType->model()); 1514 QStandardItem* item = model->item(static_cast<int>(LiveDataSource::UpdateType::NewData)); 1515 1516 switch (sourceType) { 1517 case LiveDataSource::SourceType::FileOrPipe: 1518 ui.lFileName->show(); 1519 m_cbFileName->show(); 1520 ui.bFileInfo->show(); 1521 ui.bOpen->show(); 1522 if (m_liveDataSource) { 1523 ui.lRelativePath->show(); 1524 ui.chbRelativePath->show(); 1525 } 1526 ui.chbLinkFile->show(); 1527 1528 //option for sample size are available for "continuously fixed" and "from end" reading options 1529 if (ui.cbReadingType->currentIndex() < 2) { 1530 ui.lSampleSize->show(); 1531 ui.sbSampleSize->show(); 1532 } else { 1533 ui.lSampleSize->hide(); 1534 ui.sbSampleSize->hide(); 1535 } 1536 1537 ui.cbBaudRate->hide(); 1538 ui.lBaudRate->hide(); 1539 ui.lHost->hide(); 1540 ui.leHost->hide(); 1541 ui.lPort->hide(); 1542 ui.lePort->hide(); 1543 ui.cbSerialPort->hide(); 1544 ui.lSerialPort->hide(); 1545 1546 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 1547 1548 fileNameChanged(fileName()); 1549 ui.cbFileType->show(); 1550 ui.lFileType->show(); 1551 setMQTTVisible(false); 1552 break; 1553 case LiveDataSource::SourceType::NetworkTcpSocket: 1554 case LiveDataSource::SourceType::NetworkUdpSocket: 1555 ui.lHost->show(); 1556 ui.leHost->show(); 1557 ui.lePort->show(); 1558 ui.lPort->show(); 1559 if (sourceType == LiveDataSource::SourceType::NetworkTcpSocket) { 1560 ui.lSampleSize->hide(); 1561 ui.sbSampleSize->hide(); 1562 } else { 1563 ui.lSampleSize->show(); 1564 ui.sbSampleSize->show(); 1565 } 1566 1567 ui.lBaudRate->hide(); 1568 ui.cbBaudRate->hide(); 1569 ui.lSerialPort->hide(); 1570 ui.cbSerialPort->hide(); 1571 1572 ui.lFileName->hide(); 1573 m_cbFileName->hide(); 1574 ui.bFileInfo->hide(); 1575 ui.bOpen->hide(); 1576 ui.lRelativePath->hide(); 1577 ui.chbRelativePath->hide(); 1578 ui.chbLinkFile->hide(); 1579 1580 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 1581 1582 ui.gbOptions->setEnabled(true); 1583 ui.bManageFilters->setEnabled(true); 1584 ui.cbFilter->setEnabled(true); 1585 ui.cbFileType->setEnabled(true); 1586 ui.cbFileType->show(); 1587 ui.lFileType->show(); 1588 setMQTTVisible(false); 1589 break; 1590 case LiveDataSource::SourceType::LocalSocket: 1591 ui.lFileName->show(); 1592 m_cbFileName->show(); 1593 ui.bFileInfo->hide(); 1594 ui.bOpen->show(); 1595 ui.lRelativePath->hide(); 1596 ui.chbRelativePath->hide(); 1597 1598 ui.lSampleSize->hide(); 1599 ui.sbSampleSize->hide(); 1600 ui.cbBaudRate->hide(); 1601 ui.lBaudRate->hide(); 1602 ui.lHost->hide(); 1603 ui.leHost->hide(); 1604 ui.lPort->hide(); 1605 ui.lePort->hide(); 1606 ui.cbSerialPort->hide(); 1607 ui.lSerialPort->hide(); 1608 ui.chbLinkFile->hide(); 1609 1610 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 1611 1612 ui.gbOptions->setEnabled(true); 1613 ui.bManageFilters->setEnabled(true); 1614 ui.cbFilter->setEnabled(true); 1615 ui.cbFileType->setEnabled(true); 1616 ui.cbFileType->show(); 1617 ui.lFileType->show(); 1618 setMQTTVisible(false); 1619 break; 1620 case LiveDataSource::SourceType::SerialPort: 1621 ui.lBaudRate->show(); 1622 ui.cbBaudRate->show(); 1623 ui.lSerialPort->show(); 1624 ui.cbSerialPort->show(); 1625 ui.lSampleSize->show(); 1626 ui.sbSampleSize->show(); 1627 1628 ui.lHost->hide(); 1629 ui.leHost->hide(); 1630 ui.lePort->hide(); 1631 ui.lPort->hide(); 1632 1633 ui.lFileName->hide(); 1634 m_cbFileName->hide(); 1635 ui.bFileInfo->hide(); 1636 ui.bOpen->hide(); 1637 ui.lRelativePath->hide(); 1638 ui.chbRelativePath->hide(); 1639 ui.chbLinkFile->hide(); 1640 1641 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 1642 1643 ui.cbFileType->setEnabled(true); 1644 ui.cbFileType->show(); 1645 ui.gbOptions->setEnabled(true); 1646 ui.bManageFilters->setEnabled(true); 1647 ui.cbFilter->setEnabled(true); 1648 ui.lFileType->show(); 1649 setMQTTVisible(false); 1650 break; 1651 case LiveDataSource::SourceType::MQTT: 1652 #ifdef HAVE_MQTT 1653 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 1654 1655 //for MQTT we read ascii data only, hide the file type options 1656 for (int i = 0; i < ui.cbFileType->count(); ++i) { 1657 if (static_cast<AbstractFileFilter::FileType>(ui.cbFileType->itemData(i).toInt()) == AbstractFileFilter::FileType::Ascii) { 1658 if (ui.cbFileType->currentIndex() == i) 1659 initOptionsWidget(); 1660 else 1661 ui.cbFileType->setCurrentIndex(i); 1662 1663 break; 1664 } 1665 } 1666 ui.cbFileType->hide(); 1667 ui.lFileType->hide(); 1668 1669 ui.lBaudRate->hide(); 1670 ui.cbBaudRate->hide(); 1671 ui.lSerialPort->hide(); 1672 ui.cbSerialPort->hide(); 1673 ui.lHost->hide(); 1674 ui.leHost->hide(); 1675 ui.lPort->hide(); 1676 ui.lePort->hide(); 1677 ui.lFileName->hide(); 1678 m_cbFileName->hide(); 1679 ui.bFileInfo->hide(); 1680 ui.bOpen->hide(); 1681 ui.lRelativePath->hide(); 1682 ui.chbRelativePath->hide(); 1683 ui.chbLinkFile->hide(); 1684 1685 setMQTTVisible(true); 1686 1687 ui.cbFileType->setEnabled(true); 1688 ui.gbOptions->setEnabled(true); 1689 ui.bManageFilters->setEnabled(true); 1690 ui.cbFilter->setEnabled(true); 1691 1692 //in case there are already connections defined, 1693 //show the available topics for the currently selected connection 1694 mqttConnectionChanged(); 1695 #endif 1696 break; 1697 } 1698 1699 //deactivate/activate options that are specific to file of pipe sources only 1700 auto* typeModel = qobject_cast<const QStandardItemModel*>(ui.cbFileType->model()); 1701 if (sourceType != LiveDataSource::SourceType::FileOrPipe) { 1702 //deactivate file types other than ascii and binary 1703 for (int i = 2; i < ui.cbFileType->count(); ++i) 1704 typeModel->item(i)->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 1705 if (ui.cbFileType->currentIndex() > 1) 1706 ui.cbFileType->setCurrentIndex(1); 1707 1708 //"whole file" read option is available for file or pipe only, disable it 1709 typeModel = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model()); 1710 QStandardItem* item = typeModel->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 1711 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); 1712 if (static_cast<LiveDataSource::ReadingType>(ui.cbReadingType->currentIndex()) == LiveDataSource::ReadingType::WholeFile) 1713 ui.cbReadingType->setCurrentIndex(static_cast<int>(LiveDataSource::ReadingType::TillEnd)); 1714 1715 //"update options" groupbox can be deactivated for "file and pipe" if the file is invalid. 1716 //Activate the groupbox when switching from "file and pipe" to a different source type. 1717 ui.gbUpdateOptions->setEnabled(true); 1718 } else { 1719 for (int i = 2; i < ui.cbFileType->count(); ++i) 1720 typeModel->item(i)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 1721 1722 //enable "whole file" item for file or pipe 1723 typeModel = qobject_cast<const QStandardItemModel*>(ui.cbReadingType->model()); 1724 QStandardItem* item = typeModel->item(static_cast<int>(LiveDataSource::ReadingType::WholeFile)); 1725 item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); 1726 } 1727 1728 //disable the header options for non-file sources because: 1729 //* for sockets we allow to import one single value only at the moment 1730 //* for MQTT topics we don't allow to set the vector names since the different topics can have different number of columns 1731 //For files this option still can be usefull if the user have to re-read the whole file 1732 //and wants to use the header to set the column names or the user provides manually the column names. 1733 //TODO: adjust this logic later once we allow to import multiple columns from sockets, 1734 //it should be possible to provide the names of the columns 1735 bool visible = (currentSourceType() == LiveDataSource::SourceType::FileOrPipe); 1736 if (m_asciiOptionsWidget) 1737 m_asciiOptionsWidget->showAsciiHeaderOptions(visible); 1738 1739 emit sourceTypeChanged(); 1740 refreshPreview(); 1741 } 1742 1743 #ifdef HAVE_MQTT 1744 1745 /*! 1746 *\brief called when a different MQTT connection is selected in the connection ComboBox. 1747 * connects to the MQTT broker according to the connection settings. 1748 */ 1749 void ImportFileWidget::mqttConnectionChanged() { 1750 if (m_initialisingMQTT || ui.cbConnection->currentIndex() == -1) { 1751 ui.lLWT->hide(); 1752 ui.bLWT->hide(); 1753 ui.lTopics->hide(); 1754 return; 1755 } 1756 1757 WAIT_CURSOR; 1758 emit error(QString()); 1759 1760 //disconnected from the broker that was selected before 1761 disconnectMqttConnection(); 1762 1763 //determine the connection settings for the new broker and initialize the mqtt client 1764 KConfig config(m_configPath, KConfig::SimpleConfig); 1765 KConfigGroup group = config.group(ui.cbConnection->currentText()); 1766 1767 m_client = new QMqttClient; 1768 connect(m_client, &QMqttClient::connected, this, &ImportFileWidget::onMqttConnect); 1769 connect(m_client, &QMqttClient::disconnected, this, &ImportFileWidget::onMqttDisconnect); 1770 connect(m_client, &QMqttClient::messageReceived, this, &ImportFileWidget::mqttMessageReceived); 1771 connect(m_client, &QMqttClient::errorChanged, this, &ImportFileWidget::mqttErrorChanged); 1772 1773 m_client->setHostname(group.readEntry("Host")); 1774 m_client->setPort(group.readEntry("Port").toUInt()); 1775 1776 const bool useID = group.readEntry("UseID").toUInt(); 1777 if (useID) 1778 m_client->setClientId(group.readEntry("ClientID")); 1779 1780 const bool useAuthentication = group.readEntry("UseAuthentication").toUInt(); 1781 if (useAuthentication) { 1782 m_client->setUsername(group.readEntry("UserName")); 1783 m_client->setPassword(group.readEntry("Password")); 1784 } 1785 1786 //connect to the selected broker 1787 QDEBUG("Connect to " << m_client->hostname() << ":" << m_client->port()); 1788 if (!m_connectTimeoutTimer) { 1789 m_connectTimeoutTimer = new QTimer(this); 1790 m_connectTimeoutTimer->setInterval(6000); 1791 connect(m_connectTimeoutTimer, &QTimer::timeout, this, &ImportFileWidget::mqttConnectTimeout); 1792 } 1793 m_connectTimeoutTimer->start(); 1794 m_client->connectToHost(); 1795 } 1796 1797 void ImportFileWidget::disconnectMqttConnection() { 1798 if (m_client && m_client->state() == QMqttClient::ClientState::Connected) { 1799 emit MQTTClearTopics(); 1800 disconnect(m_client, &QMqttClient::disconnected, this, &ImportFileWidget::onMqttDisconnect); 1801 QDEBUG("Disconnecting from " << m_client->hostname()); 1802 m_client->disconnectFromHost(); 1803 delete m_client; 1804 m_client = nullptr; 1805 } 1806 } 1807 1808 /*! 1809 * returns \c true if there is a valid connection to an MQTT broker and the user has subscribed to at least 1 topic, 1810 * returns \c false otherwise. 1811 */ 1812 bool ImportFileWidget::isMqttValid() { 1813 if (!m_client) 1814 return false; 1815 1816 bool connected = (m_client->state() == QMqttClient::ClientState::Connected); 1817 bool subscribed = (m_subscriptionWidget->subscriptionCount() > 0); 1818 bool fileTypeOk = false; 1819 if (this->currentFileType() == AbstractFileFilter::FileType::Ascii) 1820 fileTypeOk = true; 1821 1822 return connected && subscribed && fileTypeOk; 1823 } 1824 1825 /*! 1826 *\brief called when the client connects to the broker successfully. 1827 * subscribes to every topic (# wildcard) in order to later list every available topic 1828 */ 1829 void ImportFileWidget::onMqttConnect() { 1830 m_connectTimeoutTimer->stop(); 1831 if (m_client->error() == QMqttClient::NoError) { 1832 ui.frameSubscriptions->setVisible(true); 1833 m_subscriptionWidget->setVisible(true); 1834 m_subscriptionWidget->makeVisible(true); 1835 1836 if (!m_client->subscribe(QMqttTopicFilter(QLatin1String("#")), 1)) 1837 emit error(i18n("Couldn't subscribe to all available topics.")); 1838 else { 1839 emit error(QString()); 1840 ui.lLWT->show(); 1841 ui.bLWT->show(); 1842 ui.lTopics->show(); 1843 } 1844 } else 1845 emit error("on mqtt connect error " + QString::number(m_client->error())); 1846 1847 emit subscriptionsChanged(); 1848 RESET_CURSOR; 1849 } 1850 1851 /*! 1852 *\brief called when the client disconnects from the broker successfully 1853 * removes every information about the former connection 1854 */ 1855 void ImportFileWidget::onMqttDisconnect() { 1856 DEBUG("Disconnected from " << STDSTRING(m_client->hostname())); 1857 m_connectTimeoutTimer->stop(); 1858 1859 ui.lTopics->hide(); 1860 ui.frameSubscriptions->hide(); 1861 ui.lLWT->hide(); 1862 ui.bLWT->hide(); 1863 1864 ui.cbConnection->setCurrentIndex(-1); 1865 1866 emit subscriptionsChanged(); 1867 emit error(i18n("Disconnected from '%1'.", m_client->hostname())); 1868 RESET_CURSOR; 1869 } 1870 1871 /*! 1872 *\brief called when the subscribe button is pressed 1873 * subscribes to the topic represented by the current item of twTopics 1874 */ 1875 void ImportFileWidget::subscribeTopic(const QString& name, uint QoS) { 1876 const QMqttTopicFilter filter {name}; 1877 QMqttSubscription* tempSubscription = m_client->subscribe(filter, static_cast<quint8>(QoS) ); 1878 1879 if (tempSubscription) { 1880 m_mqttSubscriptions.push_back(tempSubscription); 1881 connect(tempSubscription, &QMqttSubscription::messageReceived, this, &ImportFileWidget::mqttSubscriptionMessageReceived); 1882 emit subscriptionsChanged(); 1883 } 1884 } 1885 1886 /*! 1887 *\brief Unsubscribes from the given topic, and removes any data connected to it 1888 * 1889 * \param topicName the name of a topic we want to unsubscribe from 1890 */ 1891 void ImportFileWidget::unsubscribeTopic(const QString& topicName, QVector<QTreeWidgetItem*> children) { 1892 if (topicName.isEmpty()) 1893 return; 1894 1895 for (int i = 0; i< m_mqttSubscriptions.count(); ++i) { 1896 if (m_mqttSubscriptions[i]->topic().filter() == topicName) { 1897 //explicitely disconnect from the signal, callling QMqttClient::unsubscribe() below is not enough 1898 disconnect(m_mqttSubscriptions.at(i), &QMqttSubscription::messageReceived, this, &ImportFileWidget::mqttSubscriptionMessageReceived); 1899 m_mqttSubscriptions.remove(i); 1900 break; 1901 } 1902 } 1903 1904 QMqttTopicFilter filter{topicName}; 1905 m_client->unsubscribe(filter); 1906 1907 QMapIterator<QMqttTopicName, QMqttMessage> j(m_lastMessage); 1908 while (j.hasNext()) { 1909 j.next(); 1910 if (MQTTSubscriptionWidget::checkTopicContains(topicName, j.key().name())) 1911 m_lastMessage.remove(j.key()); 1912 } 1913 1914 if (m_willSettings.willTopic == topicName) { 1915 if (m_subscriptionWidget->subscriptionCount() > 0) 1916 m_willSettings.willTopic = children[0]->text(0); 1917 else 1918 m_willSettings.willTopic.clear(); 1919 } 1920 1921 //signals that there was a change among the subscribed topics 1922 emit subscriptionsChanged(); 1923 refreshPreview(); 1924 } 1925 1926 /*! 1927 *\brief called when the client receives a message 1928 * if the message arrived from a new topic, the topic is put in twTopics 1929 */ 1930 void ImportFileWidget::mqttMessageReceived(const QByteArray& message, const QMqttTopicName& topic) { 1931 Q_UNUSED(message); 1932 // qDebug()<<"received " << topic.name(); 1933 if (m_addedTopics.contains(topic.name())) 1934 return; 1935 1936 m_addedTopics.push_back(topic.name()); 1937 m_subscriptionWidget->setTopicTreeText(i18n("Available (%1)", m_addedTopics.size())); 1938 QStringList name; 1939 QString rootName; 1940 const QChar sep = '/'; 1941 1942 if (topic.name().contains(sep)) { 1943 const QStringList& list = topic.name().split(sep, QString::SkipEmptyParts); 1944 1945 if (!list.isEmpty()) { 1946 rootName = list.at(0); 1947 name.append(list.at(0)); 1948 int topItemIdx = -1; 1949 //check whether the first level of the topic can be found in twTopics 1950 for (int i = 0; i < m_subscriptionWidget->topicCount(); ++i) { 1951 if (m_subscriptionWidget->topLevelTopic(i)->text(0) == list.at(0)) { 1952 topItemIdx = i; 1953 break; 1954 } 1955 } 1956 1957 //if not we simply add every level of the topic to the tree 1958 if (topItemIdx < 0) { 1959 auto* currentItem = new QTreeWidgetItem(name); 1960 m_subscriptionWidget->addTopic(currentItem); 1961 for (int i = 1; i < list.size(); ++i) { 1962 name.clear(); 1963 name.append(list.at(i)); 1964 currentItem->addChild(new QTreeWidgetItem(name)); 1965 currentItem = currentItem->child(0); 1966 } 1967 } 1968 //otherwise we search for the first level that isn't part of the tree, 1969 //then add every level of the topic to the tree from that certain level 1970 else { 1971 QTreeWidgetItem* currentItem = m_subscriptionWidget->topLevelTopic(topItemIdx); 1972 int listIdx = 1; 1973 for (; listIdx < list.size(); ++listIdx) { 1974 QTreeWidgetItem* childItem = nullptr; 1975 bool found = false; 1976 for (int j = 0; j < currentItem->childCount(); ++j) { 1977 childItem = currentItem->child(j); 1978 if (childItem->text(0) == list.at(listIdx)) { 1979 found = true; 1980 currentItem = childItem; 1981 break; 1982 } 1983 } 1984 if (!found) { 1985 //this is the level that isn't present in the tree 1986 break; 1987 } 1988 } 1989 1990 //add every level to the tree starting with the first level that isn't part of the tree 1991 for (; listIdx < list.size(); ++listIdx) { 1992 name.clear(); 1993 name.append(list.at(listIdx)); 1994 currentItem->addChild(new QTreeWidgetItem(name)); 1995 currentItem = currentItem->child(currentItem->childCount() - 1); 1996 } 1997 } 1998 } 1999 } else { 2000 rootName = topic.name(); 2001 name.append(topic.name()); 2002 m_subscriptionWidget->addTopic(new QTreeWidgetItem(name)); 2003 } 2004 2005 //if a subscribed topic contains the new topic, we have to update twSubscriptions 2006 for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i) { 2007 const QStringList subscriptionName = m_subscriptionWidget->topLevelSubscription(i)->text(0).split(sep, QString::SkipEmptyParts); 2008 if (!subscriptionName.isEmpty()) { 2009 if (rootName == subscriptionName.first()) { 2010 QVector<QString> subscriptions; 2011 for (const auto& sub : m_mqttSubscriptions) 2012 subscriptions.push_back(sub->topic().filter()); 2013 emit updateSubscriptionTree(subscriptions); 2014 break; 2015 } 2016 } 2017 } 2018 2019 //signals that a newTopic was added, in order to fill the completer of leTopics 2020 emit newTopic(rootName); 2021 } 2022 2023 /*! 2024 *\brief called when the client receives a message from a subscribed topic (that isn't the "#" wildcard) 2025 */ 2026 void ImportFileWidget::mqttSubscriptionMessageReceived(const QMqttMessage &msg) { 2027 QDEBUG("message received from: " << msg.topic().name()); 2028 2029 //update the last message for the topic 2030 m_lastMessage[msg.topic()] = msg; 2031 } 2032 2033 /*! 2034 *\brief called when the clientError of the MQTT client changes 2035 * 2036 * \param clientError the current error of the client 2037 */ 2038 void ImportFileWidget::mqttErrorChanged(QMqttClient::ClientError clientError) { 2039 switch (clientError) { 2040 case QMqttClient::BadUsernameOrPassword: 2041 emit error(i18n("Wrong username or password")); 2042 break; 2043 case QMqttClient::IdRejected: 2044 emit error(i18n("The client ID wasn't accepted")); 2045 break; 2046 case QMqttClient::ServerUnavailable: 2047 case QMqttClient::TransportInvalid: 2048 emit error(i18n("The broker %1 couldn't be reached.", m_client->hostname())); 2049 break; 2050 case QMqttClient::NotAuthorized: 2051 emit error(i18n("The client is not authorized to connect.")); 2052 break; 2053 case QMqttClient::UnknownError: 2054 emit error(i18n("An unknown error occurred.")); 2055 break; 2056 case QMqttClient::NoError: 2057 case QMqttClient::InvalidProtocolVersion: 2058 case QMqttClient::ProtocolViolation: 2059 case QMqttClient::Mqtt5SpecificError: 2060 emit error(i18n("An error occurred.")); 2061 break; 2062 default: 2063 emit error(i18n("An error occurred.")); 2064 break; 2065 } 2066 m_connectTimeoutTimer->stop(); 2067 } 2068 2069 /*! 2070 *\brief called when m_connectTimeoutTimer ticks, 2071 * meaning that the client couldn't connect to the broker in 5 seconds 2072 * disconnects the client, stops the timer, and warns the user 2073 */ 2074 void ImportFileWidget::mqttConnectTimeout() { 2075 m_client->disconnectFromHost(); 2076 m_connectTimeoutTimer->stop(); 2077 emit error(i18n("Connecting to '%1:%2' timed out.", m_client->hostname(), m_client->port())); 2078 RESET_CURSOR; 2079 } 2080 2081 /*! 2082 Shows the MQTT connection manager where the connections are created and edited. 2083 The selected connection is selected in the connection combo box in this widget. 2084 */ 2085 void ImportFileWidget::showMQTTConnectionManager() { 2086 bool previousConnectionChanged = false; 2087 auto* dlg = new MQTTConnectionManagerDialog(this, ui.cbConnection->currentText(), previousConnectionChanged); 2088 2089 if (dlg->exec() == QDialog::Accepted) { 2090 //re-read the available connections to be in sync with the changes in MQTTConnectionManager 2091 m_initialisingMQTT = true; 2092 const QString& prevConn = ui.cbConnection->currentText(); 2093 ui.cbConnection->clear(); 2094 readMQTTConnections(); 2095 m_initialisingMQTT = false; 2096 2097 //select the connection the user has selected in MQTTConnectionManager 2098 const QString& conn = dlg->connection(); 2099 2100 int index = ui.cbConnection->findText(conn); 2101 if (conn != prevConn) {//Current connection isn't the previous one 2102 if (ui.cbConnection->currentIndex() != index) 2103 ui.cbConnection->setCurrentIndex(index); 2104 else 2105 mqttConnectionChanged(); 2106 } else if (dlg->initialConnectionChanged()) {//Current connection is the same with previous one but it changed 2107 if (ui.cbConnection->currentIndex() == index) 2108 mqttConnectionChanged(); 2109 else 2110 ui.cbConnection->setCurrentIndex(index); 2111 } else { //Previous connection wasn't changed 2112 m_initialisingMQTT = true; 2113 ui.cbConnection->setCurrentIndex(index); 2114 m_initialisingMQTT = false; 2115 } 2116 } 2117 delete dlg; 2118 } 2119 2120 /*! 2121 loads all available saved MQTT nconnections 2122 */ 2123 void ImportFileWidget::readMQTTConnections() { 2124 DEBUG("ImportFileWidget: reading available MQTT connections"); 2125 KConfig config(m_configPath, KConfig::SimpleConfig); 2126 for (const auto& name : config.groupList()) 2127 ui.cbConnection->addItem(name); 2128 } 2129 2130 /*! 2131 * \brief Shows the mqtt will settings widget, which allows the user to modify the will settings 2132 */ 2133 void ImportFileWidget::showWillSettings() { 2134 QMenu menu; 2135 2136 QVector<QTreeWidgetItem*> children; 2137 for (int i = 0; i < m_subscriptionWidget->subscriptionCount(); ++i) 2138 MQTTSubscriptionWidget::findSubscriptionLeafChildren(children, m_subscriptionWidget->topLevelSubscription(i)); 2139 2140 QVector<QString> topics; 2141 for (const auto& child : children) 2142 topics.append(child->text(0)); 2143 2144 MQTTWillSettingsWidget willSettingsWidget(&menu, m_willSettings, topics); 2145 2146 connect(&willSettingsWidget, &MQTTWillSettingsWidget::applyClicked, [this, &menu, &willSettingsWidget]() { 2147 m_willSettings = willSettingsWidget.will(); 2148 menu.close(); 2149 }); 2150 auto* widgetAction = new QWidgetAction(this); 2151 widgetAction->setDefaultWidget(&willSettingsWidget); 2152 menu.addAction(widgetAction); 2153 2154 const QPoint pos(ui.bLWT->sizeHint().width(),ui.bLWT->sizeHint().height()); 2155 menu.exec(ui.bLWT->mapToGlobal(pos)); 2156 } 2157 2158 void ImportFileWidget::enableWill(bool enable) { 2159 if (enable) { 2160 if (!ui.bLWT->isEnabled()) 2161 ui.bLWT->setEnabled(enable); 2162 } else 2163 ui.bLWT->setEnabled(enable); 2164 } 2165 2166 2167 /*! 2168 saves the settings to the MQTTClient \c client. 2169 */ 2170 void ImportFileWidget::saveMQTTSettings(MQTTClient* client) const { 2171 DEBUG("ImportFileWidget::saveMQTTSettings"); 2172 auto updateType = static_cast<MQTTClient::UpdateType>(ui.cbUpdateType->currentIndex()); 2173 auto readingType = static_cast<MQTTClient::ReadingType>(ui.cbReadingType->currentIndex()); 2174 2175 currentFileFilter(); 2176 client->setFilter(static_cast<AsciiFilter*>(m_currentFilter.release())); // pass ownership of the filter to MQTTClient 2177 2178 client->setReadingType(readingType); 2179 2180 if (updateType == MQTTClient::UpdateType::TimeInterval) 2181 client->setUpdateInterval(ui.sbUpdateInterval->value()); 2182 2183 client->setKeepNValues(ui.sbKeepNValues->value()); 2184 client->setUpdateType(updateType); 2185 2186 if (readingType != MQTTClient::ReadingType::TillEnd) 2187 client->setSampleSize(ui.sbSampleSize->value()); 2188 2189 client->setMQTTClientHostPort(m_client->hostname(), m_client->port()); 2190 2191 KConfig config(m_configPath, KConfig::SimpleConfig); 2192 KConfigGroup group = config.group(ui.cbConnection->currentText()); 2193 2194 bool useID = group.readEntry("UseID").toUInt(); 2195 bool useAuthentication = group.readEntry("UseAuthentication").toUInt(); 2196 2197 client->setMQTTUseAuthentication(useAuthentication); 2198 if (useAuthentication) 2199 client->setMQTTClientAuthentication(m_client->username(), m_client->password()); 2200 2201 client->setMQTTUseID(useID); 2202 if (useID) 2203 client->setMQTTClientId(m_client->clientId()); 2204 2205 for (int i = 0; i < m_mqttSubscriptions.count(); ++i) 2206 client->addInitialMQTTSubscriptions(m_mqttSubscriptions[i]->topic(), m_mqttSubscriptions[i]->qos()); 2207 2208 const bool retain = group.readEntry("Retain").toUInt(); 2209 client->setMQTTRetain(retain); 2210 2211 if (m_willSettings.enabled) 2212 client->setWillSettings(m_willSettings); 2213 } 2214 #endif