Commit 0eff8d451180c8c1328ff0d33de4e2f477147e16
Committed by
David Gräff
1 parent
52622ac9
Cursor measurements (#177)
* Cursor measurements * encapsulate measurements grid into widget * enable fg / bg color change after creation
Showing
15 changed files
with
699 additions
and
129 deletions
openhantek/src/configdialog/DsoConfigScopePage.cpp
| @@ -27,8 +27,22 @@ DsoConfigScopePage::DsoConfigScopePage(DsoSettings *settings, QWidget *parent) : | @@ -27,8 +27,22 @@ DsoConfigScopePage::DsoConfigScopePage(DsoSettings *settings, QWidget *parent) : | ||
| 27 | graphGroup = new QGroupBox(tr("Graph")); | 27 | graphGroup = new QGroupBox(tr("Graph")); |
| 28 | graphGroup->setLayout(graphLayout); | 28 | graphGroup->setLayout(graphLayout); |
| 29 | 29 | ||
| 30 | + cursorsLabel = new QLabel(tr("Position")); | ||
| 31 | + cursorsComboBox = new QComboBox(); | ||
| 32 | + cursorsComboBox->addItem("Left", Qt::LeftToolBarArea); | ||
| 33 | + cursorsComboBox->addItem("Right", Qt::RightToolBarArea); | ||
| 34 | + cursorsComboBox->setCurrentIndex(settings->view.cursorGridPosition == Qt::LeftToolBarArea ? 0 : 1); | ||
| 35 | + | ||
| 36 | + cursorsLayout = new QGridLayout(); | ||
| 37 | + cursorsLayout->addWidget(cursorsLabel, 0, 0); | ||
| 38 | + cursorsLayout->addWidget(cursorsComboBox, 0, 1); | ||
| 39 | + | ||
| 40 | + cursorsGroup = new QGroupBox(tr("Cursors")); | ||
| 41 | + cursorsGroup->setLayout(cursorsLayout); | ||
| 42 | + | ||
| 30 | mainLayout = new QVBoxLayout(); | 43 | mainLayout = new QVBoxLayout(); |
| 31 | mainLayout->addWidget(graphGroup); | 44 | mainLayout->addWidget(graphGroup); |
| 45 | + mainLayout->addWidget(cursorsGroup); | ||
| 32 | mainLayout->addStretch(1); | 46 | mainLayout->addStretch(1); |
| 33 | 47 | ||
| 34 | setLayout(mainLayout); | 48 | setLayout(mainLayout); |
| @@ -38,4 +52,5 @@ DsoConfigScopePage::DsoConfigScopePage(DsoSettings *settings, QWidget *parent) : | @@ -38,4 +52,5 @@ DsoConfigScopePage::DsoConfigScopePage(DsoSettings *settings, QWidget *parent) : | ||
| 38 | void DsoConfigScopePage::saveSettings() { | 52 | void DsoConfigScopePage::saveSettings() { |
| 39 | settings->view.interpolation = (Dso::InterpolationMode)interpolationComboBox->currentIndex(); | 53 | settings->view.interpolation = (Dso::InterpolationMode)interpolationComboBox->currentIndex(); |
| 40 | settings->view.digitalPhosphorDepth = digitalPhosphorDepthSpinBox->value(); | 54 | settings->view.digitalPhosphorDepth = digitalPhosphorDepthSpinBox->value(); |
| 55 | + settings->view.cursorGridPosition = (Qt::ToolBarArea)cursorsComboBox->currentData().toUInt(); | ||
| 41 | } | 56 | } |
openhantek/src/configdialog/DsoConfigScopePage.h
| @@ -37,4 +37,9 @@ class DsoConfigScopePage : public QWidget { | @@ -37,4 +37,9 @@ class DsoConfigScopePage : public QWidget { | ||
| 37 | QSpinBox *digitalPhosphorDepthSpinBox; | 37 | QSpinBox *digitalPhosphorDepthSpinBox; |
| 38 | QLabel *interpolationLabel; | 38 | QLabel *interpolationLabel; |
| 39 | QComboBox *interpolationComboBox; | 39 | QComboBox *interpolationComboBox; |
| 40 | + | ||
| 41 | + QGroupBox *cursorsGroup; | ||
| 42 | + QGridLayout *cursorsLayout; | ||
| 43 | + QLabel *cursorsLabel; | ||
| 44 | + QComboBox *cursorsComboBox; | ||
| 40 | }; | 45 | }; |
openhantek/src/dsowidget.cpp
| @@ -22,6 +22,7 @@ | @@ -22,6 +22,7 @@ | ||
| 22 | #include "viewconstants.h" | 22 | #include "viewconstants.h" |
| 23 | #include "viewsettings.h" | 23 | #include "viewsettings.h" |
| 24 | #include "widgets/levelslider.h" | 24 | #include "widgets/levelslider.h" |
| 25 | +#include "widgets/datagrid.h" | ||
| 25 | 26 | ||
| 26 | static int zoomScopeRow = 0; | 27 | static int zoomScopeRow = 0; |
| 27 | 28 | ||
| @@ -38,12 +39,14 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | @@ -38,12 +39,14 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | ||
| 38 | setupSliders(mainSliders); | 39 | setupSliders(mainSliders); |
| 39 | setupSliders(zoomSliders); | 40 | setupSliders(zoomSliders); |
| 40 | 41 | ||
| 41 | - connect(mainScope, &GlScope::markerMoved, [this](int marker, double position) { | ||
| 42 | - double value = std::round(position / MARKER_STEP) * MARKER_STEP; | ||
| 43 | - | ||
| 44 | - this->scope->horizontal.marker[marker] = value; | ||
| 45 | - this->mainSliders.markerSlider->setValue(marker, value); | ||
| 46 | - this->mainScope->markerUpdated(); | 42 | + connect(mainScope, &GlScope::markerMoved, [this](unsigned cursorIndex, unsigned marker) { |
| 43 | + mainSliders.markerSlider->setValue(marker, this->scope->getMarker(marker)); | ||
| 44 | + mainScope->updateCursor(cursorIndex); | ||
| 45 | + zoomScope->updateCursor(cursorIndex); | ||
| 46 | + }); | ||
| 47 | + connect(zoomScope, &GlScope::markerMoved, [this](unsigned cursorIndex, unsigned marker) { | ||
| 48 | + mainScope->updateCursor(cursorIndex); | ||
| 49 | + zoomScope->updateCursor(cursorIndex); | ||
| 47 | }); | 50 | }); |
| 48 | 51 | ||
| 49 | // The table for the settings | 52 | // The table for the settings |
| @@ -142,40 +145,88 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | @@ -142,40 +145,88 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | ||
| 142 | updateSpectrumDetails((unsigned)channel); | 145 | updateSpectrumDetails((unsigned)channel); |
| 143 | } | 146 | } |
| 144 | 147 | ||
| 148 | + // Cursors | ||
| 149 | + cursorDataGrid = new DataGrid(this); | ||
| 150 | + cursorDataGrid->setBackgroundColor(view->screen.background); | ||
| 151 | + cursorDataGrid->addItem(tr("Markers"), view->screen.text); | ||
| 152 | + for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { | ||
| 153 | + cursorDataGrid->addItem(scope->voltage[channel].name, view->screen.voltage[channel]); | ||
| 154 | + } | ||
| 155 | + for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { | ||
| 156 | + cursorDataGrid->addItem(scope->spectrum[channel].name, view->screen.spectrum[channel]); | ||
| 157 | + } | ||
| 158 | + cursorDataGrid->selectItem(0); | ||
| 159 | + | ||
| 160 | + connect(cursorDataGrid, &DataGrid::itemSelected, [this] (unsigned index) { | ||
| 161 | + mainScope->cursorSelected(index); | ||
| 162 | + zoomScope->cursorSelected(index); | ||
| 163 | + }); | ||
| 164 | + connect(cursorDataGrid, &DataGrid::itemUpdated, [this, scope] (unsigned index) { | ||
| 165 | + unsigned channelCount = scope->countChannels(); | ||
| 166 | + if (0 < index && index < channelCount + 1) { | ||
| 167 | + ChannelID channel = index - 1; | ||
| 168 | + if (scope->voltage[channel].used) { | ||
| 169 | + unsigned shape = (unsigned)scope->voltage[channel].cursor.shape; | ||
| 170 | + if (shape == DsoSettingsScopeCursor::NONE) { | ||
| 171 | + scope->voltage[channel].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; | ||
| 172 | + } else { | ||
| 173 | + scope->voltage[channel].cursor.shape = DsoSettingsScopeCursor::NONE; | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + } else if (channelCount < index && index < 2 * channelCount + 1) { | ||
| 177 | + ChannelID channel = index - channelCount - 1; | ||
| 178 | + if (scope->spectrum[channel].used) { | ||
| 179 | + unsigned shape = (unsigned)scope->spectrum[channel].cursor.shape; | ||
| 180 | + if (shape == DsoSettingsScopeCursor::NONE) { | ||
| 181 | + scope->spectrum[channel].cursor.shape = DsoSettingsScopeCursor::RECTANGULAR; | ||
| 182 | + } else { | ||
| 183 | + scope->spectrum[channel].cursor.shape = DsoSettingsScopeCursor::NONE; | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + updateMarkerDetails(); | ||
| 188 | + mainScope->updateCursor(index); | ||
| 189 | + zoomScope->updateCursor(index); | ||
| 190 | + }); | ||
| 191 | + | ||
| 192 | + scope->horizontal.cursor.shape = DsoSettingsScopeCursor::VERTICAL; | ||
| 193 | + | ||
| 145 | // The layout for the widgets | 194 | // The layout for the widgets |
| 146 | mainLayout = new QGridLayout(); | 195 | mainLayout = new QGridLayout(); |
| 147 | - mainLayout->setColumnStretch(2, 1); // Scopes increase their size | 196 | + mainLayout->setColumnStretch(3, 1); // Scopes increase their size |
| 148 | // Bars around the scope, needed because the slider-drawing-area is outside | 197 | // Bars around the scope, needed because the slider-drawing-area is outside |
| 149 | // the scope at min/max | 198 | // the scope at min/max |
| 150 | - mainLayout->setColumnMinimumWidth(1, mainSliders.triggerPositionSlider->preMargin()); | ||
| 151 | - mainLayout->setColumnMinimumWidth(3, mainSliders.triggerPositionSlider->postMargin()); | 199 | + mainLayout->setColumnMinimumWidth(2, mainSliders.triggerPositionSlider->preMargin()); |
| 200 | + mainLayout->setColumnMinimumWidth(4, mainSliders.triggerPositionSlider->postMargin()); | ||
| 152 | mainLayout->setSpacing(0); | 201 | mainLayout->setSpacing(0); |
| 153 | int row = 0; | 202 | int row = 0; |
| 154 | - mainLayout->addLayout(settingsLayout, row++, 0, 1, 5); | 203 | + mainLayout->addLayout(settingsLayout, row++, 1, 1, 5); |
| 155 | // 5x5 box for mainScope & mainSliders | 204 | // 5x5 box for mainScope & mainSliders |
| 156 | mainLayout->setRowMinimumHeight(row + 1, mainSliders.offsetSlider->preMargin()); | 205 | mainLayout->setRowMinimumHeight(row + 1, mainSliders.offsetSlider->preMargin()); |
| 157 | mainLayout->setRowMinimumHeight(row + 3, mainSliders.offsetSlider->postMargin()); | 206 | mainLayout->setRowMinimumHeight(row + 3, mainSliders.offsetSlider->postMargin()); |
| 158 | mainLayout->setRowStretch(row + 2, 1); | 207 | mainLayout->setRowStretch(row + 2, 1); |
| 159 | - mainLayout->addWidget(mainScope, row + 2, 2); | ||
| 160 | - mainLayout->addWidget(mainSliders.offsetSlider, row + 1, 0, 3, 2, Qt::AlignRight); | ||
| 161 | - mainLayout->addWidget(mainSliders.triggerPositionSlider, row, 1, 2, 3, Qt::AlignBottom); | ||
| 162 | - mainLayout->addWidget(mainSliders.triggerLevelSlider, row + 1, 3, 3, 2, Qt::AlignLeft); | ||
| 163 | - mainLayout->addWidget(mainSliders.markerSlider, row + 3, 1, 2, 3, Qt::AlignTop); | 208 | + mainLayout->addWidget(mainScope, row + 2, 3); |
| 209 | + mainLayout->addWidget(mainSliders.offsetSlider, row + 1, 1, 3, 2, Qt::AlignRight); | ||
| 210 | + mainLayout->addWidget(mainSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom); | ||
| 211 | + mainLayout->addWidget(mainSliders.triggerLevelSlider, row + 1, 4, 3, 2, Qt::AlignLeft); | ||
| 212 | + mainLayout->addWidget(mainSliders.markerSlider, row + 3, 2, 2, 3, Qt::AlignTop); | ||
| 164 | row += 5; | 213 | row += 5; |
| 165 | // Separators and markerLayout | 214 | // Separators and markerLayout |
| 166 | - mainLayout->setRowMinimumHeight(row++, 4); | ||
| 167 | - mainLayout->addLayout(markerLayout, row++, 0, 1, 5); | 215 | + mainLayout->setRowMinimumHeight(row++, 5); |
| 216 | + mainLayout->addLayout(markerLayout, row++, 1, 1, 5); | ||
| 168 | mainLayout->setRowMinimumHeight(row++, 4); | 217 | mainLayout->setRowMinimumHeight(row++, 4); |
| 169 | // 5x5 box for zoomScope & zoomSliders | 218 | // 5x5 box for zoomScope & zoomSliders |
| 170 | zoomScopeRow = row + 2; | 219 | zoomScopeRow = row + 2; |
| 171 | - mainLayout->addWidget(zoomScope, zoomScopeRow, 2); | ||
| 172 | - mainLayout->addWidget(zoomSliders.offsetSlider, row + 1, 0, 3, 2, Qt::AlignRight); | ||
| 173 | - mainLayout->addWidget(zoomSliders.triggerPositionSlider, row, 1, 2, 3, Qt::AlignBottom); | ||
| 174 | - mainLayout->addWidget(zoomSliders.triggerLevelSlider, row + 1, 3, 3, 2, Qt::AlignLeft); | 220 | + mainLayout->addWidget(zoomScope, zoomScopeRow, 3); |
| 221 | + mainLayout->addWidget(zoomSliders.offsetSlider, row + 1, 1, 3, 2, Qt::AlignRight); | ||
| 222 | + mainLayout->addWidget(zoomSliders.triggerPositionSlider, row, 2, 2, 3, Qt::AlignBottom); | ||
| 223 | + mainLayout->addWidget(zoomSliders.triggerLevelSlider, row + 1, 4, 3, 2, Qt::AlignLeft); | ||
| 175 | row += 5; | 224 | row += 5; |
| 176 | // Separator and embedded measurementLayout | 225 | // Separator and embedded measurementLayout |
| 177 | mainLayout->setRowMinimumHeight(row++, 8); | 226 | mainLayout->setRowMinimumHeight(row++, 8); |
| 178 | - mainLayout->addLayout(measurementLayout, row++, 0, 1, 5); | 227 | + mainLayout->addLayout(measurementLayout, row++, 1, 1, 5); |
| 228 | + | ||
| 229 | + updateCursorGrid(view->cursorsVisible); | ||
| 179 | 230 | ||
| 180 | // The widget itself | 231 | // The widget itself |
| 181 | setPalette(palette); | 232 | setPalette(palette); |
| @@ -187,20 +238,54 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | @@ -187,20 +238,54 @@ DsoWidget::DsoWidget(DsoSettingsScope *scope, DsoSettingsView *view, const Dso:: | ||
| 187 | connect(mainSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); | 238 | connect(mainSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); |
| 188 | connect(zoomSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); | 239 | connect(zoomSliders.offsetSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateOffset); |
| 189 | 240 | ||
| 190 | - connect(mainSliders.triggerPositionSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerPosition); | ||
| 191 | - zoomSliders.triggerPositionSlider->setEnabled(false); | 241 | + connect(mainSliders.triggerPositionSlider, &LevelSlider::valueChanged, [this](int index, double value) { |
| 242 | + updateTriggerPosition(index, value, true); | ||
| 243 | + }); | ||
| 244 | + connect(zoomSliders.triggerPositionSlider, &LevelSlider::valueChanged, [this](int index, double value) { | ||
| 245 | + updateTriggerPosition(index, value, false); | ||
| 246 | + }); | ||
| 192 | 247 | ||
| 193 | connect(mainSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); | 248 | connect(mainSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); |
| 194 | connect(zoomSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); | 249 | connect(zoomSliders.triggerLevelSlider, &LevelSlider::valueChanged, this, &DsoWidget::updateTriggerLevel); |
| 195 | 250 | ||
| 196 | connect(mainSliders.markerSlider, &LevelSlider::valueChanged, [this](int index, double value) { | 251 | connect(mainSliders.markerSlider, &LevelSlider::valueChanged, [this](int index, double value) { |
| 197 | updateMarker(index, value); | 252 | updateMarker(index, value); |
| 198 | - mainScope->update(); | ||
| 199 | - zoomScope->update(); | 253 | + mainScope->updateCursor(); |
| 254 | + zoomScope->updateCursor(); | ||
| 200 | }); | 255 | }); |
| 201 | zoomSliders.markerSlider->setEnabled(false); | 256 | zoomSliders.markerSlider->setEnabled(false); |
| 202 | } | 257 | } |
| 203 | 258 | ||
| 259 | +void DsoWidget::updateCursorGrid(bool enabled) { | ||
| 260 | + if (!enabled) { | ||
| 261 | + cursorDataGrid->selectItem(0); | ||
| 262 | + cursorDataGrid->setParent(nullptr); | ||
| 263 | + mainScope->cursorSelected(0); | ||
| 264 | + zoomScope->cursorSelected(0); | ||
| 265 | + return; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + switch (view->cursorGridPosition) { | ||
| 269 | + case Qt::LeftToolBarArea: | ||
| 270 | + if (mainLayout->itemAtPosition(0, 0) == nullptr) { | ||
| 271 | + cursorDataGrid->setParent(nullptr); | ||
| 272 | + mainLayout->addWidget(cursorDataGrid, 0, 0, mainLayout->rowCount(), 1); | ||
| 273 | + } | ||
| 274 | + break; | ||
| 275 | + case Qt::RightToolBarArea: | ||
| 276 | + if (mainLayout->itemAtPosition(0, 6) == nullptr) { | ||
| 277 | + cursorDataGrid->setParent(nullptr); | ||
| 278 | + mainLayout->addWidget(cursorDataGrid, 0, 6, mainLayout->rowCount(), 1); | ||
| 279 | + } | ||
| 280 | + break; | ||
| 281 | + default: | ||
| 282 | + if (cursorDataGrid->parent() != nullptr) { | ||
| 283 | + cursorDataGrid->setParent(nullptr); | ||
| 284 | + } | ||
| 285 | + break; | ||
| 286 | + } | ||
| 287 | +} | ||
| 288 | + | ||
| 204 | void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { | 289 | void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { |
| 205 | // The offset sliders for all possible channels | 290 | // The offset sliders for all possible channels |
| 206 | sliders.offsetSlider = new LevelSlider(Qt::RightArrow); | 291 | sliders.offsetSlider = new LevelSlider(Qt::RightArrow); |
| @@ -248,7 +333,7 @@ void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { | @@ -248,7 +333,7 @@ void DsoWidget::setupSliders(DsoWidget::Sliders &sliders) { | ||
| 248 | sliders.markerSlider->addSlider(QString::number(marker + 1), marker); | 333 | sliders.markerSlider->addSlider(QString::number(marker + 1), marker); |
| 249 | sliders.markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); | 334 | sliders.markerSlider->setLimits(marker, -DIVS_TIME / 2, DIVS_TIME / 2); |
| 250 | sliders.markerSlider->setStep(marker, MARKER_STEP); | 335 | sliders.markerSlider->setStep(marker, MARKER_STEP); |
| 251 | - sliders.markerSlider->setValue(marker, scope->horizontal.marker[marker]); | 336 | + sliders.markerSlider->setValue(marker, scope->horizontal.cursor.pos[marker].x()); |
| 252 | sliders.markerSlider->setIndexVisible(marker, true); | 337 | sliders.markerSlider->setIndexVisible(marker, true); |
| 253 | } | 338 | } |
| 254 | } | 339 | } |
| @@ -285,7 +370,7 @@ void DsoWidget::setMeasurementVisible(ChannelID channel) { | @@ -285,7 +370,7 @@ void DsoWidget::setMeasurementVisible(ChannelID channel) { | ||
| 285 | } | 370 | } |
| 286 | 371 | ||
| 287 | static QString markerToString(DsoSettingsScope *scope, unsigned index) { | 372 | static QString markerToString(DsoSettingsScope *scope, unsigned index) { |
| 288 | - double value = (DIVS_TIME * (0.5 - scope->trigger.position) + scope->horizontal.marker[index]) * scope->horizontal.timebase; | 373 | + double value = (DIVS_TIME * (0.5 - scope->trigger.position) + scope->getMarker(index)) * scope->horizontal.timebase; |
| 289 | int precision = 3 - (int)floor(log10(fabs(value))); | 374 | int precision = 3 - (int)floor(log10(fabs(value))); |
| 290 | 375 | ||
| 291 | if (scope->horizontal.timebase < 1e-9) | 376 | if (scope->horizontal.timebase < 1e-9) |
| @@ -302,8 +387,9 @@ static QString markerToString(DsoSettingsScope *scope, unsigned index) { | @@ -302,8 +387,9 @@ static QString markerToString(DsoSettingsScope *scope, unsigned index) { | ||
| 302 | 387 | ||
| 303 | /// \brief Update the label about the marker measurements | 388 | /// \brief Update the label about the marker measurements |
| 304 | void DsoWidget::updateMarkerDetails() { | 389 | void DsoWidget::updateMarkerDetails() { |
| 305 | - double divs = fabs(scope->horizontal.marker[1] - scope->horizontal.marker[0]); | 390 | + double divs = fabs(scope->horizontal.cursor.pos[1].x() - scope->horizontal.cursor.pos[0].x()); |
| 306 | double time = divs * scope->horizontal.timebase; | 391 | double time = divs * scope->horizontal.timebase; |
| 392 | + double freq = divs * scope->horizontal.frequencybase; | ||
| 307 | 393 | ||
| 308 | QString prefix(tr("Markers")); | 394 | QString prefix(tr("Markers")); |
| 309 | if (view->zoom) { | 395 | if (view->zoom) { |
| @@ -315,6 +401,36 @@ void DsoWidget::updateMarkerDetails() { | @@ -315,6 +401,36 @@ void DsoWidget::updateMarkerDetails() { | ||
| 315 | markerInfoLabel->setText(prefix.append(": %1 %2").arg(markerToString(scope, 0)).arg(markerToString(scope, 1))); | 401 | markerInfoLabel->setText(prefix.append(": %1 %2").arg(markerToString(scope, 0)).arg(markerToString(scope, 1))); |
| 316 | markerTimeLabel->setText(valueToString(time, UNIT_SECONDS, 4)); | 402 | markerTimeLabel->setText(valueToString(time, UNIT_SECONDS, 4)); |
| 317 | markerFrequencyLabel->setText(valueToString(1.0 / time, UNIT_HERTZ, 4)); | 403 | markerFrequencyLabel->setText(valueToString(1.0 / time, UNIT_HERTZ, 4)); |
| 404 | + | ||
| 405 | + int index = 0; | ||
| 406 | + cursorDataGrid->updateInfo(index++, true, QString(), valueToString(time, UNIT_SECONDS, 4), valueToString(freq, UNIT_HERTZ, 4)); | ||
| 407 | + | ||
| 408 | + for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { | ||
| 409 | + if (scope->voltage[channel].used) { | ||
| 410 | + QPointF p0 = scope->voltage[channel].cursor.pos[0]; | ||
| 411 | + QPointF p1 = scope->voltage[channel].cursor.pos[1]; | ||
| 412 | + cursorDataGrid->updateInfo(index, true, | ||
| 413 | + scope->voltage[channel].cursor.shape != DsoSettingsScopeCursor::NONE ? tr("ON") : tr("OFF"), | ||
| 414 | + valueToString(fabs(p1.x() - p0.x()) * scope->horizontal.timebase, UNIT_SECONDS, 4), | ||
| 415 | + valueToString(fabs(p1.y() - p0.y()) * scope->gain(channel), UNIT_VOLTS, 4)); | ||
| 416 | + } else { | ||
| 417 | + cursorDataGrid->updateInfo(index, false); | ||
| 418 | + } | ||
| 419 | + ++index; | ||
| 420 | + } | ||
| 421 | + for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { | ||
| 422 | + if (scope->spectrum[channel].used) { | ||
| 423 | + QPointF p0 = scope->spectrum[channel].cursor.pos[0]; | ||
| 424 | + QPointF p1 = scope->spectrum[channel].cursor.pos[1]; | ||
| 425 | + cursorDataGrid->updateInfo(index, true, | ||
| 426 | + scope->spectrum[channel].cursor.shape != DsoSettingsScopeCursor::NONE ? tr("ON") : tr("OFF"), | ||
| 427 | + valueToString(fabs(p1.x() - p0.x()) * scope->horizontal.frequencybase, UNIT_HERTZ, 4), | ||
| 428 | + valueToString(fabs(p1.y() - p0.y()) * scope->spectrum[channel].magnitude, UNIT_DECIBEL, 4)); | ||
| 429 | + } else { | ||
| 430 | + cursorDataGrid->updateInfo(index, false); | ||
| 431 | + } | ||
| 432 | + ++index; | ||
| 433 | + } | ||
| 318 | } | 434 | } |
| 319 | 435 | ||
| 320 | /// \brief Update the label about the trigger settings | 436 | /// \brief Update the label about the trigger settings |
| @@ -385,10 +501,13 @@ void DsoWidget::updateSpectrumMagnitude(ChannelID channel) { updateSpectrumDetai | @@ -385,10 +501,13 @@ void DsoWidget::updateSpectrumMagnitude(ChannelID channel) { updateSpectrumDetai | ||
| 385 | void DsoWidget::updateSpectrumUsed(ChannelID channel, bool used) { | 501 | void DsoWidget::updateSpectrumUsed(ChannelID channel, bool used) { |
| 386 | if (channel >= (unsigned int)scope->voltage.size()) return; | 502 | if (channel >= (unsigned int)scope->voltage.size()) return; |
| 387 | 503 | ||
| 504 | +// if (!used && cursorDataGrid->spectrumCursors[channel].selector->isChecked()) cursorDataGrid->selectItem(0); | ||
| 505 | + | ||
| 388 | mainSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); | 506 | mainSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); |
| 389 | zoomSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); | 507 | zoomSliders.offsetSlider->setIndexVisible(scope->voltage.size() + channel, used); |
| 390 | 508 | ||
| 391 | updateSpectrumDetails(channel); | 509 | updateSpectrumDetails(channel); |
| 510 | + updateMarkerDetails(); | ||
| 392 | } | 511 | } |
| 393 | 512 | ||
| 394 | /// \brief Handles modeChanged signal from the trigger dock. | 513 | /// \brief Handles modeChanged signal from the trigger dock. |
| @@ -452,6 +571,8 @@ void DsoWidget::updateVoltageGain(ChannelID channel) { | @@ -452,6 +571,8 @@ void DsoWidget::updateVoltageGain(ChannelID channel) { | ||
| 452 | void DsoWidget::updateVoltageUsed(ChannelID channel, bool used) { | 571 | void DsoWidget::updateVoltageUsed(ChannelID channel, bool used) { |
| 453 | if (channel >= (unsigned int)scope->voltage.size()) return; | 572 | if (channel >= (unsigned int)scope->voltage.size()) return; |
| 454 | 573 | ||
| 574 | +// if (!used && cursorDataGrid->voltageCursors[channel].selector->isChecked()) cursorDataGrid->selectItem(0); | ||
| 575 | + | ||
| 455 | mainSliders.offsetSlider->setIndexVisible(channel, used); | 576 | mainSliders.offsetSlider->setIndexVisible(channel, used); |
| 456 | zoomSliders.offsetSlider->setIndexVisible(channel, used); | 577 | zoomSliders.offsetSlider->setIndexVisible(channel, used); |
| 457 | 578 | ||
| @@ -460,6 +581,7 @@ void DsoWidget::updateVoltageUsed(ChannelID channel, bool used) { | @@ -460,6 +581,7 @@ void DsoWidget::updateVoltageUsed(ChannelID channel, bool used) { | ||
| 460 | 581 | ||
| 461 | setMeasurementVisible(channel); | 582 | setMeasurementVisible(channel); |
| 462 | updateVoltageDetails(channel); | 583 | updateVoltageDetails(channel); |
| 584 | + updateMarkerDetails(); | ||
| 463 | } | 585 | } |
| 464 | 586 | ||
| 465 | /// \brief Change the record length. | 587 | /// \brief Change the record length. |
| @@ -549,11 +671,11 @@ void DsoWidget::updateOffset(ChannelID channel, double value) { | @@ -549,11 +671,11 @@ void DsoWidget::updateOffset(ChannelID channel, double value) { | ||
| 549 | 671 | ||
| 550 | if (channel < scope->voltage.size() * 2) { | 672 | if (channel < scope->voltage.size() * 2) { |
| 551 | if (mainSliders.offsetSlider->value(channel) != value) { | 673 | if (mainSliders.offsetSlider->value(channel) != value) { |
| 552 | - QSignalBlocker blocker(mainSliders.offsetSlider); | 674 | + const QSignalBlocker blocker(mainSliders.offsetSlider); |
| 553 | mainSliders.offsetSlider->setValue(channel, value); | 675 | mainSliders.offsetSlider->setValue(channel, value); |
| 554 | } | 676 | } |
| 555 | if (zoomSliders.offsetSlider->value(channel) != value) { | 677 | if (zoomSliders.offsetSlider->value(channel) != value) { |
| 556 | - QSignalBlocker blocker(zoomSliders.offsetSlider); | 678 | + const QSignalBlocker blocker(zoomSliders.offsetSlider); |
| 557 | zoomSliders.offsetSlider->setValue(channel, value); | 679 | zoomSliders.offsetSlider->setValue(channel, value); |
| 558 | } | 680 | } |
| 559 | } | 681 | } |
| @@ -561,18 +683,38 @@ void DsoWidget::updateOffset(ChannelID channel, double value) { | @@ -561,18 +683,38 @@ void DsoWidget::updateOffset(ChannelID channel, double value) { | ||
| 561 | emit offsetChanged(channel, value); | 683 | emit offsetChanged(channel, value); |
| 562 | } | 684 | } |
| 563 | 685 | ||
| 686 | +/// \brief Translate horizontal position (0..1) from main view to zoom view. | ||
| 687 | +double DsoWidget::mainToZoom(double position) const { | ||
| 688 | + double m1 = scope->getMarker(0); | ||
| 689 | + double m2 = scope->getMarker(1); | ||
| 690 | + if (m1 > m2) std::swap(m1, m2); | ||
| 691 | + return ((position - 0.5) * DIVS_TIME - m1) / (m2 - m1); | ||
| 692 | +} | ||
| 693 | + | ||
| 694 | +/// \brief Translate horizontal position (0..1) from zoom view to main view. | ||
| 695 | +double DsoWidget::zoomToMain(double position) const { | ||
| 696 | + double m1 = scope->getMarker(0); | ||
| 697 | + double m2 = scope->getMarker(1); | ||
| 698 | + if (m1 > m2) std::swap(m1, m2); | ||
| 699 | + return 0.5 + (m1 + position * (m2 - m1)) / DIVS_TIME; | ||
| 700 | +} | ||
| 701 | + | ||
| 564 | /// \brief Handles signals affecting trigger position in the zoom view. | 702 | /// \brief Handles signals affecting trigger position in the zoom view. |
| 565 | void DsoWidget::adaptTriggerPositionSlider() { | 703 | void DsoWidget::adaptTriggerPositionSlider() { |
| 566 | - double m1 = scope->horizontal.marker[0]; | ||
| 567 | - double m2 = scope->horizontal.marker[1]; | 704 | + double value = mainToZoom(scope->trigger.position); |
| 568 | 705 | ||
| 569 | - if (m1 > m2) std::swap(m1, m2); | ||
| 570 | - double value = (scope->trigger.position - 0.5) * DIVS_TIME; | ||
| 571 | - if (m1 != m2 && m1 <= value && value <= m2) { | ||
| 572 | - zoomSliders.triggerPositionSlider->setIndexVisible(0, true); | ||
| 573 | - zoomSliders.triggerPositionSlider->setValue(0, (value - m1) / (m2 - m1)); | 706 | + LevelSlider &slider = *zoomSliders.triggerPositionSlider; |
| 707 | + const QSignalBlocker blocker(slider); | ||
| 708 | + if (slider.minimum(0) <= value && value <= slider.maximum(0)) { | ||
| 709 | + slider.setEnabled(true); | ||
| 710 | + slider.setValue(0, value); | ||
| 574 | } else { | 711 | } else { |
| 575 | - zoomSliders.triggerPositionSlider->setIndexVisible(0, false); | 712 | + slider.setEnabled(false); |
| 713 | + if (value < slider.minimum(0)) { | ||
| 714 | + slider.setValue(0, slider.minimum(0)); | ||
| 715 | + } else { | ||
| 716 | + slider.setValue(0, slider.maximum(0)); | ||
| 717 | + } | ||
| 576 | } | 718 | } |
| 577 | } | 719 | } |
| 578 | 720 | ||
| @@ -580,16 +722,22 @@ void DsoWidget::adaptTriggerPositionSlider() { | @@ -580,16 +722,22 @@ void DsoWidget::adaptTriggerPositionSlider() { | ||
| 580 | /// \param index The index of the slider. | 722 | /// \param index The index of the slider. |
| 581 | /// \param value The new triggerPosition in seconds relative to the first | 723 | /// \param value The new triggerPosition in seconds relative to the first |
| 582 | /// sample. | 724 | /// sample. |
| 583 | -void DsoWidget::updateTriggerPosition(int index, double value) { | 725 | +void DsoWidget::updateTriggerPosition(int index, double value, bool mainView) { |
| 584 | if (index != 0) return; | 726 | if (index != 0) return; |
| 585 | 727 | ||
| 586 | - scope->trigger.position = value; | ||
| 587 | - adaptTriggerPositionSlider(); | 728 | + if (mainView) { |
| 729 | + scope->trigger.position = value; | ||
| 730 | + adaptTriggerPositionSlider(); | ||
| 731 | + } else { | ||
| 732 | + scope->trigger.position = zoomToMain(value); | ||
| 733 | + const QSignalBlocker blocker(mainSliders.triggerPositionSlider); | ||
| 734 | + mainSliders.triggerPositionSlider->setValue(index, scope->trigger.position); | ||
| 735 | + } | ||
| 588 | 736 | ||
| 589 | updateTriggerDetails(); | 737 | updateTriggerDetails(); |
| 590 | updateMarkerDetails(); | 738 | updateMarkerDetails(); |
| 591 | 739 | ||
| 592 | - emit triggerPositionChanged(value * scope->horizontal.timebase * DIVS_TIME); | 740 | + emit triggerPositionChanged(scope->trigger.position * scope->horizontal.timebase * DIVS_TIME); |
| 593 | } | 741 | } |
| 594 | 742 | ||
| 595 | /// \brief Handles valueChanged signal from the trigger level slider. | 743 | /// \brief Handles valueChanged signal from the trigger level slider. |
| @@ -599,11 +747,11 @@ void DsoWidget::updateTriggerLevel(ChannelID channel, double value) { | @@ -599,11 +747,11 @@ void DsoWidget::updateTriggerLevel(ChannelID channel, double value) { | ||
| 599 | scope->voltage[channel].trigger = value; | 747 | scope->voltage[channel].trigger = value; |
| 600 | 748 | ||
| 601 | if (mainSliders.triggerLevelSlider->value(channel) != value) { | 749 | if (mainSliders.triggerLevelSlider->value(channel) != value) { |
| 602 | - QSignalBlocker blocker(mainSliders.triggerLevelSlider); | 750 | + const QSignalBlocker blocker(mainSliders.triggerLevelSlider); |
| 603 | mainSliders.triggerLevelSlider->setValue(channel, value); | 751 | mainSliders.triggerLevelSlider->setValue(channel, value); |
| 604 | } | 752 | } |
| 605 | if (zoomSliders.triggerLevelSlider->value(channel) != value) { | 753 | if (zoomSliders.triggerLevelSlider->value(channel) != value) { |
| 606 | - QSignalBlocker blocker(zoomSliders.triggerLevelSlider); | 754 | + const QSignalBlocker blocker(zoomSliders.triggerLevelSlider); |
| 607 | zoomSliders.triggerLevelSlider->setValue(channel, value); | 755 | zoomSliders.triggerLevelSlider->setValue(channel, value); |
| 608 | } | 756 | } |
| 609 | 757 | ||
| @@ -616,10 +764,7 @@ void DsoWidget::updateTriggerLevel(ChannelID channel, double value) { | @@ -616,10 +764,7 @@ void DsoWidget::updateTriggerLevel(ChannelID channel, double value) { | ||
| 616 | /// \param marker The index of the slider. | 764 | /// \param marker The index of the slider. |
| 617 | /// \param value The new marker position. | 765 | /// \param value The new marker position. |
| 618 | void DsoWidget::updateMarker(int marker, double value) { | 766 | void DsoWidget::updateMarker(int marker, double value) { |
| 619 | - scope->horizontal.marker[marker] = value; | ||
| 620 | - | 767 | + scope->setMarker(marker, value); |
| 621 | adaptTriggerPositionSlider(); | 768 | adaptTriggerPositionSlider(); |
| 622 | updateMarkerDetails(); | 769 | updateMarkerDetails(); |
| 623 | - | ||
| 624 | - emit markerChanged(marker, value); | ||
| 625 | } | 770 | } |
openhantek/src/dsowidget.h
| @@ -15,12 +15,15 @@ | @@ -15,12 +15,15 @@ | ||
| 15 | class SpectrumGenerator; | 15 | class SpectrumGenerator; |
| 16 | struct DsoSettingsScope; | 16 | struct DsoSettingsScope; |
| 17 | struct DsoSettingsView; | 17 | struct DsoSettingsView; |
| 18 | +class DataGrid; | ||
| 18 | 19 | ||
| 19 | /// \brief The widget for the oszilloscope-screen | 20 | /// \brief The widget for the oszilloscope-screen |
| 20 | /// This widget contains the scopes and all level sliders. | 21 | /// This widget contains the scopes and all level sliders. |
| 21 | class DsoWidget : public QWidget { | 22 | class DsoWidget : public QWidget { |
| 22 | Q_OBJECT | 23 | Q_OBJECT |
| 23 | 24 | ||
| 25 | + public: | ||
| 26 | + | ||
| 24 | struct Sliders { | 27 | struct Sliders { |
| 25 | LevelSlider *offsetSlider; ///< The sliders for the graph offsets | 28 | LevelSlider *offsetSlider; ///< The sliders for the graph offsets |
| 26 | LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger | 29 | LevelSlider *triggerPositionSlider; ///< The slider for the pretrigger |
| @@ -28,7 +31,6 @@ class DsoWidget : public QWidget { | @@ -28,7 +31,6 @@ class DsoWidget : public QWidget { | ||
| 28 | LevelSlider *markerSlider; ///< The sliders for the markers | 31 | LevelSlider *markerSlider; ///< The sliders for the markers |
| 29 | }; | 32 | }; |
| 30 | 33 | ||
| 31 | - public: | ||
| 32 | /// \brief Initializes the components of the oszilloscope-screen. | 34 | /// \brief Initializes the components of the oszilloscope-screen. |
| 33 | /// \param settings The settings object containing the oscilloscope settings. | 35 | /// \param settings The settings object containing the oscilloscope settings. |
| 34 | /// \param dataAnalyzer The data analyzer that should be used as data source. | 36 | /// \param dataAnalyzer The data analyzer that should be used as data source. |
| @@ -50,6 +52,9 @@ class DsoWidget : public QWidget { | @@ -50,6 +52,9 @@ class DsoWidget : public QWidget { | ||
| 50 | void updateTriggerDetails(); | 52 | void updateTriggerDetails(); |
| 51 | void updateVoltageDetails(ChannelID channel); | 53 | void updateVoltageDetails(ChannelID channel); |
| 52 | 54 | ||
| 55 | + double mainToZoom(double position) const; | ||
| 56 | + double zoomToMain(double position) const; | ||
| 57 | + | ||
| 53 | Sliders mainSliders; | 58 | Sliders mainSliders; |
| 54 | Sliders zoomSliders; | 59 | Sliders zoomSliders; |
| 55 | 60 | ||
| @@ -79,6 +84,8 @@ class DsoWidget : public QWidget { | @@ -79,6 +84,8 @@ class DsoWidget : public QWidget { | ||
| 79 | std::vector<QLabel *> measurementAmplitudeLabel; ///< Amplitude of the signal (V) | 84 | std::vector<QLabel *> measurementAmplitudeLabel; ///< Amplitude of the signal (V) |
| 80 | std::vector<QLabel *> measurementFrequencyLabel; ///< Frequency of the signal (Hz) | 85 | std::vector<QLabel *> measurementFrequencyLabel; ///< Frequency of the signal (Hz) |
| 81 | 86 | ||
| 87 | + DataGrid *cursorDataGrid; | ||
| 88 | + | ||
| 82 | DsoSettingsScope* scope; | 89 | DsoSettingsScope* scope; |
| 83 | DsoSettingsView* view; | 90 | DsoSettingsView* view; |
| 84 | const Dso::ControlSpecification* spec; | 91 | const Dso::ControlSpecification* spec; |
| @@ -113,11 +120,12 @@ class DsoWidget : public QWidget { | @@ -113,11 +120,12 @@ class DsoWidget : public QWidget { | ||
| 113 | 120 | ||
| 114 | // Scope control | 121 | // Scope control |
| 115 | void updateZoom(bool enabled); | 122 | void updateZoom(bool enabled); |
| 123 | + void updateCursorGrid(bool enabled); | ||
| 116 | 124 | ||
| 117 | private slots: | 125 | private slots: |
| 118 | // Sliders | 126 | // Sliders |
| 119 | void updateOffset(ChannelID channel, double value); | 127 | void updateOffset(ChannelID channel, double value); |
| 120 | - void updateTriggerPosition(int index, double value); | 128 | + void updateTriggerPosition(int index, double value, bool mainView = true); |
| 121 | void updateTriggerLevel(ChannelID channel, double value); | 129 | void updateTriggerLevel(ChannelID channel, double value); |
| 122 | void updateMarker(int marker, double value); | 130 | void updateMarker(int marker, double value); |
| 123 | 131 | ||
| @@ -126,5 +134,4 @@ class DsoWidget : public QWidget { | @@ -126,5 +134,4 @@ class DsoWidget : public QWidget { | ||
| 126 | void offsetChanged(ChannelID channel, double value); ///< A graph offset has been changed | 134 | void offsetChanged(ChannelID channel, double value); ///< A graph offset has been changed |
| 127 | void triggerPositionChanged(double value); ///< The pretrigger has been changed | 135 | void triggerPositionChanged(double value); ///< The pretrigger has been changed |
| 128 | void triggerLevelChanged(ChannelID channel, double value); ///< A trigger level has been changed | 136 | void triggerLevelChanged(ChannelID channel, double value); ///< A trigger level has been changed |
| 129 | - void markerChanged(unsigned int marker, double value); ///< A marker position has been changed | ||
| 130 | }; | 137 | }; |
openhantek/src/exporting/legacyexportdrawer.cpp
| @@ -115,10 +115,12 @@ bool LegacyExportDrawer::exportSamples(const PPresult *result, QPaintDevice* pai | @@ -115,10 +115,12 @@ bool LegacyExportDrawer::exportSamples(const PPresult *result, QPaintDevice* pai | ||
| 115 | painter.setPen(colorValues->text); | 115 | painter.setPen(colorValues->text); |
| 116 | 116 | ||
| 117 | // Calculate variables needed for zoomed scope | 117 | // Calculate variables needed for zoomed scope |
| 118 | - double divs = fabs(settings->scope.horizontal.marker[1] - settings->scope.horizontal.marker[0]); | 118 | + double m1 = settings->scope.getMarker(0); |
| 119 | + double m2 = settings->scope.getMarker(1); | ||
| 120 | + double divs = fabs(m2 - m1); | ||
| 119 | double time = divs * settings->scope.horizontal.timebase; | 121 | double time = divs * settings->scope.horizontal.timebase; |
| 120 | double zoomFactor = DIVS_TIME / divs; | 122 | double zoomFactor = DIVS_TIME / divs; |
| 121 | - double zoomOffset = (settings->scope.horizontal.marker[0] + settings->scope.horizontal.marker[1]) / 2; | 123 | + double zoomOffset = (m1 + m2) / 2; |
| 122 | 124 | ||
| 123 | if (settings->view.zoom) { | 125 | if (settings->view.zoom) { |
| 124 | scopeHeight = (double)(paintDevice->height() - (channelCount + 5) * lineHeight) / 2; | 126 | scopeHeight = (double)(paintDevice->height() - (channelCount + 5) * lineHeight) / 2; |
openhantek/src/glscope.cpp
| @@ -54,50 +54,106 @@ void GlScope::fixOpenGLversion(QSurfaceFormat::RenderableType t) { | @@ -54,50 +54,106 @@ void GlScope::fixOpenGLversion(QSurfaceFormat::RenderableType t) { | ||
| 54 | 54 | ||
| 55 | GlScope::GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) | 55 | GlScope::GlScope(DsoSettingsScope *scope, DsoSettingsView *view, QWidget *parent) |
| 56 | : QOpenGLWidget(parent), scope(scope), view(view) { | 56 | : QOpenGLWidget(parent), scope(scope), view(view) { |
| 57 | - vaMarker.resize(MARKER_COUNT); | 57 | + cursorInfo.clear(); |
| 58 | + cursorInfo.push_back(&scope->horizontal.cursor); | ||
| 59 | + selectedCursor = 0; | ||
| 60 | + for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { | ||
| 61 | + cursorInfo.push_back(&scope->voltage[channel].cursor); | ||
| 62 | + } | ||
| 63 | + for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel) { | ||
| 64 | + cursorInfo.push_back(&scope->spectrum[channel].cursor); | ||
| 65 | + } | ||
| 66 | + vaMarker.resize(cursorInfo.size()); | ||
| 58 | } | 67 | } |
| 59 | 68 | ||
| 60 | GlScope::~GlScope() {/* virtual destructor necessary */} | 69 | GlScope::~GlScope() {/* virtual destructor necessary */} |
| 61 | 70 | ||
| 71 | +QPointF GlScope::eventToPosition(QMouseEvent *event) { | ||
| 72 | + QPointF position((double)(event->x() - width() / 2) * DIVS_TIME / (double)width(), | ||
| 73 | + (double)(height() / 2 - event->y()) * DIVS_VOLTAGE / (double)height()); | ||
| 74 | + if (zoomed) { | ||
| 75 | + double m1 = scope->getMarker(0); | ||
| 76 | + double m2 = scope->getMarker(1); | ||
| 77 | + if (m1 > m2) std::swap(m1, m2); | ||
| 78 | + position.setX(m1 + (0.5 + (position.x() / DIVS_TIME)) * (m2 - m1)); | ||
| 79 | + } | ||
| 80 | + return position; | ||
| 81 | +} | ||
| 82 | + | ||
| 62 | void GlScope::mousePressEvent(QMouseEvent *event) { | 83 | void GlScope::mousePressEvent(QMouseEvent *event) { |
| 63 | - if (!zoomed && event->button() == Qt::LeftButton) { | ||
| 64 | - double position = (double)(event->x() - width() / 2) * DIVS_TIME / (double)width(); | ||
| 65 | - double distance = DIVS_TIME; | 84 | + if (!(zoomed && selectedCursor == 0) && event->button() == Qt::LeftButton) { |
| 85 | + QPointF position = eventToPosition(event); | ||
| 66 | selectedMarker = NO_MARKER; | 86 | selectedMarker = NO_MARKER; |
| 87 | + DsoSettingsScopeCursor *cursor = cursorInfo[selectedCursor]; | ||
| 67 | // Capture nearest marker located within snap area (+/- 1% of full scale). | 88 | // Capture nearest marker located within snap area (+/- 1% of full scale). |
| 68 | - for (unsigned marker = 0; marker < MARKER_COUNT; ++marker) { | ||
| 69 | - if (!scope->horizontal.marker_visible[marker]) continue; | ||
| 70 | - if (fabs(scope->horizontal.marker[marker] - position) < std::min(distance, DIVS_TIME / 100.0)) { | ||
| 71 | - distance = fabs(scope->horizontal.marker[marker] - position); | ||
| 72 | - selectedMarker = marker; | 89 | + double dX0 = fabs(cursor->pos[0].x() - position.x()); |
| 90 | + double dX1 = fabs(cursor->pos[1].x() - position.x()); | ||
| 91 | + double dY0 = fabs(cursor->pos[0].y() - position.y()); | ||
| 92 | + double dY1 = fabs(cursor->pos[1].y() - position.y()); | ||
| 93 | + | ||
| 94 | + switch (cursor->shape) { | ||
| 95 | + case DsoSettingsScopeCursor::RECTANGULAR: | ||
| 96 | + if (std::min(dX0, dX1) < 1.0 / DIVS_SUB && std::min(dY0, dY1) < 1.0 / DIVS_SUB) { | ||
| 97 | + // Do we need to swap Y-coords? | ||
| 98 | + if ((dX0 < dX1 && dY0 > dY1) || (dX0 > dX1 && dY0 < dY1)) { | ||
| 99 | + std::swap(cursor->pos[0].ry(), cursor->pos[1].ry()); | ||
| 100 | + } | ||
| 101 | + selectedMarker = (dX0 < dX1) ? 0 : 1; | ||
| 73 | } | 102 | } |
| 103 | + break; | ||
| 104 | + case DsoSettingsScopeCursor::VERTICAL: | ||
| 105 | + if (dX0 < dX1) { | ||
| 106 | + if (dX0 < 1.0 / DIVS_SUB) selectedMarker = 0; | ||
| 107 | + } else { | ||
| 108 | + if (dX1 < 1.0 / DIVS_SUB) selectedMarker = 1; | ||
| 109 | + } | ||
| 110 | + break; | ||
| 111 | + case DsoSettingsScopeCursor::HORIZONTAL: | ||
| 112 | + if (dY0 < dY1) { | ||
| 113 | + if (dY0 < 1.0 / DIVS_SUB) selectedMarker = 0; | ||
| 114 | + } else { | ||
| 115 | + if (dY1 < 1.0 / DIVS_SUB) selectedMarker = 1; | ||
| 116 | + } | ||
| 117 | + break; | ||
| 118 | + case DsoSettingsScopeCursor::NONE: | ||
| 119 | + break; | ||
| 120 | + default: | ||
| 121 | + break; | ||
| 122 | + } | ||
| 123 | + if (selectedMarker != NO_MARKER) { | ||
| 124 | + cursorInfo[selectedCursor]->pos[selectedMarker] = position; | ||
| 125 | + if (selectedCursor == 0) emit markerMoved(selectedCursor, selectedMarker); | ||
| 74 | } | 126 | } |
| 75 | - if (selectedMarker != NO_MARKER) { emit markerMoved(selectedMarker, position); } | ||
| 76 | } | 127 | } |
| 77 | event->accept(); | 128 | event->accept(); |
| 78 | } | 129 | } |
| 79 | 130 | ||
| 80 | void GlScope::mouseMoveEvent(QMouseEvent *event) { | 131 | void GlScope::mouseMoveEvent(QMouseEvent *event) { |
| 81 | - if (!zoomed && (event->buttons() & Qt::LeftButton) != 0) { | ||
| 82 | - double position = (double)(event->x() - width() / 2) * DIVS_TIME / (double)width(); | 132 | + if (!(zoomed && selectedCursor == 0) && (event->buttons() & Qt::LeftButton) != 0) { |
| 133 | + QPointF position = eventToPosition(event); | ||
| 83 | if (selectedMarker == NO_MARKER) { | 134 | if (selectedMarker == NO_MARKER) { |
| 84 | // User started draging outside the snap area of any marker: | 135 | // User started draging outside the snap area of any marker: |
| 85 | // move all markers to current position and select last marker in the array. | 136 | // move all markers to current position and select last marker in the array. |
| 86 | for (unsigned marker = 0; marker < MARKER_COUNT; ++marker) { | 137 | for (unsigned marker = 0; marker < MARKER_COUNT; ++marker) { |
| 87 | - emit markerMoved(marker, position); | 138 | + cursorInfo[selectedCursor]->pos[marker] = position; |
| 139 | + emit markerMoved(selectedCursor, marker); | ||
| 88 | selectedMarker = marker; | 140 | selectedMarker = marker; |
| 89 | } | 141 | } |
| 90 | } else if (selectedMarker < MARKER_COUNT) { | 142 | } else if (selectedMarker < MARKER_COUNT) { |
| 91 | - emit markerMoved(selectedMarker, position); | 143 | + cursorInfo[selectedCursor]->pos[selectedMarker] = position; |
| 144 | + emit markerMoved(selectedCursor, selectedMarker); | ||
| 92 | } | 145 | } |
| 93 | } | 146 | } |
| 94 | event->accept(); | 147 | event->accept(); |
| 95 | } | 148 | } |
| 96 | 149 | ||
| 97 | void GlScope::mouseReleaseEvent(QMouseEvent *event) { | 150 | void GlScope::mouseReleaseEvent(QMouseEvent *event) { |
| 98 | - if (!zoomed && event->button() == Qt::LeftButton) { | ||
| 99 | - double position = (double)(event->x() - width() / 2) * DIVS_TIME / (double)width(); | ||
| 100 | - if (selectedMarker < MARKER_COUNT) { emit markerMoved(selectedMarker, position); } | 151 | + if (!(zoomed && selectedCursor == 0) && event->button() == Qt::LeftButton) { |
| 152 | + QPointF position = eventToPosition(event); | ||
| 153 | + if (selectedMarker < MARKER_COUNT) { | ||
| 154 | + cursorInfo[selectedCursor]->pos[selectedMarker] = position; | ||
| 155 | + emit markerMoved(selectedCursor, selectedMarker); | ||
| 156 | + } | ||
| 101 | selectedMarker = NO_MARKER; | 157 | selectedMarker = NO_MARKER; |
| 102 | } | 158 | } |
| 103 | event->accept(); | 159 | event->accept(); |
| @@ -209,12 +265,11 @@ void GlScope::initializeGL() { | @@ -209,12 +265,11 @@ void GlScope::initializeGL() { | ||
| 209 | m_marker.create(); | 265 | m_marker.create(); |
| 210 | m_marker.bind(); | 266 | m_marker.bind(); |
| 211 | m_marker.setUsagePattern(QOpenGLBuffer::StaticDraw); | 267 | m_marker.setUsagePattern(QOpenGLBuffer::StaticDraw); |
| 212 | - m_marker.allocate(int(vaMarker.size() * sizeof(Line))); | 268 | + m_marker.allocate(int(vaMarker.size() * sizeof(Vertices))); |
| 213 | program->enableAttributeArray(vertexLocation); | 269 | program->enableAttributeArray(vertexLocation); |
| 214 | program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); | 270 | program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3, 0); |
| 215 | } | 271 | } |
| 216 | - | ||
| 217 | - markerUpdated(); | 272 | + updateCursor(); |
| 218 | 273 | ||
| 219 | m_program = std::move(program); | 274 | m_program = std::move(program); |
| 220 | shaderCompileSuccess = true; | 275 | shaderCompileSuccess = true; |
| @@ -239,18 +294,65 @@ void GlScope::showData(std::shared_ptr<PPresult> data) { | @@ -239,18 +294,65 @@ void GlScope::showData(std::shared_ptr<PPresult> data) { | ||
| 239 | update(); | 294 | update(); |
| 240 | } | 295 | } |
| 241 | 296 | ||
| 242 | -void GlScope::markerUpdated() { | ||
| 243 | - | ||
| 244 | - for (unsigned marker = 0; marker < vaMarker.size(); ++marker) { | ||
| 245 | - if (!scope->horizontal.marker_visible[marker]) continue; | ||
| 246 | - vaMarker[marker] = {QVector3D((GLfloat)scope->horizontal.marker[marker], -DIVS_VOLTAGE, 0.0f), | ||
| 247 | - QVector3D((GLfloat)scope->horizontal.marker[marker], DIVS_VOLTAGE, 0.0f)}; | 297 | +void GlScope::generateVertices(unsigned marker, const DsoSettingsScopeCursor &cursor) { |
| 298 | + const float Z_ORDER = 1.0f; | ||
| 299 | + switch (cursor.shape) { | ||
| 300 | + case DsoSettingsScopeCursor::NONE: | ||
| 301 | + vaMarker[marker] = { | ||
| 302 | + QVector3D(-DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER), | ||
| 303 | + QVector3D(-DIVS_TIME, DIVS_VOLTAGE, Z_ORDER), | ||
| 304 | + QVector3D( DIVS_TIME, DIVS_VOLTAGE, Z_ORDER), | ||
| 305 | + QVector3D( DIVS_TIME, -DIVS_VOLTAGE, Z_ORDER) | ||
| 306 | + }; | ||
| 307 | + break; | ||
| 308 | + case DsoSettingsScopeCursor::VERTICAL: | ||
| 309 | + vaMarker[marker] = { | ||
| 310 | + QVector3D(cursor.pos[0].x(), -DIVS_VOLTAGE, Z_ORDER), | ||
| 311 | + QVector3D(cursor.pos[0].x(), DIVS_VOLTAGE, Z_ORDER), | ||
| 312 | + QVector3D(cursor.pos[1].x(), DIVS_VOLTAGE, Z_ORDER), | ||
| 313 | + QVector3D(cursor.pos[1].x(), -DIVS_VOLTAGE, Z_ORDER) | ||
| 314 | + }; | ||
| 315 | + break; | ||
| 316 | + case DsoSettingsScopeCursor::HORIZONTAL: | ||
| 317 | + vaMarker[marker] = { | ||
| 318 | + QVector3D(-DIVS_TIME, cursor.pos[0].y(), Z_ORDER), | ||
| 319 | + QVector3D( DIVS_TIME, cursor.pos[0].y(), Z_ORDER), | ||
| 320 | + QVector3D( DIVS_TIME, cursor.pos[1].y(), Z_ORDER), | ||
| 321 | + QVector3D(-DIVS_TIME, cursor.pos[1].y(), Z_ORDER) | ||
| 322 | + }; | ||
| 323 | + break; | ||
| 324 | + case DsoSettingsScopeCursor::RECTANGULAR: | ||
| 325 | + if ((cursor.pos[1].x() - cursor.pos[0].x()) * (cursor.pos[1].y() - cursor.pos[0].y()) > 0.0) { | ||
| 326 | + vaMarker[marker] = { | ||
| 327 | + QVector3D(cursor.pos[0].x(), cursor.pos[0].y(), Z_ORDER), | ||
| 328 | + QVector3D(cursor.pos[1].x(), cursor.pos[0].y(), Z_ORDER), | ||
| 329 | + QVector3D(cursor.pos[1].x(), cursor.pos[1].y(), Z_ORDER), | ||
| 330 | + QVector3D(cursor.pos[0].x(), cursor.pos[1].y(), Z_ORDER) | ||
| 331 | + }; | ||
| 332 | + } else { | ||
| 333 | + vaMarker[marker] = { | ||
| 334 | + QVector3D(cursor.pos[0].x(), cursor.pos[0].y(), Z_ORDER), | ||
| 335 | + QVector3D(cursor.pos[0].x(), cursor.pos[1].y(), Z_ORDER), | ||
| 336 | + QVector3D(cursor.pos[1].x(), cursor.pos[1].y(), Z_ORDER), | ||
| 337 | + QVector3D(cursor.pos[1].x(), cursor.pos[0].y(), Z_ORDER) | ||
| 338 | + }; | ||
| 339 | + } | ||
| 340 | + break; | ||
| 341 | + default: | ||
| 342 | + break; | ||
| 248 | } | 343 | } |
| 344 | +} | ||
| 249 | 345 | ||
| 346 | +void GlScope::updateCursor(unsigned index) { | ||
| 347 | + if (index > 0) { | ||
| 348 | + generateVertices(index, *cursorInfo[index]); | ||
| 349 | + } else for (index = 0; index < cursorInfo.size(); ++index) { | ||
| 350 | + generateVertices(index, *cursorInfo[index]); | ||
| 351 | + } | ||
| 250 | // Write coordinates to GPU | 352 | // Write coordinates to GPU |
| 251 | makeCurrent(); | 353 | makeCurrent(); |
| 252 | m_marker.bind(); | 354 | m_marker.bind(); |
| 253 | - m_marker.write(0, vaMarker.data(), vaMarker.size() * sizeof(Line)); | 355 | + m_marker.write(0, vaMarker.data(), vaMarker.size() * sizeof(Vertices)); |
| 254 | } | 356 | } |
| 255 | 357 | ||
| 256 | void GlScope::paintGL() { | 358 | void GlScope::paintGL() { |
| @@ -268,12 +370,13 @@ void GlScope::paintGL() { | @@ -268,12 +370,13 @@ void GlScope::paintGL() { | ||
| 268 | // Apply zoom settings via matrix transformation | 370 | // Apply zoom settings via matrix transformation |
| 269 | if (zoomed) { | 371 | if (zoomed) { |
| 270 | QMatrix4x4 m; | 372 | QMatrix4x4 m; |
| 271 | - m.scale(QVector3D(DIVS_TIME / (GLfloat)fabs(scope->horizontal.marker[1] - scope->horizontal.marker[0]), 1.0f, | ||
| 272 | - 1.0f)); | ||
| 273 | - m.translate((GLfloat) - (scope->horizontal.marker[0] + scope->horizontal.marker[1]) / 2, 0.0f, 0.0f); | 373 | + m.scale(QVector3D(DIVS_TIME / (GLfloat)fabs(scope->getMarker(1) - scope->getMarker(0)), 1.0f, 1.0f)); |
| 374 | + m.translate((GLfloat) - (scope->getMarker(0) + scope->getMarker(1)) / 2, 0.0f, 0.0f); | ||
| 274 | m_program->setUniformValue(matrixLocation, pmvMatrix * m); | 375 | m_program->setUniformValue(matrixLocation, pmvMatrix * m); |
| 275 | } | 376 | } |
| 276 | 377 | ||
| 378 | + drawMarkers(); | ||
| 379 | + | ||
| 277 | unsigned historyIndex = 0; | 380 | unsigned historyIndex = 0; |
| 278 | for (Graph &graph : m_GraphHistory) { | 381 | for (Graph &graph : m_GraphHistory) { |
| 279 | for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { | 382 | for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel) { |
| @@ -287,8 +390,6 @@ void GlScope::paintGL() { | @@ -287,8 +390,6 @@ void GlScope::paintGL() { | ||
| 287 | 390 | ||
| 288 | if (zoomed) { m_program->setUniformValue(matrixLocation, pmvMatrix); } | 391 | if (zoomed) { m_program->setUniformValue(matrixLocation, pmvMatrix); } |
| 289 | 392 | ||
| 290 | - if (!this->zoomed) drawMarkers(); | ||
| 291 | - | ||
| 292 | drawGrid(); | 393 | drawGrid(); |
| 293 | m_program->release(); | 394 | m_program->release(); |
| 294 | } | 395 | } |
| @@ -432,21 +533,38 @@ void GlScope::drawGrid() { | @@ -432,21 +533,38 @@ void GlScope::drawGrid() { | ||
| 432 | m_vaoGrid[2].release(); | 533 | m_vaoGrid[2].release(); |
| 433 | } | 534 | } |
| 434 | 535 | ||
| 536 | +void GlScope::drawVertices(QOpenGLFunctions *gl, unsigned marker, QColor color) { | ||
| 537 | + m_program->setUniformValue(colorLocation, (marker == selectedCursor) ? color : color.darker()); | ||
| 538 | + gl->glDrawArrays(GL_LINE_LOOP, GLint(marker * VERTICES_ARRAY_SIZE), VERTICES_ARRAY_SIZE); | ||
| 539 | + if (cursorInfo[marker]->shape == DsoSettingsScopeCursor::RECTANGULAR) { | ||
| 540 | + color.setAlphaF(0.25); | ||
| 541 | + m_program->setUniformValue(colorLocation, color.darker()); | ||
| 542 | + gl->glDrawArrays(GL_TRIANGLE_FAN, GLint(marker * VERTICES_ARRAY_SIZE), VERTICES_ARRAY_SIZE); | ||
| 543 | + } | ||
| 544 | +} | ||
| 545 | + | ||
| 435 | void GlScope::drawMarkers() { | 546 | void GlScope::drawMarkers() { |
| 436 | auto *gl = context()->functions(); | 547 | auto *gl = context()->functions(); |
| 437 | - QColor trColor = view->screen.markers; | ||
| 438 | - m_program->setUniformValue(colorLocation, trColor); | ||
| 439 | 548 | ||
| 440 | m_vaoMarker.bind(); | 549 | m_vaoMarker.bind(); |
| 441 | 550 | ||
| 442 | - // Draw all | ||
| 443 | - gl->glLineWidth(1); | ||
| 444 | - gl->glDrawArrays(GL_LINES, 0, (GLsizei)vaMarker.size() * 2); | 551 | + unsigned marker = 0; |
| 552 | + drawVertices(gl, marker, view->screen.markers); | ||
| 553 | + ++marker; | ||
| 445 | 554 | ||
| 446 | - // Draw selected | ||
| 447 | - if (selectedMarker != NO_MARKER) { | ||
| 448 | - gl->glLineWidth(3); | ||
| 449 | - gl->glDrawArrays(GL_LINES, selectedMarker * 2, (GLsizei)2); | 555 | + if (view->cursorsVisible) { |
| 556 | + gl->glDepthMask(GL_FALSE); | ||
| 557 | + for (ChannelID channel = 0; channel < scope->voltage.size(); ++channel, ++marker) { | ||
| 558 | + if (scope->voltage[channel].used) { | ||
| 559 | + drawVertices(gl, marker, view->screen.voltage[channel]); | ||
| 560 | + } | ||
| 561 | + } | ||
| 562 | + for (ChannelID channel = 0; channel < scope->spectrum.size(); ++channel, ++marker) { | ||
| 563 | + if (scope->spectrum[channel].used) { | ||
| 564 | + drawVertices(gl, marker, view->screen.spectrum[channel]); | ||
| 565 | + } | ||
| 566 | + } | ||
| 567 | + gl->glDepthMask(GL_TRUE); | ||
| 450 | } | 568 | } |
| 451 | 569 | ||
| 452 | m_vaoMarker.release(); | 570 | m_vaoMarker.release(); |
openhantek/src/glscope.h
| @@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
| 18 | 18 | ||
| 19 | struct DsoSettingsView; | 19 | struct DsoSettingsView; |
| 20 | struct DsoSettingsScope; | 20 | struct DsoSettingsScope; |
| 21 | +struct DsoSettingsScopeCursor; | ||
| 21 | class PPresult; | 22 | class PPresult; |
| 22 | 23 | ||
| 23 | /// \brief OpenGL accelerated widget that displays the oscilloscope screen. | 24 | /// \brief OpenGL accelerated widget that displays the oscilloscope screen. |
| @@ -40,7 +41,8 @@ class GlScope : public QOpenGLWidget { | @@ -40,7 +41,8 @@ class GlScope : public QOpenGLWidget { | ||
| 40 | * @param data | 41 | * @param data |
| 41 | */ | 42 | */ |
| 42 | void showData(std::shared_ptr<PPresult> data); | 43 | void showData(std::shared_ptr<PPresult> data); |
| 43 | - void markerUpdated(); | 44 | + void updateCursor(unsigned index = 0); |
| 45 | + void cursorSelected(unsigned index) { selectedCursor = index; updateCursor(index); } | ||
| 44 | 46 | ||
| 45 | protected: | 47 | protected: |
| 46 | /// \brief Initializes the scope widget. | 48 | /// \brief Initializes the scope widget. |
| @@ -70,11 +72,14 @@ class GlScope : public QOpenGLWidget { | @@ -70,11 +72,14 @@ class GlScope : public QOpenGLWidget { | ||
| 70 | void drawGrid(); | 72 | void drawGrid(); |
| 71 | /// Draw vertical lines at marker positions | 73 | /// Draw vertical lines at marker positions |
| 72 | void drawMarkers(); | 74 | void drawMarkers(); |
| 75 | + void generateVertices(unsigned marker, const DsoSettingsScopeCursor &cursor); | ||
| 76 | + void drawVertices(QOpenGLFunctions *gl, unsigned marker, QColor color); | ||
| 73 | 77 | ||
| 74 | void drawVoltageChannelGraph(ChannelID channel, Graph &graph, int historyIndex); | 78 | void drawVoltageChannelGraph(ChannelID channel, Graph &graph, int historyIndex); |
| 75 | void drawSpectrumChannelGraph(ChannelID channel, Graph &graph, int historyIndex); | 79 | void drawSpectrumChannelGraph(ChannelID channel, Graph &graph, int historyIndex); |
| 80 | + QPointF eventToPosition(QMouseEvent *event); | ||
| 76 | signals: | 81 | signals: |
| 77 | - void markerMoved(unsigned marker, double position); | 82 | + void markerMoved(unsigned cursorIndex, unsigned marker); |
| 78 | 83 | ||
| 79 | private: | 84 | private: |
| 80 | // User settings | 85 | // User settings |
| @@ -85,16 +90,20 @@ class GlScope : public QOpenGLWidget { | @@ -85,16 +90,20 @@ class GlScope : public QOpenGLWidget { | ||
| 85 | // Marker | 90 | // Marker |
| 86 | const unsigned NO_MARKER = UINT_MAX; | 91 | const unsigned NO_MARKER = UINT_MAX; |
| 87 | #pragma pack(push, 1) | 92 | #pragma pack(push, 1) |
| 88 | - struct Line { | ||
| 89 | - QVector3D x; | ||
| 90 | - QVector3D y; | 93 | + struct Vertices { |
| 94 | + QVector3D a, b, c, d; | ||
| 91 | }; | 95 | }; |
| 92 | #pragma pack(pop) | 96 | #pragma pack(pop) |
| 93 | - std::vector<Line> vaMarker; | 97 | + const unsigned VERTICES_ARRAY_SIZE = sizeof(Vertices) / sizeof(QVector3D); |
| 98 | + std::vector<Vertices> vaMarker; | ||
| 94 | unsigned selectedMarker = NO_MARKER; | 99 | unsigned selectedMarker = NO_MARKER; |
| 95 | QOpenGLBuffer m_marker; | 100 | QOpenGLBuffer m_marker; |
| 96 | QOpenGLVertexArrayObject m_vaoMarker; | 101 | QOpenGLVertexArrayObject m_vaoMarker; |
| 97 | 102 | ||
| 103 | + // Cursors | ||
| 104 | + std::vector<DsoSettingsScopeCursor *> cursorInfo; | ||
| 105 | + unsigned selectedCursor = 0; | ||
| 106 | + | ||
| 98 | // Grid | 107 | // Grid |
| 99 | QOpenGLBuffer m_grid; | 108 | QOpenGLBuffer m_grid; |
| 100 | QOpenGLVertexArrayObject m_vaoGrid[3]; | 109 | QOpenGLVertexArrayObject m_vaoGrid[3]; |
openhantek/src/mainwindow.cpp
| @@ -39,6 +39,7 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | @@ -39,6 +39,7 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | ||
| 39 | ui->actionManualCommand->setIcon(iconFont->icon(fa::edit)); | 39 | ui->actionManualCommand->setIcon(iconFont->icon(fa::edit)); |
| 40 | ui->actionDigital_phosphor->setIcon(QIcon(":/images/digitalphosphor.svg")); | 40 | ui->actionDigital_phosphor->setIcon(QIcon(":/images/digitalphosphor.svg")); |
| 41 | ui->actionZoom->setIcon(iconFont->icon(fa::crop)); | 41 | ui->actionZoom->setIcon(iconFont->icon(fa::crop)); |
| 42 | + ui->actionCursors->setIcon(iconFont->icon(fa::crosshairs)); | ||
| 42 | 43 | ||
| 43 | // Window title | 44 | // Window title |
| 44 | setWindowIcon(QIcon(":openhantek.png")); | 45 | setWindowIcon(QIcon(":openhantek.png")); |
| @@ -160,6 +161,7 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | @@ -160,6 +161,7 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | ||
| 160 | auto usedChanged = [this, dsoControl, spec](ChannelID channel, bool used) { | 161 | auto usedChanged = [this, dsoControl, spec](ChannelID channel, bool used) { |
| 161 | if (channel >= (unsigned int)mSettings->scope.voltage.size()) return; | 162 | if (channel >= (unsigned int)mSettings->scope.voltage.size()) return; |
| 162 | 163 | ||
| 164 | +// if (!used) dsoWidget-> | ||
| 163 | bool mathUsed = mSettings->scope.anyUsed(spec->channels); | 165 | bool mathUsed = mSettings->scope.anyUsed(spec->channels); |
| 164 | 166 | ||
| 165 | // Normal channel, check if voltage/spectrum or math channel is used | 167 | // Normal channel, check if voltage/spectrum or math channel is used |
| @@ -267,6 +269,18 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | @@ -267,6 +269,18 @@ MainWindow::MainWindow(HantekDsoControl *dsoControl, DsoSettings *settings, Expo | ||
| 267 | }); | 269 | }); |
| 268 | ui->actionZoom->setChecked(mSettings->view.zoom); | 270 | ui->actionZoom->setChecked(mSettings->view.zoom); |
| 269 | 271 | ||
| 272 | + connect(ui->actionCursors, &QAction::toggled, [this](bool enabled) { | ||
| 273 | + mSettings->view.cursorsVisible = enabled; | ||
| 274 | + | ||
| 275 | + if (mSettings->view.cursorsVisible) | ||
| 276 | + this->ui->actionCursors->setStatusTip(tr("Hide measurements")); | ||
| 277 | + else | ||
| 278 | + this->ui->actionCursors->setStatusTip(tr("Show measurements")); | ||
| 279 | + | ||
| 280 | + this->dsoWidget->updateCursorGrid(enabled); | ||
| 281 | + }); | ||
| 282 | + ui->actionCursors->setChecked(mSettings->view.cursorsVisible); | ||
| 283 | + | ||
| 270 | connect(ui->actionAbout, &QAction::triggered, [this]() { | 284 | connect(ui->actionAbout, &QAction::triggered, [this]() { |
| 271 | QMessageBox::about( | 285 | QMessageBox::about( |
| 272 | this, tr("About OpenHantek %1").arg(VERSION), | 286 | this, tr("About OpenHantek %1").arg(VERSION), |
openhantek/src/mainwindow.ui
| @@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
| 20 | <x>0</x> | 20 | <x>0</x> |
| 21 | <y>0</y> | 21 | <y>0</y> |
| 22 | <width>800</width> | 22 | <width>800</width> |
| 23 | - <height>25</height> | 23 | + <height>28</height> |
| 24 | </rect> | 24 | </rect> |
| 25 | </property> | 25 | </property> |
| 26 | <widget class="QMenu" name="menuFile"> | 26 | <widget class="QMenu" name="menuFile"> |
| @@ -39,6 +39,8 @@ | @@ -39,6 +39,8 @@ | ||
| 39 | </property> | 39 | </property> |
| 40 | <addaction name="actionDigital_phosphor"/> | 40 | <addaction name="actionDigital_phosphor"/> |
| 41 | <addaction name="actionZoom"/> | 41 | <addaction name="actionZoom"/> |
| 42 | + <addaction name="actionCursors"/> | ||
| 43 | + <addaction name="separator"/> | ||
| 42 | <addaction name="actionManualCommand"/> | 44 | <addaction name="actionManualCommand"/> |
| 43 | </widget> | 45 | </widget> |
| 44 | <widget class="QMenu" name="menuOscilloscope"> | 46 | <widget class="QMenu" name="menuOscilloscope"> |
| @@ -84,6 +86,7 @@ | @@ -84,6 +86,7 @@ | ||
| 84 | <addaction name="separator"/> | 86 | <addaction name="separator"/> |
| 85 | <addaction name="actionDigital_phosphor"/> | 87 | <addaction name="actionDigital_phosphor"/> |
| 86 | <addaction name="actionZoom"/> | 88 | <addaction name="actionZoom"/> |
| 89 | + <addaction name="actionCursors"/> | ||
| 87 | <addaction name="separator"/> | 90 | <addaction name="separator"/> |
| 88 | </widget> | 91 | </widget> |
| 89 | <action name="actionOpen"> | 92 | <action name="actionOpen"> |
| @@ -167,6 +170,14 @@ | @@ -167,6 +170,14 @@ | ||
| 167 | <string>Manual command</string> | 170 | <string>Manual command</string> |
| 168 | </property> | 171 | </property> |
| 169 | </action> | 172 | </action> |
| 173 | + <action name="actionCursors"> | ||
| 174 | + <property name="checkable"> | ||
| 175 | + <bool>true</bool> | ||
| 176 | + </property> | ||
| 177 | + <property name="text"> | ||
| 178 | + <string>Cursors</string> | ||
| 179 | + </property> | ||
| 180 | + </action> | ||
| 170 | </widget> | 181 | </widget> |
| 171 | <resources/> | 182 | <resources/> |
| 172 | <connections/> | 183 | <connections/> |
openhantek/src/scopesettings.h
| @@ -3,6 +3,7 @@ | @@ -3,6 +3,7 @@ | ||
| 3 | #pragma once | 3 | #pragma once |
| 4 | 4 | ||
| 5 | #include <QString> | 5 | #include <QString> |
| 6 | +#include <QPointF> | ||
| 6 | 7 | ||
| 7 | #include "hantekdso/controlspecification.h" | 8 | #include "hantekdso/controlspecification.h" |
| 8 | #include "hantekdso/enums.h" | 9 | #include "hantekdso/enums.h" |
| @@ -12,12 +13,22 @@ | @@ -12,12 +13,22 @@ | ||
| 12 | #define MARKER_COUNT 2 ///< Number of markers | 13 | #define MARKER_COUNT 2 ///< Number of markers |
| 13 | #define MARKER_STEP (DIVS_TIME / 100.0) | 14 | #define MARKER_STEP (DIVS_TIME / 100.0) |
| 14 | 15 | ||
| 16 | +/// \brief Holds the cursor parameters | ||
| 17 | +struct DsoSettingsScopeCursor { | ||
| 18 | + enum CursorShape { | ||
| 19 | + NONE, | ||
| 20 | + HORIZONTAL, | ||
| 21 | + VERTICAL, | ||
| 22 | + RECTANGULAR | ||
| 23 | + } shape = NONE; | ||
| 24 | + QPointF pos[MARKER_COUNT] = {{-1.0, -1.0}, {1.0, 1.0}}; ///< Position in div | ||
| 25 | +}; | ||
| 26 | + | ||
| 15 | /// \brief Holds the settings for the horizontal axis. | 27 | /// \brief Holds the settings for the horizontal axis. |
| 16 | struct DsoSettingsScopeHorizontal { | 28 | struct DsoSettingsScopeHorizontal { |
| 17 | Dso::GraphFormat format = Dso::GraphFormat::TY; ///< Graph drawing mode of the scope | 29 | Dso::GraphFormat format = Dso::GraphFormat::TY; ///< Graph drawing mode of the scope |
| 18 | double frequencybase = 1e3; ///< Frequencybase in Hz/div | 30 | double frequencybase = 1e3; ///< Frequencybase in Hz/div |
| 19 | - double marker[MARKER_COUNT] = {-1.0, 1.0}; ///< Marker positions in div | ||
| 20 | - bool marker_visible[MARKER_COUNT] = {true, true}; | 31 | + DsoSettingsScopeCursor cursor; |
| 21 | 32 | ||
| 22 | unsigned int recordLength = 0; ///< Sample count | 33 | unsigned int recordLength = 0; ///< Sample count |
| 23 | 34 | ||
| @@ -39,25 +50,27 @@ struct DsoSettingsScopeTrigger { | @@ -39,25 +50,27 @@ struct DsoSettingsScopeTrigger { | ||
| 39 | unsigned swTriggerSampleSet = 11; ///< Software trigger, sample set | 50 | unsigned swTriggerSampleSet = 11; ///< Software trigger, sample set |
| 40 | }; | 51 | }; |
| 41 | 52 | ||
| 53 | +/// \brief Base for DsoSettingsScopeSpectrum and DsoSettingsScopeVoltage | ||
| 54 | +struct DsoSettingsScopeChannel { | ||
| 55 | + QString name; ///< Name of this channel | ||
| 56 | + bool used = false; ///< true if the channel is turned on | ||
| 57 | + DsoSettingsScopeCursor cursor; | ||
| 58 | +}; | ||
| 59 | + | ||
| 42 | /// \brief Holds the settings for the spectrum analysis. | 60 | /// \brief Holds the settings for the spectrum analysis. |
| 43 | -struct DsoSettingsScopeSpectrum { | ||
| 44 | - ChannelID channel; | ||
| 45 | - double magnitude = 20.0; ///< The vertical resolution in dB/div | ||
| 46 | - QString name; ///< Name of this channel | 61 | +struct DsoSettingsScopeSpectrum : public DsoSettingsScopeChannel { |
| 47 | double offset = 0.0; ///< Vertical offset in divs | 62 | double offset = 0.0; ///< Vertical offset in divs |
| 48 | - bool used = false; ///< true if the spectrum is turned on | 63 | + double magnitude = 20.0; ///< The vertical resolution in dB/div |
| 49 | }; | 64 | }; |
| 50 | 65 | ||
| 51 | /// \brief Holds the settings for the normal voltage graphs. | 66 | /// \brief Holds the settings for the normal voltage graphs. |
| 52 | /// TODO Use ControlSettingsVoltage | 67 | /// TODO Use ControlSettingsVoltage |
| 53 | -struct DsoSettingsScopeVoltage { | 68 | +struct DsoSettingsScopeVoltage : public DsoSettingsScopeChannel { |
| 54 | double offset = 0.0; ///< Vertical offset in divs | 69 | double offset = 0.0; ///< Vertical offset in divs |
| 55 | double trigger = 0.0; ///< Trigger level in V | 70 | double trigger = 0.0; ///< Trigger level in V |
| 56 | unsigned gainStepIndex = 6; ///< The vertical resolution in V/div (default = 1.0) | 71 | unsigned gainStepIndex = 6; ///< The vertical resolution in V/div (default = 1.0) |
| 57 | unsigned couplingOrMathIndex = 0; ///< Different index: coupling for real- and mode for math-channels | 72 | unsigned couplingOrMathIndex = 0; ///< Different index: coupling for real- and mode for math-channels |
| 58 | - QString name; ///< Name of this channel | ||
| 59 | bool inverted = false; ///< true if the channel is inverted (mirrored on cross-axis) | 73 | bool inverted = false; ///< true if the channel is inverted (mirrored on cross-axis) |
| 60 | - bool used = false; ///< true if this channel is enabled | ||
| 61 | }; | 74 | }; |
| 62 | 75 | ||
| 63 | /// \brief Holds the settings for the oscilloscope. | 76 | /// \brief Holds the settings for the oscilloscope. |
| @@ -77,4 +90,11 @@ struct DsoSettingsScope { | @@ -77,4 +90,11 @@ struct DsoSettingsScope { | ||
| 77 | } | 90 | } |
| 78 | // Channels, including math channels | 91 | // Channels, including math channels |
| 79 | unsigned countChannels() const { return (unsigned)voltage.size(); } | 92 | unsigned countChannels() const { return (unsigned)voltage.size(); } |
| 93 | + | ||
| 94 | + double getMarker(unsigned int marker) const { | ||
| 95 | + return marker < MARKER_COUNT ? horizontal.cursor.pos[marker].x() : 0.0; | ||
| 96 | + } | ||
| 97 | + void setMarker(unsigned int marker, double value) { | ||
| 98 | + if (marker < MARKER_COUNT) horizontal.cursor.pos[marker].setX(value); | ||
| 99 | + } | ||
| 80 | }; | 100 | }; |
openhantek/src/settings.cpp
| @@ -75,10 +75,10 @@ void DsoSettings::load() { | @@ -75,10 +75,10 @@ void DsoSettings::load() { | ||
| 75 | if (store->contains("format")) scope.horizontal.format = (Dso::GraphFormat)store->value("format").toInt(); | 75 | if (store->contains("format")) scope.horizontal.format = (Dso::GraphFormat)store->value("format").toInt(); |
| 76 | if (store->contains("frequencybase")) | 76 | if (store->contains("frequencybase")) |
| 77 | scope.horizontal.frequencybase = store->value("frequencybase").toDouble(); | 77 | scope.horizontal.frequencybase = store->value("frequencybase").toDouble(); |
| 78 | - for (int marker = 0; marker < 2; ++marker) { | 78 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { |
| 79 | QString name; | 79 | QString name; |
| 80 | name = QString("marker%1").arg(marker); | 80 | name = QString("marker%1").arg(marker); |
| 81 | - if (store->contains(name)) scope.horizontal.marker[marker] = store->value(name).toDouble(); | 81 | + if (store->contains(name)) scope.setMarker(marker, store->value(name).toDouble()); |
| 82 | } | 82 | } |
| 83 | if (store->contains("timebase")) scope.horizontal.timebase = store->value("timebase").toDouble(); | 83 | if (store->contains("timebase")) scope.horizontal.timebase = store->value("timebase").toDouble(); |
| 84 | if (store->contains("recordLength")) scope.horizontal.recordLength = store->value("recordLength").toUInt(); | 84 | if (store->contains("recordLength")) scope.horizontal.recordLength = store->value("recordLength").toUInt(); |
| @@ -100,6 +100,17 @@ void DsoSettings::load() { | @@ -100,6 +100,17 @@ void DsoSettings::load() { | ||
| 100 | scope.spectrum[channel].magnitude = store->value("magnitude").toDouble(); | 100 | scope.spectrum[channel].magnitude = store->value("magnitude").toDouble(); |
| 101 | if (store->contains("offset")) scope.spectrum[channel].offset = store->value("offset").toDouble(); | 101 | if (store->contains("offset")) scope.spectrum[channel].offset = store->value("offset").toDouble(); |
| 102 | if (store->contains("used")) scope.spectrum[channel].used = store->value("used").toBool(); | 102 | if (store->contains("used")) scope.spectrum[channel].used = store->value("used").toBool(); |
| 103 | + store->beginGroup("cursor"); | ||
| 104 | + if (store->contains("shape")) scope.spectrum[channel].cursor.shape = | ||
| 105 | + DsoSettingsScopeCursor::CursorShape(store->value("shape").toUInt()); | ||
| 106 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | ||
| 107 | + QString name; | ||
| 108 | + name = QString("x%1").arg(marker); | ||
| 109 | + if (store->contains(name)) scope.spectrum[channel].cursor.pos[marker].setX(store->value(name).toDouble()); | ||
| 110 | + name = QString("y%1").arg(marker); | ||
| 111 | + if (store->contains(name)) scope.spectrum[channel].cursor.pos[marker].setY(store->value(name).toDouble()); | ||
| 112 | + } | ||
| 113 | + store->endGroup(); | ||
| 103 | store->endGroup(); | 114 | store->endGroup(); |
| 104 | } | 115 | } |
| 105 | // Vertical axis | 116 | // Vertical axis |
| @@ -112,6 +123,17 @@ void DsoSettings::load() { | @@ -112,6 +123,17 @@ void DsoSettings::load() { | ||
| 112 | if (store->contains("offset")) scope.voltage[channel].offset = store->value("offset").toDouble(); | 123 | if (store->contains("offset")) scope.voltage[channel].offset = store->value("offset").toDouble(); |
| 113 | if (store->contains("trigger")) scope.voltage[channel].trigger = store->value("trigger").toDouble(); | 124 | if (store->contains("trigger")) scope.voltage[channel].trigger = store->value("trigger").toDouble(); |
| 114 | if (store->contains("used")) scope.voltage[channel].used = store->value("used").toBool(); | 125 | if (store->contains("used")) scope.voltage[channel].used = store->value("used").toBool(); |
| 126 | + store->beginGroup("cursor"); | ||
| 127 | + if (store->contains("shape")) scope.voltage[channel].cursor.shape = | ||
| 128 | + DsoSettingsScopeCursor::CursorShape(store->value("shape").toUInt()); | ||
| 129 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | ||
| 130 | + QString name; | ||
| 131 | + name = QString("x%1").arg(marker); | ||
| 132 | + if (store->contains(name)) scope.voltage[channel].cursor.pos[marker].setX(store->value(name).toDouble()); | ||
| 133 | + name = QString("y%1").arg(marker); | ||
| 134 | + if (store->contains(name)) scope.voltage[channel].cursor.pos[marker].setY(store->value(name).toDouble()); | ||
| 135 | + } | ||
| 136 | + store->endGroup(); | ||
| 115 | store->endGroup(); | 137 | store->endGroup(); |
| 116 | } | 138 | } |
| 117 | 139 | ||
| @@ -159,7 +181,10 @@ void DsoSettings::load() { | @@ -159,7 +181,10 @@ void DsoSettings::load() { | ||
| 159 | if (store->contains("interpolation")) | 181 | if (store->contains("interpolation")) |
| 160 | view.interpolation = (Dso::InterpolationMode)store->value("interpolation").toInt(); | 182 | view.interpolation = (Dso::InterpolationMode)store->value("interpolation").toInt(); |
| 161 | if (store->contains("screenColorImages")) view.screenColorImages = store->value("screenColorImages").toBool(); | 183 | if (store->contains("screenColorImages")) view.screenColorImages = store->value("screenColorImages").toBool(); |
| 162 | - if (store->contains("zoom")) view.zoom = (Dso::InterpolationMode)store->value("zoom").toBool(); | 184 | + if (store->contains("zoom")) view.zoom = store->value("zoom").toBool(); |
| 185 | + if (store->contains("cursorGridPosition")) | ||
| 186 | + view.cursorGridPosition = (Qt::ToolBarArea)store->value("cursorGridPosition").toUInt(); | ||
| 187 | + if (store->contains("cursorsVisible")) view.cursorsVisible = store->value("cursorsVisible").toBool(); | ||
| 163 | store->endGroup(); | 188 | store->endGroup(); |
| 164 | 189 | ||
| 165 | store->beginGroup("window"); | 190 | store->beginGroup("window"); |
| @@ -184,8 +209,8 @@ void DsoSettings::save() { | @@ -184,8 +209,8 @@ void DsoSettings::save() { | ||
| 184 | store->beginGroup("horizontal"); | 209 | store->beginGroup("horizontal"); |
| 185 | store->setValue("format", scope.horizontal.format); | 210 | store->setValue("format", scope.horizontal.format); |
| 186 | store->setValue("frequencybase", scope.horizontal.frequencybase); | 211 | store->setValue("frequencybase", scope.horizontal.frequencybase); |
| 187 | - for (int marker = 0; marker < 2; ++marker) | ||
| 188 | - store->setValue(QString("marker%1").arg(marker), scope.horizontal.marker[marker]); | 212 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) |
| 213 | + store->setValue(QString("marker%1").arg(marker), scope.getMarker(marker)); | ||
| 189 | store->setValue("timebase", scope.horizontal.timebase); | 214 | store->setValue("timebase", scope.horizontal.timebase); |
| 190 | store->setValue("recordLength", scope.horizontal.recordLength); | 215 | store->setValue("recordLength", scope.horizontal.recordLength); |
| 191 | store->setValue("samplerate", scope.horizontal.samplerate); | 216 | store->setValue("samplerate", scope.horizontal.samplerate); |
| @@ -205,6 +230,16 @@ void DsoSettings::save() { | @@ -205,6 +230,16 @@ void DsoSettings::save() { | ||
| 205 | store->setValue("magnitude", scope.spectrum[channel].magnitude); | 230 | store->setValue("magnitude", scope.spectrum[channel].magnitude); |
| 206 | store->setValue("offset", scope.spectrum[channel].offset); | 231 | store->setValue("offset", scope.spectrum[channel].offset); |
| 207 | store->setValue("used", scope.spectrum[channel].used); | 232 | store->setValue("used", scope.spectrum[channel].used); |
| 233 | + store->beginGroup("cursor"); | ||
| 234 | + store->setValue("shape", scope.spectrum[channel].cursor.shape); | ||
| 235 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | ||
| 236 | + QString name; | ||
| 237 | + name = QString("x%1").arg(marker); | ||
| 238 | + store->setValue(name, scope.spectrum[channel].cursor.pos[marker].x()); | ||
| 239 | + name = QString("y%1").arg(marker); | ||
| 240 | + store->setValue(name, scope.spectrum[channel].cursor.pos[marker].y()); | ||
| 241 | + } | ||
| 242 | + store->endGroup(); | ||
| 208 | store->endGroup(); | 243 | store->endGroup(); |
| 209 | } | 244 | } |
| 210 | // Vertical axis | 245 | // Vertical axis |
| @@ -216,6 +251,16 @@ void DsoSettings::save() { | @@ -216,6 +251,16 @@ void DsoSettings::save() { | ||
| 216 | store->setValue("offset", scope.voltage[channel].offset); | 251 | store->setValue("offset", scope.voltage[channel].offset); |
| 217 | store->setValue("trigger", scope.voltage[channel].trigger); | 252 | store->setValue("trigger", scope.voltage[channel].trigger); |
| 218 | store->setValue("used", scope.voltage[channel].used); | 253 | store->setValue("used", scope.voltage[channel].used); |
| 254 | + store->beginGroup("cursor"); | ||
| 255 | + store->setValue("shape", scope.voltage[channel].cursor.shape); | ||
| 256 | + for (int marker = 0; marker < MARKER_COUNT; ++marker) { | ||
| 257 | + QString name; | ||
| 258 | + name = QString("x%1").arg(marker); | ||
| 259 | + store->setValue(name, scope.voltage[channel].cursor.pos[marker].x()); | ||
| 260 | + name = QString("y%1").arg(marker); | ||
| 261 | + store->setValue(name, scope.voltage[channel].cursor.pos[marker].y()); | ||
| 262 | + } | ||
| 263 | + store->endGroup(); | ||
| 219 | store->endGroup(); | 264 | store->endGroup(); |
| 220 | } | 265 | } |
| 221 | 266 | ||
| @@ -259,6 +304,8 @@ void DsoSettings::save() { | @@ -259,6 +304,8 @@ void DsoSettings::save() { | ||
| 259 | store->setValue("interpolation", view.interpolation); | 304 | store->setValue("interpolation", view.interpolation); |
| 260 | store->setValue("screenColorImages", view.screenColorImages); | 305 | store->setValue("screenColorImages", view.screenColorImages); |
| 261 | store->setValue("zoom", view.zoom); | 306 | store->setValue("zoom", view.zoom); |
| 307 | + store->setValue("cursorGridPosition", view.cursorGridPosition); | ||
| 308 | + store->setValue("cursorsVisible", view.cursorsVisible); | ||
| 262 | store->endGroup(); | 309 | store->endGroup(); |
| 263 | 310 | ||
| 264 | store->beginGroup("window"); | 311 | store->beginGroup("window"); |
openhantek/src/viewsettings.h
| @@ -42,6 +42,8 @@ struct DsoSettingsView { | @@ -42,6 +42,8 @@ struct DsoSettingsView { | ||
| 42 | Dso::InterpolationMode interpolation = Dso::INTERPOLATION_LINEAR; ///< Interpolation mode for the graph | 42 | Dso::InterpolationMode interpolation = Dso::INTERPOLATION_LINEAR; ///< Interpolation mode for the graph |
| 43 | bool screenColorImages = false; ///< true exports images with screen colors | 43 | bool screenColorImages = false; ///< true exports images with screen colors |
| 44 | bool zoom = false; ///< true if the magnified scope is enabled | 44 | bool zoom = false; ///< true if the magnified scope is enabled |
| 45 | + Qt::ToolBarArea cursorGridPosition = Qt::RightToolBarArea; | ||
| 46 | + bool cursorsVisible = false; | ||
| 45 | 47 | ||
| 46 | unsigned digitalPhosphorDraws() const { | 48 | unsigned digitalPhosphorDraws() const { |
| 47 | return digitalPhosphor ? digitalPhosphorDepth : 1; | 49 | return digitalPhosphor ? digitalPhosphorDepth : 1; |
openhantek/src/widgets/datagrid.cpp
0 → 100644
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | + | ||
| 3 | +#include "datagrid.h" | ||
| 4 | + | ||
| 5 | +#include <QGridLayout> | ||
| 6 | +#include <QLabel> | ||
| 7 | +#include <QPushButton> | ||
| 8 | +#include <QButtonGroup> | ||
| 9 | + | ||
| 10 | +DataGrid::DataGrid(QWidget *parent) : QGroupBox(parent) | ||
| 11 | +{ | ||
| 12 | + cursorsLayout = new QGridLayout(); | ||
| 13 | + cursorsLayout->setSpacing(5); | ||
| 14 | + cursorsSelectorGroup = new QButtonGroup(); | ||
| 15 | + cursorsSelectorGroup->setExclusive(true); | ||
| 16 | + | ||
| 17 | + connect(cursorsSelectorGroup, | ||
| 18 | + static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonPressed), [this] (int index) { | ||
| 19 | + emit itemSelected(index); | ||
| 20 | + }); | ||
| 21 | + | ||
| 22 | + setLayout(cursorsLayout); | ||
| 23 | + setFixedWidth(180); | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +DataGrid::CursorInfo::CursorInfo() { | ||
| 27 | + selector = new QPushButton(); | ||
| 28 | + selector->setCheckable(true); | ||
| 29 | + shape = new QPushButton(); | ||
| 30 | + deltaXLabel = new QLabel(); | ||
| 31 | + deltaXLabel->setAlignment(Qt::AlignRight); | ||
| 32 | + deltaYLabel = new QLabel(); | ||
| 33 | + deltaYLabel->setAlignment(Qt::AlignRight); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +void DataGrid::CursorInfo::configure(const QString &text, const QColor &bgColor, const QColor &fgColor) { | ||
| 37 | + palette.setColor(QPalette::Background, bgColor); | ||
| 38 | + palette.setColor(QPalette::WindowText, fgColor); | ||
| 39 | + | ||
| 40 | + selector->setText(text); | ||
| 41 | + selector->setStyleSheet(QString(R"( | ||
| 42 | + QPushButton { | ||
| 43 | + color: %2; | ||
| 44 | + background-color: %1; | ||
| 45 | + border: 1px solid %2; | ||
| 46 | + } | ||
| 47 | + QPushButton:checked { | ||
| 48 | + color: %1; | ||
| 49 | + background-color: %2; | ||
| 50 | + } | ||
| 51 | + QPushButton:disabled { | ||
| 52 | + color: %3; | ||
| 53 | + border: 1px dotted %2; | ||
| 54 | + } | ||
| 55 | + )").arg(bgColor.name(QColor::HexArgb)) | ||
| 56 | + .arg(fgColor.name(QColor::HexArgb)) | ||
| 57 | + .arg(fgColor.darker().name(QColor::HexArgb))); | ||
| 58 | + | ||
| 59 | + shape->setStyleSheet(QString(R"( | ||
| 60 | + QPushButton { | ||
| 61 | + color: %2; | ||
| 62 | + background-color: %1; | ||
| 63 | + border: none | ||
| 64 | + } | ||
| 65 | + )").arg(bgColor.name(QColor::HexArgb)) | ||
| 66 | + .arg(fgColor.name(QColor::HexArgb))); | ||
| 67 | + | ||
| 68 | + deltaXLabel->setPalette(palette); | ||
| 69 | + deltaYLabel->setPalette(palette); | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +void DataGrid::setBackgroundColor(const QColor &bgColor) { | ||
| 73 | + backgroundColor = bgColor; | ||
| 74 | + for (auto it : items) { | ||
| 75 | + it.configure(it.selector->text(), bgColor, it.palette.color(QPalette::WindowText)); | ||
| 76 | + } | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +void DataGrid::configureItem(unsigned index, const QColor &fgColor) { | ||
| 80 | + if (index < items.size()) { | ||
| 81 | + items[index].configure(items[index].selector->text(), backgroundColor, fgColor); | ||
| 82 | + } | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +unsigned DataGrid::addItem(const QString &text, const QColor &fgColor) { | ||
| 86 | + unsigned index = items.size(); | ||
| 87 | + items.resize(index + 1); | ||
| 88 | + | ||
| 89 | + CursorInfo& info = items.at(index); | ||
| 90 | + info.configure(text, backgroundColor, fgColor); | ||
| 91 | + cursorsSelectorGroup->addButton(info.selector, index); | ||
| 92 | + | ||
| 93 | + connect(info.shape, &QPushButton::clicked, [this, index] () { | ||
| 94 | + emit itemUpdated(index); | ||
| 95 | + }); | ||
| 96 | + | ||
| 97 | + cursorsLayout->addWidget(info.selector, 3 * index, 0); | ||
| 98 | + cursorsLayout->addWidget(info.shape, 3 * index, 1); | ||
| 99 | + cursorsLayout->addWidget(info.deltaXLabel, 3 * index + 1, 0); | ||
| 100 | + cursorsLayout->addWidget(info.deltaYLabel, 3 * index + 1, 1); | ||
| 101 | + cursorsLayout->setRowMinimumHeight(3 * index + 2, 10); | ||
| 102 | + cursorsLayout->setRowStretch(3 * index, 0); | ||
| 103 | + cursorsLayout->setRowStretch(3 * index + 3, 1); | ||
| 104 | + | ||
| 105 | + return index; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +void DataGrid::updateInfo(unsigned index, bool visible, const QString &strShape, const QString &strX, const QString &strY) { | ||
| 109 | + if (index >= items.size()) return; | ||
| 110 | + CursorInfo &info = items.at(index); | ||
| 111 | + info.selector->setEnabled(visible); | ||
| 112 | + if (visible) { | ||
| 113 | + info.shape->setText(strShape); | ||
| 114 | + info.deltaXLabel->setText(strX); | ||
| 115 | + info.deltaYLabel->setText(strY); | ||
| 116 | + } else { | ||
| 117 | + info.shape->setText(QString()); | ||
| 118 | + info.deltaXLabel->setText(QString()); | ||
| 119 | + info.deltaYLabel->setText(QString()); | ||
| 120 | + } | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +void DataGrid::selectItem(unsigned index) { | ||
| 124 | + if (index >= items.size()) return; | ||
| 125 | + items[index].selector->setChecked(true); | ||
| 126 | +} |
openhantek/src/widgets/datagrid.h
0 → 100644
| 1 | +// SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | + | ||
| 3 | +#pragma once | ||
| 4 | + | ||
| 5 | +#include <QGroupBox> | ||
| 6 | +#include <QPalette> | ||
| 7 | + | ||
| 8 | +class QPushButton; | ||
| 9 | +class QButtonGroup; | ||
| 10 | +class QLabel; | ||
| 11 | +class QGridLayout; | ||
| 12 | + | ||
| 13 | +class DataGrid : public QGroupBox | ||
| 14 | +{ | ||
| 15 | + Q_OBJECT | ||
| 16 | +public: | ||
| 17 | + explicit DataGrid(QWidget *parent = nullptr); | ||
| 18 | + | ||
| 19 | + struct CursorInfo { | ||
| 20 | + QPalette palette; ///< The widget's palette | ||
| 21 | + QPushButton *selector; ///< The name of the channel | ||
| 22 | + QPushButton *shape; ///< The cursor shape | ||
| 23 | + QLabel *deltaXLabel; ///< The horizontal distance between cursors | ||
| 24 | + QLabel *deltaYLabel; ///< The vertical distance between cursors | ||
| 25 | + | ||
| 26 | + CursorInfo(); | ||
| 27 | + void configure(const QString &text, const QColor &bgColor, const QColor &fgColor); | ||
| 28 | + }; | ||
| 29 | + | ||
| 30 | + unsigned addItem(const QString &text, const QColor &fgColor); | ||
| 31 | + void setBackgroundColor(const QColor &bgColor); | ||
| 32 | + void configureItem(unsigned index, const QColor &fgColor); | ||
| 33 | + void updateInfo(unsigned index, bool visible, const QString &strShape = QString(), | ||
| 34 | + const QString &strX = QString(), const QString &strY = QString()); | ||
| 35 | + | ||
| 36 | +signals: | ||
| 37 | + void itemSelected(unsigned index); | ||
| 38 | + void itemUpdated(unsigned index); | ||
| 39 | + | ||
| 40 | +public slots: | ||
| 41 | + void selectItem(unsigned index); | ||
| 42 | + | ||
| 43 | +private: | ||
| 44 | + QColor backgroundColor; | ||
| 45 | + QButtonGroup *cursorsSelectorGroup; | ||
| 46 | + QGridLayout *cursorsLayout; | ||
| 47 | + std::vector<CursorInfo> items; | ||
| 48 | +}; |
openhantek/src/widgets/sispinbox.cpp
| @@ -160,18 +160,19 @@ void SiSpinBox::setMode(const int mode) { this->mode = mode; } | @@ -160,18 +160,19 @@ void SiSpinBox::setMode(const int mode) { this->mode = mode; } | ||
| 160 | 160 | ||
| 161 | /// \brief Generic initializations. | 161 | /// \brief Generic initializations. |
| 162 | void SiSpinBox::init() { | 162 | void SiSpinBox::init() { |
| 163 | - this->setMinimum(1e-12); | ||
| 164 | - this->setMaximum(1e12); | ||
| 165 | - this->setValue(1.0); | ||
| 166 | - this->setDecimals(DBL_MAX_10_EXP + DBL_DIG); // Disable automatic rounding | ||
| 167 | - this->unit = unit; | ||
| 168 | - this->steps << 1.0 << 2.0 << 5.0 << 10.0; | ||
| 169 | - | ||
| 170 | - this->steppedTo = false; | ||
| 171 | - this->stepId = 0; | ||
| 172 | - this->mode = 0; | ||
| 173 | - | ||
| 174 | - connect(this, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, &SiSpinBox::resetSteppedTo); | 163 | + setMinimum(1e-12); |
| 164 | + setMaximum(1e12); | ||
| 165 | + setValue(1.0); | ||
| 166 | + setDecimals(DBL_MAX_10_EXP + DBL_DIG); // Disable automatic rounding | ||
| 167 | + setFocusPolicy(Qt::NoFocus); | ||
| 168 | + steps << 1.0 << 2.0 << 5.0 << 10.0; | ||
| 169 | + | ||
| 170 | + steppedTo = false; | ||
| 171 | + stepId = 0; | ||
| 172 | + mode = 0; | ||
| 173 | + | ||
| 174 | + connect(this, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), | ||
| 175 | + this, &SiSpinBox::resetSteppedTo); | ||
| 175 | } | 176 | } |
| 176 | 177 | ||
| 177 | /// \brief Resets the ::steppedTo flag after the value has been changed. | 178 | /// \brief Resets the ::steppedTo flag after the value has been changed. |