dataload_csv.cpp
Go to the documentation of this file.
1 #include "dataload_csv.h"
2 #include <QTextStream>
3 #include <QFile>
4 #include <QMessageBox>
5 #include <QDebug>
6 #include <QSettings>
7 #include <QProgressDialog>
8 #include "selectlistdialog.h"
9 
11 {
12  _extensions.push_back("csv");
13 }
14 
15 const QRegExp csv_separator("(\\,|\\;|\\|)");
16 
17 const std::vector<const char*>& DataLoadCSV::compatibleFileExtensions() const
18 {
19  return _extensions;
20 }
21 
22 QSize DataLoadCSV::parseHeader(QFile* file, std::vector<std::string>& ordered_names)
23 {
24  QTextStream inA(file);
25 
26  QString first_line = inA.readLine();
27  QString second_line = inA.readLine();
28 
29  QStringList firstline_items = first_line.split(csv_separator);
30 
31  int linecount = 0;
32  const int columncount = firstline_items.count();
33 
34  for (int i = 0; i < firstline_items.size(); i++)
35  {
36  // remove annoying prefix
37  QString field_name(firstline_items[i]);
38 
39  if (field_name.isEmpty())
40  {
41  field_name = QString("_Column_%1").arg(i);
42  }
43  ordered_names.push_back(field_name.toStdString());
44  }
45 
46  while (!inA.atEnd())
47  {
48  inA.readLine();
49  linecount++;
50  }
51 
52  QSize table_size;
53  table_size.setWidth(columncount);
54  table_size.setHeight(linecount);
55  return table_size;
56 }
57 
59 {
60  bool use_provided_configuration = false;
61 
62  if (info->plugin_config.hasChildNodes())
63  {
64  use_provided_configuration = true;
65  xmlLoadState(info->plugin_config.firstChildElement());
66  }
67 
68  const int TIME_INDEX_NOT_DEFINED = -2;
69 
70  int time_index = TIME_INDEX_NOT_DEFINED;
71 
72  QFile file(info->filename);
73  file.open(QFile::ReadOnly);
74 
75  std::vector<std::string> column_names;
76 
77  const QSize table_size = parseHeader(&file, column_names);
78  const int tot_lines = table_size.height() - 1;
79  const int columncount = table_size.width();
80 
81  file.close();
82  file.open(QFile::ReadOnly);
83  QTextStream inB(&file);
84 
85  std::vector<PlotData*> plots_vector;
86 
87  bool interrupted = false;
88 
89  int linecount = 0;
90 
91  QProgressDialog progress_dialog;
92  progress_dialog.setLabelText("Loading... please wait");
93  progress_dialog.setWindowModality(Qt::ApplicationModal);
94  progress_dialog.setRange(0, tot_lines - 1);
95  progress_dialog.setAutoClose(true);
96  progress_dialog.setAutoReset(true);
97  progress_dialog.show();
98 
99  // remove first line (header)
100  inB.readLine();
101 
102  //---- build plots_vector from header ------
103  std::deque<std::string> valid_field_names;
104 
105  for (unsigned i = 0; i < column_names.size(); i++)
106  {
107  const std::string& field_name = (column_names[i]);
108 
109  auto it = plot_data.addNumeric(field_name);
110 
111  valid_field_names.push_back(field_name);
112  plots_vector.push_back(&(it->second));
113 
114  if (time_index == TIME_INDEX_NOT_DEFINED && use_provided_configuration)
115  {
116  if (_default_time_axis == field_name)
117  {
118  time_index = i;
119  }
120  }
121  }
122 
123  if (time_index == TIME_INDEX_NOT_DEFINED && !use_provided_configuration)
124  {
125  valid_field_names.push_front("INDEX (auto-generated)");
126 
127  SelectFromListDialog* dialog = new SelectFromListDialog(valid_field_names);
128  dialog->setWindowTitle("Select the time axis");
129  int res = dialog->exec();
130 
131  if (res == QDialog::Rejected)
132  {
133  return false;
134  }
135 
136  const int selected_item = dialog->getSelectedRowNumber().at(0);
137  if (selected_item > 0)
138  {
139  for (unsigned i = 0; i < column_names.size(); i++)
140  {
141  if (column_names[i] == valid_field_names[selected_item])
142  {
143  _default_time_axis = column_names[i];
144  time_index = selected_item - 1;
145  break;
146  }
147  }
148  }
149  }
150 
151  //-----------------
152  double prev_time = -std::numeric_limits<double>::max();
153  bool monotonic_warning = false;
154 
155  while (!inB.atEnd())
156  {
157  QString line = inB.readLine();
158 
159  QStringList string_items = line.split(csv_separator);
160  if (string_items.size() != columncount)
161  {
162  continue;
163  }
164  double t = linecount;
165 
166  if (time_index >= 0)
167  {
168  bool is_number = false;
169  t = string_items[time_index].toDouble(&is_number);
170 
171  if (!is_number)
172  {
173  QMessageBox::StandardButton reply;
174  reply = QMessageBox::warning(0, tr("Error reading file"),
175  tr("One of the timestamps is not a valid number. Abort\n"));
176 
177  return false;
178  }
179  if (t < prev_time)
180  {
181  QMessageBox::StandardButton reply;
182  reply = QMessageBox::warning(0, tr("Error reading file"),
183  tr("Selected time in not strictly monotonic. Loading will be aborted\n"));
184 
185  return false;
186  }
187  else if (t == prev_time)
188  {
189  monotonic_warning = true;
190  }
191 
192  prev_time = t;
193  }
194 
195  int index = 0;
196  for (int i = 0; i < string_items.size(); i++)
197  {
198  bool is_number = false;
199  double y = string_items[i].toDouble(&is_number);
200  if (is_number)
201  {
202  PlotData::Point point(t, y);
203  plots_vector[index]->pushBack(point);
204  }
205  index++;
206  }
207 
208  if (linecount++ % 100 == 0)
209  {
210  progress_dialog.setValue(linecount);
211  QApplication::processEvents();
212  if (progress_dialog.wasCanceled())
213  {
214  interrupted = true;
215  break;
216  }
217  }
218  }
219  file.close();
220 
221  if (interrupted)
222  {
223  progress_dialog.cancel();
224  plot_data.numeric.clear();
225  }
226 
227  if (monotonic_warning)
228  {
229  QString message = "Two consecutive samples had the same X value (i.e. time).\n"
230  "Since PlotJuggler makes the assumption that timeseries are strictly monotonic, you "
231  "might experience undefined behaviours.\n\n"
232  "You have been warned...";
233  QMessageBox::warning(0, tr("Warning"), message);
234  }
235 
236  return true;
237 }
238 
240 {
241 }
242 
243 bool DataLoadCSV::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
244 {
245  QDomElement elem = doc.createElement("default");
246  elem.setAttribute("time_axis", _default_time_axis.c_str());
247 
248  parent_element.appendChild(elem);
249  return true;
250 }
251 
252 bool DataLoadCSV::xmlLoadState(const QDomElement& parent_element)
253 {
254  QDomElement elem = parent_element.firstChildElement("default");
255  if (!elem.isNull())
256  {
257  if (elem.hasAttribute("time_axis"))
258  {
259  _default_time_axis = elem.attribute("time_axis").toStdString();
260  return true;
261  }
262  }
263  return false;
264 }
virtual bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const override
const QRegExp csv_separator("(\\,|\\;|\\|)")
virtual bool readDataFromFile(PJ::FileLoadInfo *fileload_info, PlotDataMapRef &destination) override
std::vector< const char * > _extensions
Definition: dataload_csv.h:37
std::unordered_map< std::string, PlotData > numeric
Definition: plotdata.h:495
virtual ~DataLoadCSV()
#define max(A, B)
Definition: Socket.h:88
std::vector< int > getSelectedRowNumber() const
QSize parseHeader(QFile *file, std::vector< std::string > &ordered_names)
virtual bool xmlLoadState(const QDomElement &parent_element) override
std::unordered_map< std::string, PlotData >::iterator addNumeric(const std::string &name)
Definition: plotdata.h:498
std::string _default_time_axis
Definition: dataload_csv.h:39
QDomDocument plugin_config
virtual const std::vector< const char * > & compatibleFileExtensions() const override
typename PlotDataBase< Value >::Point Point
Definition: plotdata.h:290


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:47:33