annotate data/fileio/CSVFileReader.cpp @ 167:665342c6ec57

* Add a bit of resistance to pane dragging so as to make it harder to inadvertently drag in the other axis from the one you intended
author Chris Cannam
date Fri, 22 Sep 2006 16:46:10 +0000
parents 21792a550ec9
children f75f8a1cd7b1
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "CSVFileReader.h"
Chris@148 17
Chris@150 18 #include "model/Model.h"
Chris@148 19 #include "base/RealTime.h"
Chris@148 20 #include "model/SparseOneDimensionalModel.h"
Chris@148 21 #include "model/SparseTimeValueModel.h"
Chris@152 22 #include "model/EditableDenseThreeDimensionalModel.h"
Chris@148 23
Chris@148 24 #include <QFile>
Chris@148 25 #include <QString>
Chris@148 26 #include <QRegExp>
Chris@148 27 #include <QStringList>
Chris@148 28 #include <QTextStream>
Chris@148 29 #include <QFrame>
Chris@148 30 #include <QGridLayout>
Chris@148 31 #include <QPushButton>
Chris@148 32 #include <QHBoxLayout>
Chris@148 33 #include <QVBoxLayout>
Chris@148 34 #include <QTableWidget>
Chris@148 35 #include <QComboBox>
Chris@148 36 #include <QLabel>
Chris@148 37
Chris@148 38 #include <iostream>
Chris@148 39
Chris@148 40 CSVFileReader::CSVFileReader(QString path, size_t mainModelSampleRate) :
Chris@148 41 m_file(0),
Chris@148 42 m_mainModelSampleRate(mainModelSampleRate)
Chris@148 43 {
Chris@148 44 m_file = new QFile(path);
Chris@148 45 bool good = false;
Chris@148 46
Chris@148 47 if (!m_file->exists()) {
Chris@148 48 m_error = QFile::tr("File \"%1\" does not exist").arg(path);
Chris@148 49 } else if (!m_file->open(QIODevice::ReadOnly | QIODevice::Text)) {
Chris@148 50 m_error = QFile::tr("Failed to open file \"%1\"").arg(path);
Chris@148 51 } else {
Chris@148 52 good = true;
Chris@148 53 }
Chris@148 54
Chris@148 55 if (!good) {
Chris@148 56 delete m_file;
Chris@148 57 m_file = 0;
Chris@148 58 }
Chris@148 59 }
Chris@148 60
Chris@148 61 CSVFileReader::~CSVFileReader()
Chris@148 62 {
Chris@148 63 std::cerr << "CSVFileReader::~CSVFileReader: file is " << m_file << std::endl;
Chris@148 64
Chris@148 65 if (m_file) {
Chris@148 66 std::cerr << "CSVFileReader::CSVFileReader: Closing file" << std::endl;
Chris@148 67 m_file->close();
Chris@148 68 }
Chris@148 69 delete m_file;
Chris@148 70 }
Chris@148 71
Chris@148 72 bool
Chris@148 73 CSVFileReader::isOK() const
Chris@148 74 {
Chris@148 75 return (m_file != 0);
Chris@148 76 }
Chris@148 77
Chris@148 78 QString
Chris@148 79 CSVFileReader::getError() const
Chris@148 80 {
Chris@148 81 return m_error;
Chris@148 82 }
Chris@148 83
Chris@148 84 Model *
Chris@148 85 CSVFileReader::load() const
Chris@148 86 {
Chris@148 87 if (!m_file) return 0;
Chris@148 88
Chris@148 89 CSVFormatDialog *dialog = new CSVFormatDialog
Chris@148 90 (0, m_file, m_mainModelSampleRate);
Chris@148 91
Chris@148 92 if (dialog->exec() == QDialog::Rejected) {
Chris@148 93 delete dialog;
Chris@148 94 return 0;
Chris@148 95 }
Chris@148 96
Chris@148 97 CSVFormatDialog::ModelType modelType = dialog->getModelType();
Chris@148 98 CSVFormatDialog::TimingType timingType = dialog->getTimingType();
Chris@148 99 CSVFormatDialog::TimeUnits timeUnits = dialog->getTimeUnits();
Chris@148 100 QString separator = dialog->getSeparator();
Chris@148 101 size_t sampleRate = dialog->getSampleRate();
Chris@148 102 size_t windowSize = dialog->getWindowSize();
Chris@148 103
Chris@148 104 delete dialog;
Chris@148 105
Chris@148 106 if (timingType == CSVFormatDialog::ExplicitTiming) {
Chris@148 107 windowSize = 1;
Chris@148 108 if (timeUnits == CSVFormatDialog::TimeSeconds) {
Chris@148 109 sampleRate = m_mainModelSampleRate;
Chris@148 110 }
Chris@148 111 }
Chris@148 112
Chris@148 113 SparseOneDimensionalModel *model1 = 0;
Chris@148 114 SparseTimeValueModel *model2 = 0;
Chris@152 115 EditableDenseThreeDimensionalModel *model3 = 0;
Chris@148 116 Model *model = 0;
Chris@148 117
Chris@148 118 QTextStream in(m_file);
Chris@148 119 in.seek(0);
Chris@148 120
Chris@148 121 unsigned int warnings = 0, warnLimit = 10;
Chris@148 122 unsigned int lineno = 0;
Chris@148 123
Chris@148 124 float min = 0.0, max = 0.0;
Chris@148 125
Chris@148 126 size_t frameNo = 0;
Chris@148 127
Chris@148 128 while (!in.atEnd()) {
Chris@148 129
Chris@148 130 QString line = in.readLine().trimmed();
Chris@148 131 if (line.startsWith("#")) continue;
Chris@148 132
Chris@148 133 QStringList list = line.split(separator);
Chris@148 134
Chris@148 135 if (!model) {
Chris@148 136
Chris@148 137 switch (modelType) {
Chris@148 138
Chris@148 139 case CSVFormatDialog::OneDimensionalModel:
Chris@148 140 model1 = new SparseOneDimensionalModel(sampleRate, windowSize);
Chris@148 141 model = model1;
Chris@148 142 break;
Chris@148 143
Chris@148 144 case CSVFormatDialog::TwoDimensionalModel:
Chris@148 145 model2 = new SparseTimeValueModel(sampleRate, windowSize,
Chris@148 146 0.0, 0.0,
Chris@148 147 false);
Chris@148 148 model = model2;
Chris@148 149 break;
Chris@148 150
Chris@148 151 case CSVFormatDialog::ThreeDimensionalModel:
Chris@152 152 model3 = new EditableDenseThreeDimensionalModel(sampleRate,
Chris@152 153 windowSize,
Chris@152 154 list.size());
Chris@148 155 model = model3;
Chris@148 156 break;
Chris@148 157 }
Chris@148 158 }
Chris@148 159
Chris@148 160 QStringList tidyList;
Chris@148 161 QRegExp nonNumericRx("[^0-9.,+-]");
Chris@148 162
Chris@148 163 for (int i = 0; i < list.size(); ++i) {
Chris@148 164
Chris@148 165 QString s(list[i].trimmed());
Chris@148 166
Chris@148 167 if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
Chris@148 168 s = s.mid(1, s.length() - 2);
Chris@148 169 } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
Chris@148 170 s = s.mid(1, s.length() - 2);
Chris@148 171 }
Chris@148 172
Chris@148 173 if (i == 0 && timingType == CSVFormatDialog::ExplicitTiming) {
Chris@148 174
Chris@148 175 bool ok = false;
Chris@148 176 QString numeric = s;
Chris@148 177 numeric.remove(nonNumericRx);
Chris@148 178
Chris@148 179 if (timeUnits == CSVFormatDialog::TimeSeconds) {
Chris@148 180
Chris@148 181 double time = numeric.toDouble(&ok);
Chris@148 182 frameNo = int(time * sampleRate + 0.00001);
Chris@148 183
Chris@148 184 } else {
Chris@148 185
Chris@148 186 frameNo = numeric.toInt(&ok);
Chris@148 187
Chris@148 188 if (timeUnits == CSVFormatDialog::TimeWindows) {
Chris@148 189 frameNo *= windowSize;
Chris@148 190 }
Chris@148 191 }
Chris@148 192
Chris@148 193 if (!ok) {
Chris@148 194 if (warnings < warnLimit) {
Chris@148 195 std::cerr << "WARNING: CSVFileReader::load: "
Chris@148 196 << "Bad time format (\"" << s.toStdString()
Chris@148 197 << "\") in data line "
Chris@148 198 << lineno << ":" << std::endl;
Chris@148 199 std::cerr << line.toStdString() << std::endl;
Chris@148 200 } else if (warnings == warnLimit) {
Chris@148 201 std::cerr << "WARNING: Too many warnings" << std::endl;
Chris@148 202 }
Chris@148 203 ++warnings;
Chris@148 204 }
Chris@148 205 } else {
Chris@148 206 tidyList.push_back(s);
Chris@148 207 }
Chris@148 208 }
Chris@148 209
Chris@148 210 if (modelType == CSVFormatDialog::OneDimensionalModel) {
Chris@148 211
Chris@148 212 SparseOneDimensionalModel::Point point
Chris@148 213 (frameNo,
Chris@148 214 tidyList.size() > 0 ? tidyList[tidyList.size()-1] :
Chris@148 215 QString("%1").arg(lineno));
Chris@148 216
Chris@148 217 model1->addPoint(point);
Chris@148 218
Chris@148 219 } else if (modelType == CSVFormatDialog::TwoDimensionalModel) {
Chris@148 220
Chris@148 221 SparseTimeValueModel::Point point
Chris@148 222 (frameNo,
Chris@148 223 tidyList.size() > 0 ? tidyList[0].toFloat() : 0.0,
Chris@148 224 tidyList.size() > 1 ? tidyList[1] : QString("%1").arg(lineno));
Chris@148 225
Chris@148 226 model2->addPoint(point);
Chris@148 227
Chris@148 228 } else if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
Chris@148 229
Chris@148 230 DenseThreeDimensionalModel::BinValueSet values;
Chris@148 231
Chris@148 232 for (int i = 0; i < tidyList.size(); ++i) {
Chris@148 233
Chris@148 234 bool ok = false;
Chris@148 235 float value = list[i].toFloat(&ok);
Chris@148 236 values.push_back(value);
Chris@148 237
Chris@148 238 if ((lineno == 0 && i == 0) || value < min) min = value;
Chris@148 239 if ((lineno == 0 && i == 0) || value > max) max = value;
Chris@148 240
Chris@148 241 if (!ok) {
Chris@148 242 if (warnings < warnLimit) {
Chris@148 243 std::cerr << "WARNING: CSVFileReader::load: "
Chris@148 244 << "Non-numeric value in data line " << lineno
Chris@148 245 << ":" << std::endl;
Chris@148 246 std::cerr << line.toStdString() << std::endl;
Chris@148 247 ++warnings;
Chris@148 248 } else if (warnings == warnLimit) {
Chris@148 249 std::cerr << "WARNING: Too many warnings" << std::endl;
Chris@148 250 }
Chris@148 251 }
Chris@148 252 }
Chris@148 253
Chris@148 254 std::cerr << "Setting bin values for count " << lineno << ", frame "
Chris@148 255 << frameNo << ", time " << RealTime::frame2RealTime(frameNo, sampleRate) << std::endl;
Chris@148 256
Chris@148 257 model3->setBinValues(frameNo, values);
Chris@148 258 }
Chris@148 259
Chris@148 260 ++lineno;
Chris@148 261 if (timingType == CSVFormatDialog::ImplicitTiming ||
Chris@148 262 list.size() == 0) {
Chris@148 263 frameNo += windowSize;
Chris@148 264 }
Chris@148 265 }
Chris@148 266
Chris@148 267 if (modelType == CSVFormatDialog::ThreeDimensionalModel) {
Chris@148 268 model3->setMinimumLevel(min);
Chris@148 269 model3->setMaximumLevel(max);
Chris@148 270 }
Chris@148 271
Chris@148 272 return model;
Chris@148 273 }
Chris@148 274
Chris@148 275
Chris@148 276 CSVFormatDialog::CSVFormatDialog(QWidget *parent, QFile *file,
Chris@148 277 size_t defaultSampleRate) :
Chris@148 278 QDialog(parent),
Chris@148 279 m_modelType(OneDimensionalModel),
Chris@148 280 m_timingType(ExplicitTiming),
Chris@148 281 m_timeUnits(TimeAudioFrames),
Chris@148 282 m_separator("")
Chris@148 283 {
Chris@148 284 setModal(true);
Chris@148 285 setWindowTitle(tr("Select Data Format"));
Chris@148 286
Chris@148 287 (void)guessFormat(file);
Chris@148 288
Chris@148 289 QGridLayout *layout = new QGridLayout;
Chris@148 290
Chris@148 291 layout->addWidget(new QLabel(tr("\nPlease select the correct data format for this file.\n")),
Chris@148 292 0, 0, 1, 4);
Chris@148 293
Chris@148 294 layout->addWidget(new QLabel(tr("Each row specifies:")), 1, 0);
Chris@148 295
Chris@148 296 m_modelTypeCombo = new QComboBox;
Chris@148 297 m_modelTypeCombo->addItem(tr("A point in time"));
Chris@148 298 m_modelTypeCombo->addItem(tr("A value at a time"));
Chris@148 299 m_modelTypeCombo->addItem(tr("A set of values"));
Chris@148 300 layout->addWidget(m_modelTypeCombo, 1, 1, 1, 2);
Chris@148 301 connect(m_modelTypeCombo, SIGNAL(activated(int)),
Chris@148 302 this, SLOT(modelTypeChanged(int)));
Chris@148 303 m_modelTypeCombo->setCurrentIndex(int(m_modelType));
Chris@148 304
Chris@148 305 layout->addWidget(new QLabel(tr("The first column contains:")), 2, 0);
Chris@148 306
Chris@148 307 m_timingTypeCombo = new QComboBox;
Chris@148 308 m_timingTypeCombo->addItem(tr("Time, in seconds"));
Chris@148 309 m_timingTypeCombo->addItem(tr("Time, in audio sample frames"));
Chris@148 310 m_timingTypeCombo->addItem(tr("Data (rows are consecutive in time)"));
Chris@148 311 layout->addWidget(m_timingTypeCombo, 2, 1, 1, 2);
Chris@148 312 connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@148 313 this, SLOT(timingTypeChanged(int)));
Chris@148 314 m_timingTypeCombo->setCurrentIndex(m_timingType == ExplicitTiming ?
Chris@148 315 m_timeUnits == TimeSeconds ? 0 : 1 : 2);
Chris@148 316
Chris@148 317 m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@148 318 layout->addWidget(m_sampleRateLabel, 3, 0);
Chris@148 319
Chris@148 320 size_t sampleRates[] = {
Chris@148 321 8000, 11025, 12000, 22050, 24000, 32000,
Chris@148 322 44100, 48000, 88200, 96000, 176400, 192000
Chris@148 323 };
Chris@148 324
Chris@148 325 m_sampleRateCombo = new QComboBox;
Chris@148 326 m_sampleRate = defaultSampleRate;
Chris@148 327 for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) {
Chris@148 328 m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@148 329 if (sampleRates[i] == m_sampleRate) m_sampleRateCombo->setCurrentIndex(i);
Chris@148 330 }
Chris@148 331 m_sampleRateCombo->setEditable(true);
Chris@148 332
Chris@148 333 layout->addWidget(m_sampleRateCombo, 3, 1);
Chris@148 334 connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@148 335 this, SLOT(sampleRateChanged(QString)));
Chris@148 336 connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@148 337 this, SLOT(sampleRateChanged(QString)));
Chris@148 338
Chris@148 339 m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@148 340 layout->addWidget(m_windowSizeLabel, 4, 0);
Chris@148 341
Chris@148 342 m_windowSizeCombo = new QComboBox;
Chris@148 343 m_windowSize = 1024;
Chris@148 344 for (int i = 0; i <= 16; ++i) {
Chris@148 345 int value = 1 << i;
Chris@148 346 m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@148 347 if (value == m_windowSize) m_windowSizeCombo->setCurrentIndex(i);
Chris@148 348 }
Chris@148 349 m_windowSizeCombo->setEditable(true);
Chris@148 350
Chris@148 351 layout->addWidget(m_windowSizeCombo, 4, 1);
Chris@148 352 connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@148 353 this, SLOT(windowSizeChanged(QString)));
Chris@148 354 connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@148 355 this, SLOT(windowSizeChanged(QString)));
Chris@148 356
Chris@148 357 layout->addWidget(new QLabel(tr("\nExample data from file:")), 5, 0, 1, 4);
Chris@148 358
Chris@148 359 m_exampleWidget = new QTableWidget
Chris@148 360 (std::min(10, m_example.size()), m_maxExampleCols);
Chris@148 361
Chris@148 362 layout->addWidget(m_exampleWidget, 6, 0, 1, 4);
Chris@148 363 layout->setColumnStretch(3, 10);
Chris@148 364 layout->setRowStretch(4, 10);
Chris@148 365
Chris@148 366 QPushButton *ok = new QPushButton(tr("OK"));
Chris@148 367 connect(ok, SIGNAL(clicked()), this, SLOT(accept()));
Chris@148 368 ok->setDefault(true);
Chris@148 369
Chris@148 370 QPushButton *cancel = new QPushButton(tr("Cancel"));
Chris@148 371 connect(cancel, SIGNAL(clicked()), this, SLOT(reject()));
Chris@148 372
Chris@148 373 QHBoxLayout *buttonLayout = new QHBoxLayout;
Chris@148 374 buttonLayout->addStretch(1);
Chris@148 375 buttonLayout->addWidget(ok);
Chris@148 376 buttonLayout->addWidget(cancel);
Chris@148 377
Chris@148 378 QVBoxLayout *mainLayout = new QVBoxLayout;
Chris@148 379 mainLayout->addLayout(layout);
Chris@148 380 mainLayout->addLayout(buttonLayout);
Chris@148 381
Chris@148 382 setLayout(mainLayout);
Chris@148 383
Chris@148 384 timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@148 385 }
Chris@148 386
Chris@148 387 CSVFormatDialog::~CSVFormatDialog()
Chris@148 388 {
Chris@148 389 }
Chris@148 390
Chris@148 391 void
Chris@148 392 CSVFormatDialog::populateExample()
Chris@148 393 {
Chris@148 394 m_exampleWidget->setColumnCount
Chris@148 395 (m_timingType == ExplicitTiming ?
Chris@148 396 m_maxExampleCols - 1 : m_maxExampleCols);
Chris@148 397
Chris@148 398 m_exampleWidget->setHorizontalHeaderLabels(QStringList());
Chris@148 399
Chris@148 400 for (int i = 0; i < m_example.size(); ++i) {
Chris@148 401 for (int j = 0; j < m_example[i].size(); ++j) {
Chris@148 402
Chris@148 403 QTableWidgetItem *item = new QTableWidgetItem(m_example[i][j]);
Chris@148 404
Chris@148 405 if (j == 0) {
Chris@148 406 if (m_timingType == ExplicitTiming) {
Chris@148 407 m_exampleWidget->setVerticalHeaderItem(i, item);
Chris@148 408 continue;
Chris@148 409 } else {
Chris@148 410 QTableWidgetItem *header =
Chris@148 411 new QTableWidgetItem(QString("%1").arg(i));
Chris@148 412 header->setFlags(Qt::ItemIsEnabled);
Chris@148 413 m_exampleWidget->setVerticalHeaderItem(i, header);
Chris@148 414 }
Chris@148 415 }
Chris@148 416 int index = j;
Chris@148 417 if (m_timingType == ExplicitTiming) --index;
Chris@148 418 item->setFlags(Qt::ItemIsEnabled);
Chris@148 419 m_exampleWidget->setItem(i, index, item);
Chris@148 420 }
Chris@148 421 }
Chris@148 422 }
Chris@148 423
Chris@148 424 void
Chris@148 425 CSVFormatDialog::modelTypeChanged(int type)
Chris@148 426 {
Chris@148 427 m_modelType = (ModelType)type;
Chris@148 428
Chris@148 429 if (m_modelType == ThreeDimensionalModel) {
Chris@148 430 // We can't load 3d models with explicit timing, because the 3d
Chris@148 431 // model is dense so we need a fixed sample increment
Chris@148 432 m_timingTypeCombo->setCurrentIndex(2);
Chris@148 433 timingTypeChanged(2);
Chris@148 434 }
Chris@148 435 }
Chris@148 436
Chris@148 437 void
Chris@148 438 CSVFormatDialog::timingTypeChanged(int type)
Chris@148 439 {
Chris@148 440 switch (type) {
Chris@148 441
Chris@148 442 case 0:
Chris@148 443 m_timingType = ExplicitTiming;
Chris@148 444 m_timeUnits = TimeSeconds;
Chris@148 445 m_sampleRateCombo->setEnabled(false);
Chris@148 446 m_sampleRateLabel->setEnabled(false);
Chris@148 447 m_windowSizeCombo->setEnabled(false);
Chris@148 448 m_windowSizeLabel->setEnabled(false);
Chris@148 449 if (m_modelType == ThreeDimensionalModel) {
Chris@148 450 m_modelTypeCombo->setCurrentIndex(1);
Chris@148 451 modelTypeChanged(1);
Chris@148 452 }
Chris@148 453 break;
Chris@148 454
Chris@148 455 case 1:
Chris@148 456 m_timingType = ExplicitTiming;
Chris@148 457 m_timeUnits = TimeAudioFrames;
Chris@148 458 m_sampleRateCombo->setEnabled(true);
Chris@148 459 m_sampleRateLabel->setEnabled(true);
Chris@148 460 m_windowSizeCombo->setEnabled(false);
Chris@148 461 m_windowSizeLabel->setEnabled(false);
Chris@148 462 if (m_modelType == ThreeDimensionalModel) {
Chris@148 463 m_modelTypeCombo->setCurrentIndex(1);
Chris@148 464 modelTypeChanged(1);
Chris@148 465 }
Chris@148 466 break;
Chris@148 467
Chris@148 468 case 2:
Chris@148 469 m_timingType = ImplicitTiming;
Chris@148 470 m_timeUnits = TimeWindows;
Chris@148 471 m_sampleRateCombo->setEnabled(true);
Chris@148 472 m_sampleRateLabel->setEnabled(true);
Chris@148 473 m_windowSizeCombo->setEnabled(true);
Chris@148 474 m_windowSizeLabel->setEnabled(true);
Chris@148 475 break;
Chris@148 476 }
Chris@148 477
Chris@148 478 populateExample();
Chris@148 479 }
Chris@148 480
Chris@148 481 void
Chris@148 482 CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@148 483 {
Chris@148 484 bool ok = false;
Chris@148 485 int sampleRate = rateString.toInt(&ok);
Chris@148 486 if (ok) m_sampleRate = sampleRate;
Chris@148 487 }
Chris@148 488
Chris@148 489 void
Chris@148 490 CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@148 491 {
Chris@148 492 bool ok = false;
Chris@148 493 int size = sizeString.toInt(&ok);
Chris@148 494 if (ok) m_windowSize = size;
Chris@148 495 }
Chris@148 496
Chris@148 497 bool
Chris@148 498 CSVFormatDialog::guessFormat(QFile *file)
Chris@148 499 {
Chris@148 500 QTextStream in(file);
Chris@148 501 in.seek(0);
Chris@148 502
Chris@148 503 unsigned int lineno = 0;
Chris@148 504
Chris@148 505 bool nonIncreasingPrimaries = false;
Chris@148 506 bool nonNumericPrimaries = false;
Chris@148 507 bool floatPrimaries = false;
Chris@148 508 bool variableItemCount = false;
Chris@148 509 int itemCount = 1;
Chris@148 510 int earliestNonNumericItem = -1;
Chris@148 511
Chris@148 512 float prevPrimary = 0.0;
Chris@148 513
Chris@148 514 m_maxExampleCols = 0;
Chris@148 515
Chris@148 516 while (!in.atEnd()) {
Chris@148 517
Chris@148 518 QString line = in.readLine().trimmed();
Chris@148 519 if (line.startsWith("#")) continue;
Chris@148 520
Chris@148 521 if (m_separator == "") {
Chris@148 522 //!!! to do: ask the user
Chris@148 523 if (line.split(",").size() >= 2) m_separator = ",";
Chris@148 524 else if (line.split("\t").size() >= 2) m_separator = "\t";
Chris@148 525 else if (line.split("|").size() >= 2) m_separator = "|";
Chris@148 526 else if (line.split("/").size() >= 2) m_separator = "/";
Chris@148 527 else if (line.split(":").size() >= 2) m_separator = ":";
Chris@148 528 else m_separator = " ";
Chris@148 529 }
Chris@148 530
Chris@148 531 QStringList list = line.split(m_separator);
Chris@148 532 QStringList tidyList;
Chris@148 533
Chris@148 534 for (int i = 0; i < list.size(); ++i) {
Chris@148 535
Chris@148 536 QString s(list[i]);
Chris@148 537 bool numeric = false;
Chris@148 538
Chris@148 539 if (s.length() >= 2 && s.startsWith("\"") && s.endsWith("\"")) {
Chris@148 540 s = s.mid(1, s.length() - 2);
Chris@148 541 } else if (s.length() >= 2 && s.startsWith("'") && s.endsWith("'")) {
Chris@148 542 s = s.mid(1, s.length() - 2);
Chris@148 543 } else {
Chris@148 544 (void)s.toFloat(&numeric);
Chris@148 545 }
Chris@148 546
Chris@148 547 tidyList.push_back(s);
Chris@148 548
Chris@148 549 if (lineno == 0 || (list.size() < itemCount)) {
Chris@148 550 itemCount = list.size();
Chris@148 551 } else {
Chris@148 552 if (itemCount != list.size()) {
Chris@148 553 variableItemCount = true;
Chris@148 554 }
Chris@148 555 }
Chris@148 556
Chris@148 557 if (i == 0) { // primary
Chris@148 558
Chris@148 559 if (numeric) {
Chris@148 560
Chris@148 561 float primary = s.toFloat();
Chris@148 562
Chris@148 563 if (lineno > 0 && primary <= prevPrimary) {
Chris@148 564 nonIncreasingPrimaries = true;
Chris@148 565 }
Chris@148 566
Chris@148 567 if (s.contains(".") || s.contains(",")) {
Chris@148 568 floatPrimaries = true;
Chris@148 569 }
Chris@148 570
Chris@148 571 prevPrimary = primary;
Chris@148 572
Chris@148 573 } else {
Chris@148 574 nonNumericPrimaries = true;
Chris@148 575 }
Chris@148 576 } else { // secondary
Chris@148 577
Chris@148 578 if (!numeric) {
Chris@148 579 if (earliestNonNumericItem < 0 ||
Chris@148 580 i < earliestNonNumericItem) {
Chris@148 581 earliestNonNumericItem = i;
Chris@148 582 }
Chris@148 583 }
Chris@148 584 }
Chris@148 585 }
Chris@148 586
Chris@148 587 if (lineno < 10) {
Chris@148 588 m_example.push_back(tidyList);
Chris@148 589 if (lineno == 0 || tidyList.size() > m_maxExampleCols) {
Chris@148 590 m_maxExampleCols = tidyList.size();
Chris@148 591 }
Chris@148 592 }
Chris@148 593
Chris@148 594 ++lineno;
Chris@148 595
Chris@148 596 if (lineno == 50) break;
Chris@148 597 }
Chris@148 598
Chris@148 599 if (nonNumericPrimaries || nonIncreasingPrimaries) {
Chris@148 600
Chris@148 601 // Primaries are probably not a series of times
Chris@148 602
Chris@148 603 m_timingType = ImplicitTiming;
Chris@148 604 m_timeUnits = TimeWindows;
Chris@148 605
Chris@148 606 if (nonNumericPrimaries) {
Chris@148 607 m_modelType = OneDimensionalModel;
Chris@148 608 } else if (itemCount == 1 || variableItemCount ||
Chris@148 609 (earliestNonNumericItem != -1)) {
Chris@148 610 m_modelType = TwoDimensionalModel;
Chris@148 611 } else {
Chris@148 612 m_modelType = ThreeDimensionalModel;
Chris@148 613 }
Chris@148 614
Chris@148 615 } else {
Chris@148 616
Chris@148 617 // Increasing numeric primaries -- likely to be time
Chris@148 618
Chris@148 619 m_timingType = ExplicitTiming;
Chris@148 620
Chris@148 621 if (floatPrimaries) {
Chris@148 622 m_timeUnits = TimeSeconds;
Chris@148 623 } else {
Chris@148 624 m_timeUnits = TimeAudioFrames;
Chris@148 625 }
Chris@148 626
Chris@148 627 if (itemCount == 1) {
Chris@148 628 m_modelType = OneDimensionalModel;
Chris@148 629 } else if (variableItemCount || (earliestNonNumericItem != -1)) {
Chris@148 630 if (earliestNonNumericItem != -1 && earliestNonNumericItem < 2) {
Chris@148 631 m_modelType = OneDimensionalModel;
Chris@148 632 } else {
Chris@148 633 m_modelType = TwoDimensionalModel;
Chris@148 634 }
Chris@148 635 } else {
Chris@148 636 m_modelType = ThreeDimensionalModel;
Chris@148 637 }
Chris@148 638 }
Chris@148 639
Chris@148 640 std::cerr << "Estimated model type: " << m_modelType << std::endl;
Chris@148 641 std::cerr << "Estimated timing type: " << m_timingType << std::endl;
Chris@148 642 std::cerr << "Estimated units: " << m_timeUnits << std::endl;
Chris@148 643
Chris@148 644 in.seek(0);
Chris@148 645 return true;
Chris@148 646 }