annotate widgets/CSVFormatDialog.cpp @ 561:aced8ec09bc8

* Complete the overhaul of CSV file import; now you can pick the purpose for each column in the file, and SV should do the rest. The most significant practical improvement here is that we can now handle files in which time and duration do not necessarily appear in known columns.
author Chris Cannam
date Mon, 19 Jul 2010 17:08:56 +0000
parents e15afed2bfeb
children e2211947cbfd
rev   line source
Chris@378 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@378 2
Chris@378 3 /*
Chris@378 4 Sonic Visualiser
Chris@378 5 An audio file viewer and annotation editor.
Chris@378 6 Centre for Digital Music, Queen Mary, University of London.
Chris@378 7 This file copyright 2006 Chris Cannam.
Chris@378 8
Chris@378 9 This program is free software; you can redistribute it and/or
Chris@378 10 modify it under the terms of the GNU General Public License as
Chris@378 11 published by the Free Software Foundation; either version 2 of the
Chris@378 12 License, or (at your option) any later version. See the file
Chris@378 13 COPYING included with this distribution for more information.
Chris@378 14 */
Chris@378 15
Chris@378 16 #include "CSVFormatDialog.h"
Chris@378 17
Chris@561 18 #include "layer/LayerFactory.h"
Chris@561 19
Chris@378 20 #include <QFrame>
Chris@378 21 #include <QGridLayout>
Chris@378 22 #include <QPushButton>
Chris@378 23 #include <QHBoxLayout>
Chris@378 24 #include <QVBoxLayout>
Chris@378 25 #include <QTableWidget>
Chris@378 26 #include <QComboBox>
Chris@378 27 #include <QLabel>
Chris@512 28 #include <QDialogButtonBox>
Chris@378 29
Chris@560 30 #include <iostream>
Chris@378 31
Chris@560 32 CSVFormatDialog::CSVFormatDialog(QWidget *parent, CSVFormat format) :
Chris@378 33 QDialog(parent),
Chris@560 34 m_format(format)
Chris@378 35 {
Chris@378 36 setModal(true);
Chris@378 37 setWindowTitle(tr("Select Data Format"));
Chris@378 38
Chris@378 39 QGridLayout *layout = new QGridLayout;
Chris@378 40
Chris@560 41 int row = 0;
Chris@378 42
Chris@560 43 layout->addWidget(new QLabel(tr("Please select the correct data format for this file.")),
Chris@560 44 row++, 0, 1, 4);
Chris@560 45
Chris@560 46 QFrame *exampleFrame = new QFrame;
Chris@560 47 exampleFrame->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
Chris@560 48 exampleFrame->setLineWidth(2);
Chris@560 49 QGridLayout *exampleLayout = new QGridLayout;
Chris@561 50 exampleLayout->setSpacing(4);
Chris@560 51 exampleFrame->setLayout(exampleLayout);
Chris@560 52
Chris@560 53 QPalette palette = exampleFrame->palette();
Chris@560 54 palette.setColor(QPalette::Window, palette.color(QPalette::Base));
Chris@560 55 exampleFrame->setPalette(palette);
Chris@560 56
Chris@560 57 QFont fp;
Chris@561 58 fp.setPointSize(fp.pointSize() * 0.9);
Chris@561 59 // fp.setFixedPitch(true);
Chris@561 60 // fp.setStyleHint(QFont::TypeWriter);
Chris@561 61 // fp.setFamily("Monospaced");
Chris@560 62
Chris@560 63 int columns = format.getColumnCount();
Chris@560 64 QList<QStringList> example = m_format.getExample();
Chris@560 65
Chris@560 66 for (int i = 0; i < columns; ++i) {
Chris@560 67
Chris@560 68 QComboBox *cpc = new QComboBox;
Chris@560 69 m_columnPurposeCombos.push_back(cpc);
Chris@560 70 exampleLayout->addWidget(cpc, 0, i);
Chris@560 71
Chris@560 72 // NB must be in the same order as the CSVFormat::ColumnPurpose enum
Chris@560 73 cpc->addItem(tr("<ignore>")); // ColumnUnknown
Chris@560 74 cpc->addItem(tr("Time")); // ColumnStartTime
Chris@560 75 cpc->addItem(tr("End time")); // ColumnEndTime
Chris@560 76 cpc->addItem(tr("Duration")); // ColumnDuration
Chris@560 77 cpc->addItem(tr("Value")); // ColumnValue
Chris@560 78 cpc->addItem(tr("Label")); // ColumnLabel
Chris@560 79 cpc->setCurrentIndex(int(m_format.getColumnPurpose(i)));
Chris@560 80 connect(cpc, SIGNAL(activated(int)), this, SLOT(columnPurposeChanged(int)));
Chris@560 81
Chris@560 82 if (i < m_format.getMaxExampleCols()) {
Chris@560 83 for (int j = 0; j < example.size() && j < 6; ++j) {
Chris@560 84 QLabel *label = new QLabel;
Chris@560 85 label->setTextFormat(Qt::PlainText);
Chris@560 86 label->setText(example[j][i]);
Chris@560 87 label->setFont(fp);
Chris@560 88 label->setPalette(palette);
Chris@561 89 label->setIndent(8);
Chris@560 90 exampleLayout->addWidget(label, j+1, i);
Chris@560 91 }
Chris@560 92 }
Chris@560 93 }
Chris@560 94
Chris@560 95 layout->addWidget(exampleFrame, row, 0, 1, 4);
Chris@560 96 layout->setColumnStretch(3, 10);
Chris@560 97 layout->setRowStretch(row++, 10);
Chris@560 98
Chris@560 99 layout->addWidget(new QLabel(tr("Timing is specified:")), row, 0);
Chris@378 100
Chris@378 101 m_timingTypeCombo = new QComboBox;
Chris@560 102 m_timingTypeCombo->addItem(tr("Explicitly, in seconds"));
Chris@560 103 m_timingTypeCombo->addItem(tr("Explicitly, in audio sample frames"));
Chris@560 104 m_timingTypeCombo->addItem(tr("Implicitly: rows are equally spaced in time"));
Chris@560 105 layout->addWidget(m_timingTypeCombo, row++, 1, 1, 2);
Chris@378 106 connect(m_timingTypeCombo, SIGNAL(activated(int)),
Chris@378 107 this, SLOT(timingTypeChanged(int)));
Chris@560 108 m_timingTypeCombo->setCurrentIndex
Chris@560 109 (m_format.getTimingType() == CSVFormat::ExplicitTiming ?
Chris@560 110 m_format.getTimeUnits() == CSVFormat::TimeSeconds ? 0 : 1 : 2);
Chris@559 111
Chris@378 112 m_sampleRateLabel = new QLabel(tr("Audio sample rate (Hz):"));
Chris@560 113 layout->addWidget(m_sampleRateLabel, row, 0);
Chris@378 114
Chris@378 115 size_t sampleRates[] = {
Chris@378 116 8000, 11025, 12000, 22050, 24000, 32000,
Chris@378 117 44100, 48000, 88200, 96000, 176400, 192000
Chris@378 118 };
Chris@378 119
Chris@378 120 m_sampleRateCombo = new QComboBox;
Chris@378 121 for (size_t i = 0; i < sizeof(sampleRates) / sizeof(sampleRates[0]); ++i) {
Chris@378 122 m_sampleRateCombo->addItem(QString("%1").arg(sampleRates[i]));
Chris@560 123 if (sampleRates[i] == m_format.getSampleRate()) {
Chris@560 124 m_sampleRateCombo->setCurrentIndex(i);
Chris@560 125 }
Chris@378 126 }
Chris@378 127 m_sampleRateCombo->setEditable(true);
Chris@378 128
Chris@560 129 layout->addWidget(m_sampleRateCombo, row++, 1);
Chris@378 130 connect(m_sampleRateCombo, SIGNAL(activated(QString)),
Chris@378 131 this, SLOT(sampleRateChanged(QString)));
Chris@378 132 connect(m_sampleRateCombo, SIGNAL(editTextChanged(QString)),
Chris@378 133 this, SLOT(sampleRateChanged(QString)));
Chris@378 134
Chris@378 135 m_windowSizeLabel = new QLabel(tr("Frame increment between rows:"));
Chris@560 136 layout->addWidget(m_windowSizeLabel, row, 0);
Chris@378 137
Chris@378 138 m_windowSizeCombo = new QComboBox;
Chris@378 139 for (int i = 0; i <= 16; ++i) {
Chris@378 140 int value = 1 << i;
Chris@378 141 m_windowSizeCombo->addItem(QString("%1").arg(value));
Chris@560 142 if (value == int(m_format.getWindowSize())) {
Chris@560 143 m_windowSizeCombo->setCurrentIndex(i);
Chris@560 144 }
Chris@378 145 }
Chris@378 146 m_windowSizeCombo->setEditable(true);
Chris@378 147
Chris@560 148 layout->addWidget(m_windowSizeCombo, row++, 1);
Chris@378 149 connect(m_windowSizeCombo, SIGNAL(activated(QString)),
Chris@378 150 this, SLOT(windowSizeChanged(QString)));
Chris@378 151 connect(m_windowSizeCombo, SIGNAL(editTextChanged(QString)),
Chris@378 152 this, SLOT(windowSizeChanged(QString)));
Chris@378 153
Chris@561 154 m_modelLabel = new QLabel;
Chris@561 155 QFont f(m_modelLabel->font());
Chris@561 156 f.setItalic(true);
Chris@561 157 m_modelLabel->setFont(f);
Chris@561 158 layout->addWidget(m_modelLabel, row++, 0, 1, 4);
Chris@561 159
Chris@512 160 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Ok |
Chris@512 161 QDialogButtonBox::Cancel);
Chris@560 162 layout->addWidget(bb, row++, 0, 1, 4);
Chris@512 163 connect(bb, SIGNAL(accepted()), this, SLOT(accept()));
Chris@512 164 connect(bb, SIGNAL(rejected()), this, SLOT(reject()));
Chris@378 165
Chris@512 166 setLayout(layout);
Chris@378 167
Chris@378 168 timingTypeChanged(m_timingTypeCombo->currentIndex());
Chris@561 169 updateModelLabel();
Chris@378 170 }
Chris@378 171
Chris@378 172 CSVFormatDialog::~CSVFormatDialog()
Chris@378 173 {
Chris@378 174 }
Chris@378 175
Chris@378 176 CSVFormat
Chris@378 177 CSVFormatDialog::getFormat() const
Chris@378 178 {
Chris@560 179 return m_format;
Chris@378 180 }
Chris@378 181
Chris@378 182 void
Chris@561 183 CSVFormatDialog::updateModelLabel()
Chris@378 184 {
Chris@561 185 LayerFactory *f = LayerFactory::getInstance();
Chris@561 186
Chris@561 187 QString s;
Chris@561 188 switch (m_format.getModelType()) {
Chris@561 189 case CSVFormat::OneDimensionalModel:
Chris@561 190 s = f->getLayerPresentationName(LayerFactory::TimeInstants);
Chris@561 191 break;
Chris@561 192 case CSVFormat::TwoDimensionalModel:
Chris@561 193 s = f->getLayerPresentationName(LayerFactory::TimeValues);
Chris@561 194 break;
Chris@561 195 case CSVFormat::TwoDimensionalModelWithDuration:
Chris@561 196 s = f->getLayerPresentationName(LayerFactory::Regions);
Chris@561 197 break;
Chris@561 198 case CSVFormat::ThreeDimensionalModel:
Chris@561 199 s = f->getLayerPresentationName(LayerFactory::Colour3DPlot);
Chris@561 200 break;
Chris@561 201 }
Chris@561 202
Chris@561 203 m_modelLabel->setText("\n" + QString("Data will be displayed in a %1 layer.").arg(s));
Chris@378 204 }
Chris@378 205
Chris@378 206 void
Chris@378 207 CSVFormatDialog::timingTypeChanged(int type)
Chris@378 208 {
Chris@378 209 switch (type) {
Chris@378 210
Chris@378 211 case 0:
Chris@560 212 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@560 213 m_format.setTimeUnits(CSVFormat::TimeSeconds);
Chris@378 214 m_sampleRateCombo->setEnabled(false);
Chris@378 215 m_sampleRateLabel->setEnabled(false);
Chris@378 216 m_windowSizeCombo->setEnabled(false);
Chris@378 217 m_windowSizeLabel->setEnabled(false);
Chris@378 218 break;
Chris@378 219
Chris@378 220 case 1:
Chris@560 221 m_format.setTimingType(CSVFormat::ExplicitTiming);
Chris@560 222 m_format.setTimeUnits(CSVFormat::TimeAudioFrames);
Chris@378 223 m_sampleRateCombo->setEnabled(true);
Chris@378 224 m_sampleRateLabel->setEnabled(true);
Chris@378 225 m_windowSizeCombo->setEnabled(false);
Chris@378 226 m_windowSizeLabel->setEnabled(false);
Chris@378 227 break;
Chris@378 228
Chris@378 229 case 2:
Chris@560 230 m_format.setTimingType(CSVFormat::ImplicitTiming);
Chris@560 231 m_format.setTimeUnits(CSVFormat::TimeWindows);
Chris@378 232 m_sampleRateCombo->setEnabled(true);
Chris@378 233 m_sampleRateLabel->setEnabled(true);
Chris@378 234 m_windowSizeCombo->setEnabled(true);
Chris@378 235 m_windowSizeLabel->setEnabled(true);
Chris@378 236 break;
Chris@378 237 }
Chris@559 238 }
Chris@559 239
Chris@559 240 void
Chris@378 241 CSVFormatDialog::sampleRateChanged(QString rateString)
Chris@378 242 {
Chris@378 243 bool ok = false;
Chris@378 244 int sampleRate = rateString.toInt(&ok);
Chris@560 245 if (ok) m_format.setSampleRate(sampleRate);
Chris@378 246 }
Chris@378 247
Chris@378 248 void
Chris@378 249 CSVFormatDialog::windowSizeChanged(QString sizeString)
Chris@378 250 {
Chris@378 251 bool ok = false;
Chris@378 252 int size = sizeString.toInt(&ok);
Chris@560 253 if (ok) m_format.setWindowSize(size);
Chris@378 254 }
Chris@560 255
Chris@560 256 void
Chris@561 257 CSVFormatDialog::columnPurposeChanged(int p)
Chris@560 258 {
Chris@560 259 QObject *o = sender();
Chris@561 260
Chris@560 261 QComboBox *cb = qobject_cast<QComboBox *>(o);
Chris@560 262 if (!cb) return;
Chris@561 263
Chris@561 264 CSVFormat::ColumnPurpose purpose = (CSVFormat::ColumnPurpose)p;
Chris@561 265
Chris@561 266 bool haveStartTime = false;
Chris@561 267 bool haveDuration = false;
Chris@561 268 int valueCount = 0;
Chris@561 269
Chris@560 270 for (int i = 0; i < m_columnPurposeCombos.size(); ++i) {
Chris@561 271
Chris@561 272 CSVFormat::ColumnPurpose cp = m_format.getColumnPurpose(i);
Chris@561 273
Chris@561 274 bool thisChanged = (cb == m_columnPurposeCombos[i]);
Chris@561 275
Chris@561 276 if (thisChanged) {
Chris@561 277
Chris@561 278 cp = purpose;
Chris@561 279
Chris@561 280 } else {
Chris@561 281
Chris@561 282 // We can only have one ColumnStartTime column, and only
Chris@561 283 // one of either ColumnDuration or ColumnEndTime
Chris@561 284
Chris@561 285 if (purpose == CSVFormat::ColumnStartTime) {
Chris@561 286 if (cp == purpose) {
Chris@561 287 cp = CSVFormat::ColumnValue;
Chris@561 288 }
Chris@561 289 } else if (purpose == CSVFormat::ColumnDuration ||
Chris@561 290 purpose == CSVFormat::ColumnEndTime) {
Chris@561 291 if (cp == CSVFormat::ColumnDuration ||
Chris@561 292 cp == CSVFormat::ColumnEndTime) {
Chris@561 293 cp = CSVFormat::ColumnValue;
Chris@561 294 }
Chris@561 295 }
Chris@561 296
Chris@561 297 // And we can only have one label
Chris@561 298 if (purpose == CSVFormat::ColumnLabel) {
Chris@561 299 if (cp == purpose) {
Chris@561 300 cp = CSVFormat::ColumnUnknown;
Chris@561 301 }
Chris@561 302 }
Chris@561 303 }
Chris@561 304
Chris@561 305 if (cp == CSVFormat::ColumnStartTime) {
Chris@561 306 haveStartTime = true;
Chris@561 307 }
Chris@561 308 if (cp == CSVFormat::ColumnEndTime ||
Chris@561 309 cp == CSVFormat::ColumnDuration) {
Chris@561 310 haveDuration = true;
Chris@561 311 }
Chris@561 312 if (cp == CSVFormat::ColumnValue) {
Chris@561 313 ++valueCount;
Chris@561 314 }
Chris@561 315
Chris@561 316 m_columnPurposeCombos[i]->setCurrentIndex(int(cp));
Chris@561 317 m_format.setColumnPurpose(i, cp);
Chris@561 318 }
Chris@561 319
Chris@561 320 if (!haveStartTime) {
Chris@561 321 m_timingTypeCombo->setCurrentIndex(2);
Chris@561 322 timingTypeChanged(2);
Chris@561 323 }
Chris@561 324
Chris@561 325 if (haveStartTime && haveDuration) {
Chris@561 326 m_format.setModelType(CSVFormat::TwoDimensionalModelWithDuration);
Chris@561 327 } else {
Chris@561 328 if (valueCount > 1) {
Chris@561 329 m_format.setModelType(CSVFormat::ThreeDimensionalModel);
Chris@561 330 } else if (valueCount > 0) {
Chris@561 331 m_format.setModelType(CSVFormat::TwoDimensionalModel);
Chris@561 332 } else {
Chris@561 333 m_format.setModelType(CSVFormat::OneDimensionalModel);
Chris@560 334 }
Chris@560 335 }
Chris@561 336
Chris@561 337 updateModelLabel();
Chris@560 338 }
Chris@560 339
Chris@560 340