diff --git a/src/backend/datasources/FileDataSource.cpp b/src/backend/datasources/FileDataSource.cpp index 423c111ea..2091641cb 100644 --- a/src/backend/datasources/FileDataSource.cpp +++ b/src/backend/datasources/FileDataSource.cpp @@ -1,954 +1,970 @@ /*************************************************************************** File : FileDataSource.cpp Project : LabPlot Description : Represents file data source -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/FileDataSource.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/core/Project.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class FileDataSource \brief Represents data stored in a file. Reading and writing is done with the help of appropriate I/O-filters. \ingroup datasources */ FileDataSource::FileDataSource(AbstractScriptingEngine* engine, const QString& name, bool loading) : Spreadsheet(engine, name, loading), m_fileType(Ascii), m_fileWatched(false), m_fileLinked(false), m_paused(false), m_prepared(false), m_bytesRead(0), m_filter(nullptr), m_updateTimer(new QTimer(this)), m_fileSystemWatcher(nullptr), m_file(nullptr), m_localSocket(nullptr), m_tcpSocket(nullptr), m_serialPort(nullptr), m_device(nullptr) { initActions(); connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(read())); } FileDataSource::~FileDataSource() { if (m_filter) delete m_filter; if (m_fileSystemWatcher) delete m_fileSystemWatcher; if (m_file) delete m_file; if (m_localSocket) delete m_localSocket; if (m_tcpSocket) delete m_tcpSocket; if (m_serialPort) delete m_serialPort; delete m_updateTimer; } /*! * depending on the update type, periodically or on data changes, starts the timer or activates the file watchers, respectively. */ void FileDataSource::ready() { if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else watch(); } void FileDataSource::initActions() { m_reloadAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), this); connect(m_reloadAction, SIGNAL(triggered()), this, SLOT(read())); m_toggleWatchAction = new QAction(i18n("Watch the file"), this); m_toggleWatchAction->setCheckable(true); connect(m_toggleWatchAction, SIGNAL(triggered()), this, SLOT(watchToggled())); m_toggleLinkAction = new QAction(i18n("Link the file"), this); m_toggleLinkAction->setCheckable(true); connect(m_toggleLinkAction, SIGNAL(triggered()), this, SLOT(linkToggled())); m_plotDataAction = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); connect(m_plotDataAction, SIGNAL(triggered()), this, SLOT(plotData())); } //TODO make the view customizable (show as a spreadsheet or as a pure text file in an editor) QWidget *FileDataSource::view() const { if (!m_view) m_view = new SpreadsheetView(const_cast(this)); return m_view; } /*! * \brief Returns a list with the names of the available ports */ QStringList FileDataSource::availablePorts() { QStringList ports; qDebug() << "available ports count:" << QSerialPortInfo::availablePorts().size(); for(const QSerialPortInfo& sp : QSerialPortInfo::availablePorts()) { ports.append(sp.portName()); qDebug() << sp.description(); qDebug() << sp.manufacturer(); qDebug() << sp.portName(); qDebug() << sp.serialNumber(); qDebug() << sp.systemLocation(); } return ports; } /*! * \brief Returns a list with the supported baud rates */ QStringList FileDataSource::supportedBaudRates() { QStringList baudRates; for(const auto& baud : QSerialPortInfo::standardBaudRates()) baudRates.append(QString::number(baud)); return baudRates; } /*! * \brief Updates this data source at this moment */ void FileDataSource::updateNow() { m_updateTimer->stop(); read(); //restart the timer after update if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); } /*! * \brief FileDataSource::stopReading */ //TODO: do we want this? void FileDataSource::stopReading() { if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! * \brief Continue reading from the live data source after it was paused. */ void FileDataSource::continueReading() { m_paused = false; if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else if (m_updateType == NewData) connect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! * \brief Pause the reading of the live data source. */ void FileDataSource::pauseReading() { m_paused = true; if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) disconnect(m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } /*! returns the list with all supported data file formats. */ QStringList FileDataSource::fileTypes() { // see FileDataSource::FileType return (QStringList()<< i18n("ASCII data") << i18n("Binary data") << i18n("Image") << i18n("Hierarchical Data Format (HDF)") << i18n("Network Common Data Format (NetCDF)") // << "CDF" << i18n("Flexible Image Transport System Data Format (FITS)") // << i18n("Sound") ); } void FileDataSource::setFileName(const QString& name) { - m_fileName=name; + m_fileName = name; } QString FileDataSource::fileName() const { return m_fileName; } +/*! + * \brief Sets the local socket's server name to name + * \param name + */ +void FileDataSource::setLocalSocketName(const QString & name) { + m_localSocketName = name; +} + +QString FileDataSource::localSocketName() const { + return m_localSocketName; +} + void FileDataSource::setFileType(const FileType type) { m_fileType = type; } FileDataSource::FileType FileDataSource::fileType() const { return m_fileType; } void FileDataSource::setFilter(AbstractFileFilter* f) { m_filter = f; } AbstractFileFilter* FileDataSource::filter() const { return m_filter; } /*! sets whether the file should be watched or not. In the first case the data source will be automatically updated on file changes. */ void FileDataSource::setFileWatched(const bool b) { m_fileWatched = b; } bool FileDataSource::isFileWatched() const { return m_fileWatched; } /*! * \brief Sets whether we'll keep the last values or append it to the previous ones * \param keepLastValues */ void FileDataSource::setKeepLastValues(const bool keepLastValues) { m_keepLastValues = keepLastValues; } bool FileDataSource::keepLastValues() const { return m_keepLastValues; } /*! * \brief Sets the serial port's baud rate * \param baudrate */ void FileDataSource::setBaudRate(const int baudrate) { m_baudRate = baudrate; } int FileDataSource::baudRate() const { return m_baudRate; } /*! * \brief Sets the source's update interval to \c interval * \param interval */ void FileDataSource::setUpdateInterval(const int interval) { m_updateInterval = interval; m_updateTimer->start(m_updateInterval); } int FileDataSource::updateInterval() const { return m_updateInterval; } /*! * \brief Sets how many values we should store * \param keepnvalues */ void FileDataSource::setKeepNvalues(const int keepnvalues) { m_keepNvalues = keepnvalues; } int FileDataSource::keepNvalues() const { return m_keepNvalues; } /*! * \brief Sets the network socket's port to port * \param port */ void FileDataSource::setPort(const int port) { m_port = port; } int FileDataSource::port() const { return m_port; } /*! * \brief Sets the serial port's name to name * \param name */ void FileDataSource::setSerialPort(const QString &name) { m_serialPortName = name; } QString FileDataSource::serialPortName() const { return m_serialPortName; } /*! * \brief Sets the sample rate to samplerate * \param samplerate */ void FileDataSource::setSampleRate(const int samplerate) { m_sampleRate = samplerate; } int FileDataSource::sampleRate() const { return m_sampleRate; } /*! * \brief Sets the source's type to sourcetype * \param sourcetype */ void FileDataSource::setSourceType(const SourceType sourcetype) { m_sourceType = sourcetype; } FileDataSource::SourceType FileDataSource::sourceType() const { return m_sourceType; } /*! * \brief Sets the source's reading type to readingType * \param readingType */ void FileDataSource::setReadingType(const ReadingType readingType) { m_readingType = readingType; } FileDataSource::ReadingType FileDataSource::readingType() const { return m_readingType; } /*! * \brief Sets the source's update type to updatetype * \param updatetype */ void FileDataSource::setUpdateType(const UpdateType updatetype) { if (updatetype == NewData) m_updateTimer->stop(); m_updateType = updatetype; } FileDataSource::UpdateType FileDataSource::updateType() const { return m_updateType; } /*! * \brief Sets the network socket's host * \param host */ void FileDataSource::setHost(const QString & host) { m_host = host; } QString FileDataSource::host() const { return m_host; } /*! sets whether only a link to the file is saved in the project file (\c b=true) or the whole content of the file (\c b=false). */ void FileDataSource::setFileLinked(const bool b) { m_fileLinked = b; } /*! returns \c true if only a link to the file is saved in the project file. \c false otherwise. */ bool FileDataSource::isFileLinked() const { return m_fileLinked; } QIcon FileDataSource::icon() const { QIcon icon; if (m_fileType == FileDataSource::Ascii) icon = QIcon::fromTheme("text-plain"); else if (m_fileType == FileDataSource::Binary) icon = QIcon::fromTheme("application-octet-stream"); else if (m_fileType == FileDataSource::Image) icon = QIcon::fromTheme("image-x-generic"); // TODO: HDF, NetCDF, FITS, etc. return icon; } QMenu* FileDataSource::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, m_plotDataAction); menu->insertSeparator(firstAction); //TODO: doesnt' always make sense... // if (!m_fileWatched) // menu->insertAction(firstAction, m_reloadAction); // // m_toggleWatchAction->setChecked(m_fileWatched); // menu->insertAction(firstAction, m_toggleWatchAction); // // m_toggleLinkAction->setChecked(m_fileLinked); // menu->insertAction(firstAction, m_toggleLinkAction); return menu; } //############################################################################## //################################# SLOTS #################################### //############################################################################## /* * called periodically or on new data changes (file changed, new data in the socket, etc.) */ void FileDataSource::read() { if (m_filter == nullptr) return; //initialize the device (file, socket, serial port), when calling this function for the first time if (!m_prepared) { switch (m_sourceType) { case FileOrPipe: m_file = new QFile(m_fileName); m_device = m_file; break; case NetworkSocket: m_tcpSocket = new QTcpSocket; m_device = m_tcpSocket; connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); -// connect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); + connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError))); break; case LocalSocket: - m_localSocket = new QLocalSocket; - m_localSocket->setServerName(m_fileName); + m_localSocket = new QLocalSocket(this); + m_localSocket->setServerName(m_localSocketName); m_device = m_localSocket; connect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); connect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); break; case SerialPort: m_serialPort = new QSerialPort; m_device = m_serialPort; m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(serialPortError(QSerialPort::SerialPortError))); connect(m_serialPort, SIGNAL(readyRead()), this, SLOT(readyRead())); break; } m_prepared = true; } qint64 bytes = 0; switch (m_sourceType) { case FileOrPipe: switch (m_fileType) { case Ascii: - qDebug() << "reading live ascii file.." ; + qDebug() << "Reading live ascii file.." ; bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead, AbstractFileFilter::Replace); m_bytesRead += bytes; - qDebug() << "read " << bytes << " bytes, in total: " << m_bytesRead; + qDebug() << "Read " << bytes << " bytes, in total: " << m_bytesRead; break; case Binary: //bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; default: break; } break; case NetworkSocket: DEBUG("reading from a network socket"); m_tcpSocket->abort(); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); break; case LocalSocket: DEBUG("reading from a local socket"); m_localSocket->abort(); - m_localSocket->connectToServer(m_fileName, QLocalSocket::ReadOnly); + qDebug() << m_localSocketName; + m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); break; case SerialPort: DEBUG("reading from the serial port"); //TODO break; } } /*! * Slot for the signal that is emitted once every time new data is available for reading from the device. * It will only be emitted again once new data is available, such as when a new payload of network data has arrived on the network socket, * or when a new block of data has been appended to your device. */ void FileDataSource::readyRead() { DEBUG("Got new data from the device"); if (m_fileType == Ascii) dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); // else if (m_fileType == Binary) // dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_localSocket, this); } void FileDataSource::localSocketError(QLocalSocket::LocalSocketError socketError) { + disconnect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); + disconnect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead())); + switch (socketError) { case QLocalSocket::ServerNotFoundError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket was not found. Please check the socket name.")); break; case QLocalSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The connection was refused by the peer")); break; case QLocalSocket::PeerClosedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket has closed the connection.")); break; default: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The following error occurred: %1.").arg(m_localSocket->errorString())); } } void FileDataSource::tcpSocketError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The connection was refused by the peer. Make sure the server is running and check the host name and port settings.")); break; case QAbstractSocket::RemoteHostClosedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The remote host closed the connection.")); break; case QAbstractSocket::HostNotFoundError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The host was not found. Please check the host name and port settings.")); break; default: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The following error occurred: %1.").arg(m_tcpSocket->errorString())); } } void FileDataSource::serialPortError(QSerialPort::SerialPortError serialPortError) { switch (serialPortError) { case QSerialPort::DeviceNotFoundError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device.")); break; case QSerialPort::PermissionError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device. Please check your permissions on this device.")); break; case QSerialPort::OpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Device already opened.")); break; case QSerialPort::NotOpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device is not opened.")); break; case QSerialPort::ReadError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data.")); break; case QSerialPort::ResourceError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data. The device is removed.")); break; case QSerialPort::TimeoutError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device timed out.")); break; default: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The following error occurred: %1.").arg(m_serialPort->errorString())); break; } } void FileDataSource::watchToggled() { m_fileWatched = !m_fileWatched; watch(); project()->setChanged(true); } void FileDataSource::linkToggled() { m_fileLinked = !m_fileLinked; project()->setChanged(true); } //watch the file upon reading for changes if required void FileDataSource::watch() { if (m_fileWatched) { if (!m_fileSystemWatcher) { m_fileSystemWatcher = new QFileSystemWatcher; connect (m_fileSystemWatcher, SIGNAL(fileChanged(QString)), this, SLOT(read())); } if ( !m_fileSystemWatcher->files().contains(m_fileName) ) m_fileSystemWatcher->addPath(m_fileName); } else { if (m_fileSystemWatcher) m_fileSystemWatcher->removePath(m_fileName); } } /*! returns a string containing the general information about the file \c name and some content specific information (number of columns and lines for ASCII, color-depth for images etc.). */ QString FileDataSource::fileInfoString(const QString &name) { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; if(file==0) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; //general information about the file infoStrings << "" + fileName + "
"; fileInfo.setFile(fileName); infoStrings << i18n("Readable: %1", fileInfo.isReadable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Writable: %1", fileInfo.isWritable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Executable: %1", fileInfo.isExecutable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Created: %1", fileInfo.created().toString()); infoStrings << i18n("Last modified: %1", fileInfo.lastModified().toString()); infoStrings << i18n("Last read: %1", fileInfo.lastRead().toString()); infoStrings << i18n("Owner: %1", fileInfo.owner()); infoStrings << i18n("Group: %1", fileInfo.group()); infoStrings << i18n("Size: %1", i18np("%1 cByte", "%1 cBytes", fileInfo.size())); #ifdef HAVE_FITS if (fileName.endsWith(QLatin1String(".fits"))) { FITSFilter* fitsFilter = new FITSFilter; infoStrings << i18n("Images: %1", QString::number(fitsFilter->imagesCount(fileName) )); infoStrings << i18n("Tables: %1", QString::number(fitsFilter->tablesCount(fileName) )); delete fitsFilter; } #endif // file type and type specific information about the file #ifdef Q_OS_LINUX QProcess *proc = new QProcess(); QStringList args; args<<"-b"<start( "file", args); if(proc->waitForReadyRead(1000) == false) infoStrings << i18n("Could not open file %1 for reading.", fileName); else { fileTypeString = proc->readLine(); if( fileTypeString.contains(i18n("cannot open")) ) fileTypeString=""; else { fileTypeString.remove(fileTypeString.length()-1,1); // remove '\n' } } infoStrings << i18n("File type: %1", fileTypeString); #endif //TODO depending on the file type, generate additional information about the file: //Number of lines for ASCII, color-depth for images etc. Use the specific filters here. // port the old labplot1.6 code. if( fileTypeString.contains("ASCII")) { infoStrings << "
"; //TODO: consider choosen separator infoStrings << i18n("Number of columns: %1", AsciiFilter::columnNumber(fileName)); infoStrings << i18n("Number of lines: %1", AsciiFilter::lineNumber(fileName)); } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } void FileDataSource::plotData() { PlotDataDialog* dlg = new PlotDataDialog(this); dlg->exec(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void FileDataSource::save(QXmlStreamWriter* writer) const { writer->writeStartElement("fileDataSource"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement("general"); writer->writeAttribute("fileName", m_fileName); writer->writeAttribute("fileType", QString::number(m_fileType)); writer->writeAttribute("fileWatched", QString::number(m_fileWatched)); writer->writeAttribute("fileLinked", QString::number(m_fileLinked)); writer->writeAttribute("updateType", QString::number(m_updateType)); writer->writeAttribute("readingType", QString::number(m_readingType)); writer->writeAttribute("sourceType", QString::number(m_sourceType)); writer->writeAttribute("keepValues", QString::number(m_keepNvalues)); if (m_updateType == TimeInterval) writer->writeAttribute("updateFrequency", QString::number(m_updateInterval)); if (m_readingType != TillEnd) writer->writeAttribute("sampleRate", QString::number(m_sampleRate)); switch (m_sourceType) { case SerialPort: writer->writeAttribute("baudRate", QString::number(m_baudRate)); writer->writeAttribute("serialPortName", m_serialPortName); break; case NetworkSocket: writer->writeAttribute("host", m_host); writer->writeAttribute("port", QString::number(m_port)); break; case FileOrPipe: break; case LocalSocket: break; default: break; } writer->writeEndElement(); //filter m_filter->save(writer); //columns if (!m_fileLinked) { foreach (Column * col, children(IncludeHidden)) col->save(writer); } writer->writeEndElement(); // "fileDataSource" } /*! Loads from XML. */ bool FileDataSource::load(XmlStreamReader* reader) { if(!reader->isStartElement() || reader->name() != "fileDataSource") { reader->raiseError(i18n("no fileDataSource element found")); return false; } if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "fileDataSource") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileName'")); else m_fileName = str; str = attribs.value("fileType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileType'")); else m_fileType = (FileType)str.toInt(); str = attribs.value("fileWatched").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileWatched'")); else m_fileWatched = str.toInt(); str = attribs.value("fileLinked").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileLinked'")); else m_fileLinked = str.toInt(); str = attribs.value("updateType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateType'")); else m_updateType = static_cast(str.toInt()); str = attribs.value("sourceType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sourceType'")); else m_sourceType = static_cast(str.toInt()); str = attribs.value("readingType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'readingType'")); else m_readingType = static_cast(str.toInt()); if (m_updateType == TimeInterval) { str = attribs.value("updateFrequency").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateFrequency'")); else m_updateInterval = str.toInt(); } if (m_readingType != TillEnd) { str = attribs.value("sampleRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sampleRate'")); else m_sampleRate = str.toInt(); } switch (m_sourceType) { case SerialPort: str = attribs.value("baudRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'baudRate'")); else m_baudRate = str.toInt(); str = attribs.value("serialPortName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'serialPortName'")); else m_serialPortName = str; break; case NetworkSocket: str = attribs.value("host").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'host'")); else m_host = str; str = attribs.value("port").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'port'")); else m_host = str; break; case FileOrPipe: break; case LocalSocket: break; default: break; } } else if (reader->name() == "asciiFilter") { m_filter = new AsciiFilter(); if (!m_filter->load(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Text); if (!column->load(reader)) { delete column; setColumnCount(0); return false; } addChild(column); } else {// unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } //read the content of the file if it was only linked if (m_fileLinked) this->read(); return !reader->hasError(); } diff --git a/src/backend/datasources/FileDataSource.h b/src/backend/datasources/FileDataSource.h index 0c10dc8c3..a132ea15d 100644 --- a/src/backend/datasources/FileDataSource.h +++ b/src/backend/datasources/FileDataSource.h @@ -1,206 +1,210 @@ /*************************************************************************** File : FileDataSource.h Project : LabPlot Description : File data source -------------------------------------------------------------------- Copyright : (C) 2012-2013 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef FILEDATASOURCE_H #define FILEDATASOURCE_H #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include #include #include class QString; class AbstractFileFilter; class QFileSystemWatcher; class QAction; class QTcpSocket; class QFile; class FileDataSource : public Spreadsheet { Q_OBJECT Q_ENUMS(FileType) public: enum FileType {Ascii, Binary, Image, HDF, NETCDF, FITS}; enum SourceType { FileOrPipe = 0, NetworkSocket, LocalSocket, SerialPort }; enum UpdateType { TimeInterval = 0, NewData }; enum ReadingType { ContinousFixed = 0, FromEnd, TillEnd }; FileDataSource(AbstractScriptingEngine*, const QString& name, bool loading = false); ~FileDataSource(); void ready(); static QStringList supportedBaudRates(); static QStringList availablePorts(); static QStringList fileTypes(); static QString fileInfoString(const QString&); void setFileType(const FileType); FileType fileType() const; UpdateType updateType() const; void setUpdateType(const UpdateType); SourceType sourceType() const; void setSourceType(const SourceType); ReadingType readingType() const; void setReadingType(const ReadingType); int sampleRate() const; void setSampleRate(const int); int port() const; void setPort(const int); void setSerialPort(const QString& name); QString serialPortName() const; QString host() const; void setHost(const QString&); int baudRate() const; void setBaudRate(const int); void setUpdateInterval(const int); int updateInterval() const; void setKeepNvalues(const int); int keepNvalues() const; void setKeepLastValues(const bool); bool keepLastValues() const; void setFileWatched(const bool); bool isFileWatched() const; void setFileLinked(const bool); bool isFileLinked() const; void setFileName(const QString&); QString fileName() const; + void setLocalSocketName(const QString&); + QString localSocketName() const; + void updateNow(); void stopReading(); void pauseReading(); void continueReading(); void setFilter(AbstractFileFilter*); AbstractFileFilter* filter() const; virtual QIcon icon() const; virtual QMenu* createContextMenu(); virtual QWidget* view() const; virtual void save(QXmlStreamWriter*) const; virtual bool load(XmlStreamReader*); private: void initActions(); void watch(); QString m_fileName; QString m_serialPortName; + QString m_localSocketName; QString m_host; FileType m_fileType; UpdateType m_updateType; SourceType m_sourceType; ReadingType m_readingType; bool m_fileWatched; bool m_fileLinked; bool m_paused; bool m_prepared; bool m_keepLastValues; int m_sampleRate; int m_keepNvalues; int m_updateInterval; int m_port; int m_baudRate; qint64 m_bytesRead; AbstractFileFilter* m_filter; QTimer* m_updateTimer; QFileSystemWatcher* m_fileSystemWatcher; QFile* m_file; QLocalSocket* m_localSocket; QTcpSocket* m_tcpSocket; QSerialPort* m_serialPort; QIODevice* m_device; QAction* m_reloadAction; QAction* m_toggleLinkAction; QAction* m_toggleWatchAction; QAction* m_showEditorAction; QAction* m_showSpreadsheetAction; QAction* m_plotDataAction; public slots: void read(); private slots: void watchToggled(); void linkToggled(); void plotData(); void readyRead(); void localSocketError(QLocalSocket::LocalSocketError); void tcpSocketError(QAbstractSocket::SocketError); void serialPortError(QSerialPort::SerialPortError); signals: void dataChanged(); void dataUpdated(); }; #endif diff --git a/src/kdefrontend/datasources/ImportFileWidget.cpp b/src/kdefrontend/datasources/ImportFileWidget.cpp index 1d555390d..b04aa79e3 100644 --- a/src/kdefrontend/datasources/ImportFileWidget.cpp +++ b/src/kdefrontend/datasources/ImportFileWidget.cpp @@ -1,889 +1,928 @@ /*************************************************************************** File : ImportFileWidget.cpp Project : LabPlot Description : import file data widget -------------------------------------------------------------------- Copyright : (C) 2009-2017 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "ImportFileWidget.h" #include "FileInfoDialog.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/datasources/filters/HDFFilter.h" #include "backend/datasources/filters/NetCDFFilter.h" #include "backend/datasources/filters/ImageFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "AsciiOptionsWidget.h" #include "BinaryOptionsWidget.h" #include "HDFOptionsWidget.h" #include "ImageOptionsWidget.h" #include "NetCDFOptionsWidget.h" #include "FITSOptionsWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class ImportFileWidget \brief Widget for importing data from a file. \ingroup kdefrontend */ ImportFileWidget::ImportFileWidget(QWidget* parent, const QString& fileName) : QWidget(parent), m_fileName(fileName), m_fileDataSource(true) { ui.setupUi(this); KUrlCompletion *comp = new KUrlCompletion(); ui.kleFileName->setCompletionObject(comp); ui.cbFileType->addItems(FileDataSource::fileTypes()); QStringList filterItems; filterItems << i18n("Automatic") << i18n("Custom"); ui.cbFilter->addItems(filterItems); // file type specific option widgets QWidget* asciiw = new QWidget(); m_asciiOptionsWidget = std::unique_ptr(new AsciiOptionsWidget(asciiw)); ui.swOptions->insertWidget(FileDataSource::Ascii, asciiw); QWidget* binaryw = new QWidget(); m_binaryOptionsWidget = std::unique_ptr(new BinaryOptionsWidget(binaryw)); ui.swOptions->insertWidget(FileDataSource::Binary, binaryw); QWidget* imagew = new QWidget(); m_imageOptionsWidget = std::unique_ptr(new ImageOptionsWidget(imagew)); ui.swOptions->insertWidget(FileDataSource::Image, imagew); QWidget* hdfw = new QWidget(); m_hdfOptionsWidget = std::unique_ptr(new HDFOptionsWidget(hdfw, this)); ui.swOptions->insertWidget(FileDataSource::HDF, hdfw); QWidget* netcdfw = new QWidget(); m_netcdfOptionsWidget = std::unique_ptr(new NetCDFOptionsWidget(netcdfw, this)); ui.swOptions->insertWidget(FileDataSource::NETCDF, netcdfw); QWidget* fitsw = new QWidget(); m_fitsOptionsWidget = std::unique_ptr(new FITSOptionsWidget(fitsw, this)); ui.swOptions->insertWidget(FileDataSource::FITS, fitsw); // the table widget for preview m_twPreview = new QTableWidget(ui.tePreview); m_twPreview->verticalHeader()->hide(); m_twPreview->setEditTriggers(QTableWidget::NoEditTriggers); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(m_twPreview); ui.tePreview->setLayout(layout); m_twPreview->hide(); // default filter ui.swOptions->setCurrentIndex(FileDataSource::Ascii); #if !defined(HAVE_HDF5) || !defined(HAVE_NETCDF) || !defined(HAVE_FITS) const QStandardItemModel* model = qobject_cast(ui.cbFileType->model()); #endif #ifndef HAVE_HDF5 // disable HDF5 item QStandardItem* item = model->item(FileDataSource::HDF); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_NETCDF // disable NETCDF item QStandardItem* item2 = model->item(FileDataSource::NETCDF); item2->setFlags(item2->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_FITS // disable FITS item QStandardItem* item3 = model->item(FileDataSource::FITS); item3->setFlags(item3->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif ui.lePort->setValidator( new QIntValidator(ui.lePort) ); ui.gbOptions->hide(); ui.gbUpdateOptions->hide(); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); ui.bFileInfo->setIcon( QIcon::fromTheme("help-about") ); ui.bManageFilters->setIcon( QIcon::fromTheme("configure") ); ui.bSaveFilter->setIcon( QIcon::fromTheme("document-save") ); ui.bRefreshPreview->setIcon( QIcon::fromTheme("view-refresh") ); connect( ui.kleFileName, SIGNAL(textChanged(QString)), SLOT(fileNameChanged(QString)) ); connect( ui.bOpen, SIGNAL(clicked()), this, SLOT (selectFile()) ); connect( ui.bFileInfo, SIGNAL(clicked()), this, SLOT (fileInfoDialog()) ); connect( ui.bSaveFilter, SIGNAL(clicked()), this, SLOT (saveFilter()) ); connect( ui.bManageFilters, SIGNAL(clicked()), this, SLOT (manageFilters()) ); connect( ui.cbFileType, SIGNAL(currentIndexChanged(int)), SLOT(fileTypeChanged(int)) ); connect( ui.cbUpdateOn, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypeChanged(int))); connect( ui.cbReadType, SIGNAL(currentIndexChanged(int)), this, SLOT(readingTypeChanged(int))); connect( ui.cbFilter, SIGNAL(activated(int)), SLOT(filterChanged(int)) ); connect( ui.bRefreshPreview, SIGNAL(clicked()), SLOT(refreshPreview()) ); connect( ui.cbSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceTypeChanged(int))); //TODO: implement save/load of user-defined settings later and activate these buttons again ui.bSaveFilter->hide(); ui.bManageFilters->hide(); //defer the loading of settings a bit in order to show the dialog prior to blocking the GUI in refreshPreview() QTimer::singleShot( 100, this, SLOT(loadSettings()) ); } void ImportFileWidget::loadSettings() { //load last used settings QString confName; if (m_fileDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); //settings for data type specific widgets m_asciiOptionsWidget->loadSettings(); m_binaryOptionsWidget->loadSettings(); m_imageOptionsWidget->loadSettings(); //general settings ui.cbFileType->setCurrentIndex(conf.readEntry("Type", 0)); ui.cbFilter->setCurrentIndex(conf.readEntry("Filter", 0)); filterChanged(ui.cbFilter->currentIndex()); // needed if filter is not changed if (m_fileName.isEmpty()) ui.kleFileName->setText(conf.readEntry("LastImportedFile", "")); else ui.kleFileName->setText(m_fileName); } ImportFileWidget::~ImportFileWidget() { // save current settings QString confName; if (m_fileDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); // general settings conf.writeEntry("LastImportedFile", ui.kleFileName->text()); conf.writeEntry("Type", ui.cbFileType->currentIndex()); conf.writeEntry("Filter", ui.cbFilter->currentIndex()); // data type specific settings m_asciiOptionsWidget->saveSettings(); m_binaryOptionsWidget->saveSettings(); m_imageOptionsWidget->saveSettings(); } void ImportFileWidget::hideDataSource() { m_fileDataSource = false; ui.gbUpdateOptions->hide(); ui.chbWatchFile->hide(); ui.chbLinkFile->hide(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.lSourceType->hide(); ui.cbSourceType->hide(); ui.cbUpdateOn->hide(); ui.lUpdateOn->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateInterval->hide(); } void ImportFileWidget::showAsciiHeaderOptions(bool b) { m_asciiOptionsWidget->showAsciiHeaderOptions(b); } void ImportFileWidget::showOptions(bool b) { ui.gbOptions->setVisible(b); if (m_fileDataSource) ui.gbUpdateOptions->setVisible(b); resize(layout()->minimumSize()); } QString ImportFileWidget::fileName() const { if (currentFileType() == FileDataSource::FITS) { QString extensionName = m_fitsOptionsWidget->currentExtensionName(); if (!extensionName.isEmpty()) return ui.kleFileName->text() + QLatin1String("[") + extensionName + QLatin1String("]"); } return ui.kleFileName->text(); } /*! saves the settings to the data source \c source. */ void ImportFileWidget::saveSettings(FileDataSource* source) const { //save the data source information FileDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); FileDataSource::UpdateType updateType = static_cast(ui.cbUpdateOn->currentIndex()); FileDataSource::SourceType sourceType = static_cast(ui.cbSourceType->currentIndex()); FileDataSource::ReadingType readingType = static_cast(ui.cbReadType->currentIndex()); source->setComment( ui.kleFileName->text() ); source->setFileType(fileType); source->setFilter(this->currentFileFilter()); source->setUpdateType(updateType); source->setSourceType(sourceType); source->setReadingType(readingType); if (updateType == FileDataSource::UpdateType::TimeInterval) source->setUpdateInterval(ui.sbUpdateInterval->value()); if (!ui.leKeepLastValues->text().isEmpty()) { source->setKeepLastValues(true); source->setKeepNvalues(ui.leKeepLastValues->text().toInt()); } if (readingType != FileDataSource::ReadingType::TillEnd) source->setSampleRate(ui.sbSampleRate->value()); - if ((sourceType == FileDataSource::SourceType::FileOrPipe) || (sourceType == FileDataSource::SourceType::LocalSocket)) { + switch (sourceType) { + case FileDataSource::SourceType::FileOrPipe: source->setFileName( ui.kleFileName->text() ); source->setFileWatched( ui.chbWatchFile->isChecked() ); source->setFileLinked( ui.chbLinkFile->isChecked() ); - } else if (sourceType == FileDataSource::SourceType::NetworkSocket) { + break; + case FileDataSource::SourceType::LocalSocket: + source->setLocalSocketName(ui.leSocketName->text()); + break; + case FileDataSource::SourceType::NetworkSocket: source->setHost(ui.leHost->text()); source->setPort(ui.lePort->text().toInt()); - } else if (sourceType == FileDataSource::SourceType::SerialPort) { + break; + case FileDataSource::SourceType::SerialPort: source->setBaudRate(ui.cbBaudRate->currentText().toInt()); source->setSerialPort(ui.cbSerialPort->currentText()); + break; + default: + break; } } /*! returns the currently used file type. */ FileDataSource::FileType ImportFileWidget::currentFileType() const { return static_cast(ui.cbFileType->currentIndex()); } /*! returns the currently used filter. */ AbstractFileFilter* ImportFileWidget::currentFileFilter() const { DEBUG("currentFileFilter()"); FileDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); switch (fileType) { case FileDataSource::Ascii: { //TODO std::unique_ptr filter(new AsciiFilter()); AsciiFilter* filter = new AsciiFilter(); if (ui.cbFilter->currentIndex() == 0) //"automatic" filter->setAutoModeEnabled(true); else if (ui.cbFilter->currentIndex() == 1) { //"custom" filter->setAutoModeEnabled(false); m_asciiOptionsWidget->applyFilterSettings(filter); } else filter->loadFilterSettings( ui.cbFilter->currentText() ); //save the data portion to import filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } case FileDataSource::Binary: { BinaryFilter* filter = new BinaryFilter(); if ( ui.cbFilter->currentIndex() == 0 ) //"automatic" filter->setAutoModeEnabled(true); else if ( ui.cbFilter->currentIndex() == 1 ) { //"custom" filter->setAutoModeEnabled(false); m_binaryOptionsWidget->applyFilterSettings(filter); } else { //TODO: load filter settings // filter->setFilterName( ui.cbFilter->currentText() ); } filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); return filter; } case FileDataSource::Image: { ImageFilter* filter = new ImageFilter(); filter->setImportFormat(m_imageOptionsWidget->currentFormat()); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case FileDataSource::HDF: { HDFFilter* filter = new HDFFilter(); QStringList names = selectedHDFNames(); if (!names.isEmpty()) filter->setCurrentDataSetName(names[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case FileDataSource::NETCDF: { NetCDFFilter* filter = new NetCDFFilter(); if (!selectedNetCDFNames().isEmpty()) filter->setCurrentVarName(selectedNetCDFNames()[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case FileDataSource::FITS: { FITSFilter* filter = new FITSFilter(); filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } } return 0; } /*! opens a file dialog and lets the user select the file data source. */ void ImportFileWidget::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileWidget"); QString dir = conf.readEntry("LastDir", ""); QString path = QFileDialog::getOpenFileName(this, i18n("Select the File Data Source"), dir); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } ui.kleFileName->setText(path); //TODO: decide whether the selection of several files should be possible // QStringList filelist = QFileDialog::getOpenFileNames(this,i18n("Select one or more files to open")); // if (! filelist.isEmpty() ) // ui.kleFileName->setText(filelist.join(";")); } /************** SLOTS **************************************************************/ /*! called on file name changes. Determines the file format (ASCII, binary etc.), if the file exists, and activates the corresponding options. */ void ImportFileWidget::fileNameChanged(const QString& name) { QString fileName = name; #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.left(1) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif bool fileExists = QFile::exists(fileName); if (fileExists) ui.kleFileName->setStyleSheet(""); else ui.kleFileName->setStyleSheet("QLineEdit{background:red;}"); ui.gbOptions->setEnabled(fileExists); ui.bFileInfo->setEnabled(fileExists); ui.cbFileType->setEnabled(fileExists); ui.cbFilter->setEnabled(fileExists); ui.bManageFilters->setEnabled(fileExists); ui.chbWatchFile->setEnabled(fileExists); ui.chbLinkFile->setEnabled(fileExists); if (!fileExists) { //file doesn't exist -> delete the content preview that is still potentially //available from the previously selected file ui.tePreview->clear(); m_twPreview->clear(); m_hdfOptionsWidget->clear(); m_netcdfOptionsWidget->clear(); m_fitsOptionsWidget->clear(); emit fileNameChanged(); return; } QString fileInfo; #ifndef HAVE_WINDOWS //check, if we can guess the file type by content QProcess *proc = new QProcess(this); QStringList args; args << "-b" << ui.kleFileName->text(); proc->start("file", args); if (proc->waitForReadyRead(1000) == false) { QDEBUG("ERROR: reading file type of file" << fileName); return; } fileInfo = proc->readLine(); #endif QByteArray imageFormat = QImageReader::imageFormat(fileName); if (fileInfo.contains(QLatin1String("compressed data")) || fileInfo.contains(QLatin1String("ASCII")) || fileName.endsWith(QLatin1String("dat"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("txt"), Qt::CaseInsensitive)) { //probably ascii data ui.cbFileType->setCurrentIndex(FileDataSource::Ascii); } else if (fileInfo.contains(QLatin1String("Hierarchical Data Format")) || fileName.endsWith(QLatin1String("h5"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf5"), Qt::CaseInsensitive) ) { ui.cbFileType->setCurrentIndex(FileDataSource::HDF); // update HDF tree widget using current selected file m_hdfOptionsWidget->updateContent((HDFFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("NetCDF Data Format")) || fileName.endsWith(QLatin1String("nc"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("netcdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("cdf"), Qt::CaseInsensitive)) { ui.cbFileType->setCurrentIndex(FileDataSource::NETCDF); // update NetCDF tree widget using current selected file m_netcdfOptionsWidget->updateContent((NetCDFFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("FITS image data")) || fileName.endsWith(QLatin1String("fits"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fit"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fts"), Qt::CaseInsensitive)) { #ifdef HAVE_FITS ui.cbFileType->setCurrentIndex(FileDataSource::FITS); #endif // update FITS tree widget using current selected file m_fitsOptionsWidget->updateContent((FITSFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains("image") || fileInfo.contains("bitmap") || !imageFormat.isEmpty()) ui.cbFileType->setCurrentIndex(FileDataSource::Image); else ui.cbFileType->setCurrentIndex(FileDataSource::Binary); refreshPreview(); emit fileNameChanged(); } /*! saves the current filter settings */ void ImportFileWidget::saveFilter() { bool ok; QString text = QInputDialog::getText(this, i18n("Save Filter Settings as"), i18n("Filter name:"), QLineEdit::Normal, i18n("new filter"), &ok); if (ok && !text.isEmpty()) { //TODO //AsciiFilter::saveFilter() } } /*! opens a dialog for managing all available predefined filters. */ void ImportFileWidget::manageFilters() { //TODO } /*! Depending on the selected file type, activates the corresponding options in the data portion tab and populates the combobox with the available pre-defined fllter settings for the selected type. */ void ImportFileWidget::fileTypeChanged(int fileType) { ui.swOptions->setCurrentIndex(fileType); //default ui.lFilter->show(); ui.cbFilter->show(); //if we switch from netCDF-format (only two tabs available), add the data preview-tab again if (ui.tabWidget->count() == 2) { ui.tabWidget->setTabText(0, i18n("Data format")); ui.tabWidget->insertTab(1, ui.tabDataPreview, i18n("Preview")); } ui.lPreviewLines->show(); ui.sbPreviewLines->show(); ui.lStartColumn->show(); ui.sbStartColumn->show(); ui.lEndColumn->show(); ui.sbEndColumn->show(); switch (fileType) { case FileDataSource::Ascii: break; case FileDataSource::Binary: ui.lStartColumn->hide(); ui.sbStartColumn->hide(); ui.lEndColumn->hide(); ui.sbEndColumn->hide(); break; case FileDataSource::HDF: case FileDataSource::NETCDF: ui.lFilter->hide(); ui.cbFilter->hide(); // hide global preview tab. we have our own ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; case FileDataSource::Image: ui.lPreviewLines->hide(); ui.sbPreviewLines->hide(); ui.lFilter->hide(); ui.cbFilter->hide(); break; case FileDataSource::FITS: ui.lFilter->hide(); ui.cbFilter->hide(); ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; default: DEBUG("unknown file type"); } m_hdfOptionsWidget->clear(); m_netcdfOptionsWidget->clear(); int lastUsedFilterIndex = ui.cbFilter->currentIndex(); ui.cbFilter->clear(); ui.cbFilter->addItem( i18n("Automatic") ); ui.cbFilter->addItem( i18n("Custom") ); //TODO: populate the combobox with the available pre-defined filter settings for the selected type ui.cbFilter->setCurrentIndex(lastUsedFilterIndex); filterChanged(lastUsedFilterIndex); refreshPreview(); } const QStringList ImportFileWidget::selectedHDFNames() const { return m_hdfOptionsWidget->selectedHDFNames(); } const QStringList ImportFileWidget::selectedNetCDFNames() const { return m_netcdfOptionsWidget->selectedNetCDFNames(); } const QStringList ImportFileWidget::selectedFITSExtensions() const { return m_fitsOptionsWidget->selectedFITSExtensions(); } /*! shows the dialog with the information about the file(s) to be imported. */ void ImportFileWidget::fileInfoDialog() { QStringList files = ui.kleFileName->text().split(';'); FileInfoDialog* dlg = new FileInfoDialog(this); dlg->setFiles(files); dlg->exec(); } /*! enables the options if the filter "custom" was chosen. Disables the options otherwise. */ void ImportFileWidget::filterChanged(int index) { // ignore filter for these formats if (ui.cbFileType->currentIndex() == FileDataSource::HDF || ui.cbFileType->currentIndex() == FileDataSource::NETCDF || ui.cbFileType->currentIndex() == FileDataSource::Image || ui.cbFileType->currentIndex() == FileDataSource::FITS) { ui.swOptions->setEnabled(true); return; } if (index == 0) { // "automatic" ui.swOptions->setEnabled(false); ui.bSaveFilter->setEnabled(false); } else if (index == 1) { //custom ui.swOptions->setEnabled(true); ui.bSaveFilter->setEnabled(true); } else { // predefined filter settings were selected. //load and show them in the GUI. //TODO } } void ImportFileWidget::refreshPreview() { DEBUG("refreshPreview()"); WAIT_CURSOR; QString fileName = ui.kleFileName->text(); #ifndef HAVE_WINDOWS if (fileName.left(1) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif QVector importedStrings; FileDataSource::FileType fileType = (FileDataSource::FileType)ui.cbFileType->currentIndex(); // generic table widget if (fileType == FileDataSource::Ascii || fileType == FileDataSource::Binary) m_twPreview->show(); else m_twPreview->hide(); int lines = ui.sbPreviewLines->value(); bool ok = true; QTableWidget *tmpTableWidget = nullptr; QStringList vectorNameList; QVector columnModes; switch (fileType) { case FileDataSource::Ascii: { ui.tePreview->clear(); AsciiFilter *filter = (AsciiFilter *)this->currentFileFilter(); importedStrings = filter->preview(fileName, lines); tmpTableWidget = m_twPreview; vectorNameList = filter->vectorNames(); columnModes = filter->columnModes(); break; } case FileDataSource::Binary: { ui.tePreview->clear(); BinaryFilter *filter = (BinaryFilter *)this->currentFileFilter(); importedStrings = filter->readDataFromFile(fileName, nullptr, AbstractFileFilter::Replace, lines); tmpTableWidget = m_twPreview; break; } case FileDataSource::Image: { ui.tePreview->clear(); QImage image(fileName); QTextCursor cursor = ui.tePreview->textCursor(); cursor.insertImage(image); RESET_CURSOR; return; } case FileDataSource::HDF: { HDFFilter *filter = (HDFFilter *)this->currentFileFilter(); lines = m_hdfOptionsWidget->lines(); importedStrings = filter->readCurrentDataSet(fileName, NULL, ok, AbstractFileFilter::Replace, lines); tmpTableWidget = m_hdfOptionsWidget->previewWidget(); break; } case FileDataSource::NETCDF: { NetCDFFilter *filter = (NetCDFFilter *)this->currentFileFilter(); lines = m_netcdfOptionsWidget->lines(); importedStrings = filter->readCurrentVar(fileName, NULL, AbstractFileFilter::Replace, lines); tmpTableWidget = m_netcdfOptionsWidget->previewWidget(); break; } case FileDataSource::FITS: { FITSFilter* filter = (FITSFilter*)this->currentFileFilter(); lines = m_fitsOptionsWidget->lines(); QString extensionName = m_fitsOptionsWidget->extensionName(&ok); if (!extensionName.isEmpty()) fileName = extensionName; bool readFitsTableToMatrix; importedStrings = filter->readChdu(fileName, &readFitsTableToMatrix, lines); emit checkedFitsTableToMatrix(readFitsTableToMatrix); tmpTableWidget = m_fitsOptionsWidget->previewWidget(); break; } } // fill the table widget tmpTableWidget->setRowCount(0); tmpTableWidget->setColumnCount(0); if( !importedStrings.isEmpty() ) { QDEBUG("importedStrings =" << importedStrings); if (!ok) { // show imported strings as error message tmpTableWidget->setRowCount(1); tmpTableWidget->setColumnCount(1); QTableWidgetItem* item = new QTableWidgetItem(); item->setText(importedStrings[0][0]); tmpTableWidget->setItem(0, 0, item); } else { //TODO: maxrows not used const int rows = qMax(importedStrings.size(), 1); const int maxColumns = 300; tmpTableWidget->setRowCount(rows); for (int i = 0; i < rows; i++) { QDEBUG(importedStrings[i]); int cols = importedStrings[i].size() > maxColumns ? maxColumns : importedStrings[i].size(); // new if (cols > tmpTableWidget->columnCount()) tmpTableWidget->setColumnCount(cols); for (int j = 0; j < cols; j++) { QTableWidgetItem* item = new QTableWidgetItem(importedStrings[i][j]); tmpTableWidget->setItem(i, j, item); } } // set header if columnMode available for (int i = 0; i < qMin(tmpTableWidget->columnCount(), columnModes.size()); i++) { QString columnName = QString::number(i+1); if (i < vectorNameList.size()) columnName = vectorNameList[i]; auto* item = new QTableWidgetItem(columnName + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes[i]) + QLatin1String("}")); item->setTextAlignment(Qt::AlignLeft); item->setIcon(AbstractColumn::iconForMode(columnModes[i])); tmpTableWidget->setHorizontalHeaderItem(i, item); } } tmpTableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); } RESET_CURSOR; } void ImportFileWidget::updateTypeChanged(int idx) { FileDataSource::UpdateType type = static_cast(idx); if (type == FileDataSource::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); ui.lUpdateIntervalUnit->show(); } else if (type == FileDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateIntervalUnit->hide(); } } void ImportFileWidget::readingTypeChanged(int idx) { FileDataSource::ReadingType type = static_cast(idx); if (type == FileDataSource::ReadingType::TillEnd) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else { ui.lSampleRate->show(); ui.sbSampleRate->show(); } } void ImportFileWidget::sourceTypeChanged(int idx) { FileDataSource::SourceType type = static_cast(idx); - if ((type == FileDataSource::SourceType::FileOrPipe) || (type == FileDataSource::SourceType::LocalSocket)) { + switch (type) { + case FileDataSource::SourceType::FileOrPipe: ui.lFileName->show(); ui.kleFileName->show(); - ui.bFileInfo->setVisible(type == FileDataSource::SourceType::FileOrPipe); + ui.bFileInfo->show(); ui.bOpen->show(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); - } else if (type == FileDataSource::SourceType::NetworkSocket) { + break; + case FileDataSource::SourceType::LocalSocket: + ui.leSocketName->show(); + ui.lSocketName->show(); + + ui.lFileName->hide(); + ui.kleFileName->hide(); + ui.bOpen->hide(); + ui.bFileInfo->hide(); + ui.cbBaudRate->hide(); + ui.lBaudRate->hide(); + ui.lHost->hide(); + ui.leHost->hide(); + ui.lPort->hide(); + ui.lePort->hide(); + ui.cbSerialPort->hide(); + ui.lSerialPort->hide(); + break; + case FileDataSource::SourceType::NetworkSocket: ui.lHost->show(); ui.leHost->show(); ui.lePort->show(); ui.lPort->show(); ui.lBaudRate->hide(); ui.cbBaudRate->hide(); ui.lSerialPort->hide(); ui.cbSerialPort->hide(); + ui.lSocketName->hide(); + ui.leSocketName->hide(); ui.lFileName->hide(); ui.kleFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); - } else if (type == FileDataSource::SourceType::SerialPort) { + break; + case FileDataSource::SourceType::SerialPort: ui.lBaudRate->show(); ui.cbBaudRate->show(); ui.lSerialPort->show(); ui.cbSerialPort->show(); ui.lHost->hide(); ui.leHost->hide(); ui.lePort->hide(); ui.lPort->hide(); ui.lFileName->hide(); ui.kleFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); + ui.lSocketName->hide(); + ui.leSocketName->hide(); + break; + default: + break; } } void ImportFileWidget::initializeAndFillPortsAndBaudRates() { for (int i = 2; i < ui.swOptions->count(); ++i) ui.swOptions->removeWidget(ui.swOptions->widget(i)); const int size = ui.cbFileType->count(); for (int i = 2; i < size; ++i) ui.cbFileType->removeItem(2); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); + ui.lSocketName->hide(); + ui.leSocketName->hide();; + ui.cbBaudRate->addItems(FileDataSource::supportedBaudRates()); ui.cbSerialPort->addItems(FileDataSource::availablePorts()); ui.leKeepLastValues->setValidator(new QIntValidator(2, 100000)); ui.tabWidget->removeTab(2); } diff --git a/src/kdefrontend/ui/datasources/importfilewidget.ui b/src/kdefrontend/ui/datasources/importfilewidget.ui index e89a28a48..753607a36 100644 --- a/src/kdefrontend/ui/datasources/importfilewidget.ui +++ b/src/kdefrontend/ui/datasources/importfilewidget.ui @@ -1,699 +1,707 @@ ImportFileWidget 0 0 679 1219 0 0 Data source Name Source 0 0 Select the file to import false 0 0 Show file info - + Port - + Port - + Baud rate - + Qt::Vertical QSizePolicy::Fixed 20 13 - + Type - + Filter - + false 0 0 - + false Save the current filter settings - + false 0 0 Manage filters - + Qt::Vertical QSizePolicy::Fixed 20 10 Specify the name of the file to import. true - + Host + + + File or named pipe Network socket Local socket Serial port - - - - + - + - + - + false + + + + Socket name + + + + + + false 0 0 Format Options 0 0 0 0 0 Data format - - gbUpdateOptions - + Preview 0 0 Number of rows to preview: 1 10000 100 Qt::Horizontal 23 20 Refresh true QTextEdit::NoWrap true Data portion to read Qt::Vertical 20 172 Start row: Specify the start row for import 1 2147483647 1 Qt::Horizontal QSizePolicy::Fixed 40 20 Qt::Horizontal 108 20 End row: Specify the end row to import; -1 stands for the last row -1 2147483647 -1 Start column: Specify the start column for import 1 2147483647 Qt::Horizontal QSizePolicy::Fixed 40 20 End column: Specify the end column to import; -1 stands for the last column -1 2147483647 -1 Qt::Horizontal 108 20 Update Options If this option is checked, the file will be automatically reloaded on changes. Watch the file true 0 0 Periodically On new data If this option is checked, only the link to the file is stored in the project file but not it's content. Link the file true Read Keep last N values Update Sample rate 1 10000 5 Continously fixed From end Till the end Update interval 0 0 5 60000 1000 ms KComboBox QWidget
kcombobox.h
KLineEdit QLineEdit
klineedit.h