File indexing completed on 2024-04-14 04:47:02
0001 /* 0002 SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "abstractscopewidget.h" 0009 0010 #include "monitor/monitor.h" 0011 0012 #include <QColor> 0013 #include <QMouseEvent> 0014 #include <QPainter> 0015 #include <QtConcurrent> 0016 0017 #include "klocalizedstring.h" 0018 #include <KConfigGroup> 0019 #include <KSharedConfig> 0020 #include <cmath> 0021 // Uncomment for Scope debugging. 0022 //#define DEBUG_ASW 0023 0024 #ifdef DEBUG_ASW 0025 #include "kdenlive_debug.h" 0026 #endif 0027 0028 const int REALTIME_FPS = 30; 0029 0030 const QColor light(250, 238, 226, 255); 0031 const QColor dark(40, 40, 39, 255); 0032 const QColor dark2(25, 25, 23, 255); 0033 const QColor AbstractScopeWidget::colHighlightLight(18, 130, 255, 255); 0034 const QColor AbstractScopeWidget::colHighlightDark(255, 64, 19, 255); 0035 const QColor AbstractScopeWidget::colDarkWhite(250, 250, 250); 0036 0037 const QPen AbstractScopeWidget::penThick(QBrush(AbstractScopeWidget::colDarkWhite.rgb()), 2, Qt::SolidLine); 0038 const QPen AbstractScopeWidget::penThin(QBrush(AbstractScopeWidget::colDarkWhite.rgb()), 1, Qt::SolidLine); 0039 const QPen AbstractScopeWidget::penLight(QBrush(QColor(200, 200, 250, 150)), 1, Qt::SolidLine); 0040 const QPen AbstractScopeWidget::penLightDots(QBrush(QColor(200, 200, 250, 150)), 1, Qt::DotLine); 0041 const QPen AbstractScopeWidget::penLighter(QBrush(QColor(225, 225, 250, 225)), 1, Qt::SolidLine); 0042 const QPen AbstractScopeWidget::penDark(QBrush(QColor(0, 0, 20, 250)), 1, Qt::SolidLine); 0043 const QPen AbstractScopeWidget::penDarkDots(QBrush(QColor(0, 0, 20, 250)), 1, Qt::DotLine); 0044 const QPen AbstractScopeWidget::penBackground(QBrush(dark2), 1, Qt::SolidLine); 0045 0046 const QString AbstractScopeWidget::directions[] = {QStringLiteral("North"), QStringLiteral("Northeast"), QStringLiteral("East"), QStringLiteral("Southeast")}; 0047 0048 AbstractScopeWidget::AbstractScopeWidget(bool trackMouse, QWidget *parent) 0049 : QWidget(parent) 0050 , m_scopePalette(QPalette()) 0051 , m_mousePos(0, 0) 0052 , m_semaphoreHUD(1) 0053 , m_semaphoreScope(1) 0054 , m_semaphoreBackground(1) 0055 0056 { 0057 m_scopePalette.setBrush(QPalette::Window, QBrush(dark2)); 0058 m_scopePalette.setBrush(QPalette::Base, QBrush(dark)); 0059 m_scopePalette.setBrush(QPalette::Button, QBrush(dark)); 0060 m_scopePalette.setBrush(QPalette::Text, QBrush(light)); 0061 m_scopePalette.setBrush(QPalette::WindowText, QBrush(light)); 0062 m_scopePalette.setBrush(QPalette::ButtonText, QBrush(light)); 0063 setPalette(m_scopePalette); 0064 setAutoFillBackground(true); 0065 0066 m_aAutoRefresh = new QAction(i18n("Auto Refresh"), this); 0067 m_aAutoRefresh->setCheckable(true); 0068 m_aRealtime = new QAction(i18n("Realtime (with precision loss)"), this); 0069 m_aRealtime->setCheckable(true); 0070 0071 m_menu = new QMenu(); 0072 // Disabled dark palette on menus since it breaks up with some themes: kdenlive issue #2950 0073 // m_menu->setPalette(m_scopePalette); 0074 m_menu->addAction(m_aAutoRefresh); 0075 m_menu->addAction(m_aRealtime); 0076 0077 setContextMenuPolicy(Qt::CustomContextMenu); 0078 0079 connect(this, &AbstractScopeWidget::customContextMenuRequested, this, &AbstractScopeWidget::slotContextMenuRequested); 0080 0081 connect(this, &AbstractScopeWidget::signalHUDRenderingFinished, this, &AbstractScopeWidget::slotHUDRenderingFinished); 0082 connect(this, &AbstractScopeWidget::signalScopeRenderingFinished, this, &AbstractScopeWidget::slotScopeRenderingFinished); 0083 connect(this, &AbstractScopeWidget::signalBackgroundRenderingFinished, this, &AbstractScopeWidget::slotBackgroundRenderingFinished); 0084 connect(m_aRealtime, &QAction::toggled, this, &AbstractScopeWidget::slotResetRealtimeFactor); 0085 connect(m_aAutoRefresh, &QAction::toggled, this, &AbstractScopeWidget::slotAutoRefreshToggled); 0086 0087 // Enable mouse tracking if desired. 0088 // Causes the mouseMoved signal to be emitted when the mouse moves inside the 0089 // widget, even when no mouse button is pressed. 0090 this->setMouseTracking(trackMouse); 0091 } 0092 0093 AbstractScopeWidget::~AbstractScopeWidget() 0094 { 0095 writeConfig(); 0096 0097 delete m_menu; 0098 delete m_aAutoRefresh; 0099 delete m_aRealtime; 0100 } 0101 0102 void AbstractScopeWidget::init() 0103 { 0104 m_widgetName = widgetName(); 0105 readConfig(); 0106 } 0107 0108 void AbstractScopeWidget::readConfig() 0109 { 0110 KSharedConfigPtr config = KSharedConfig::openConfig(); 0111 KConfigGroup scopeConfig(config, configName()); 0112 m_aAutoRefresh->setChecked(scopeConfig.readEntry("autoRefresh", true)); 0113 m_aRealtime->setChecked(scopeConfig.readEntry("realtime", false)); 0114 scopeConfig.sync(); 0115 } 0116 0117 void AbstractScopeWidget::writeConfig() 0118 { 0119 KSharedConfigPtr config = KSharedConfig::openConfig(); 0120 KConfigGroup scopeConfig(config, configName()); 0121 scopeConfig.writeEntry("autoRefresh", m_aAutoRefresh->isChecked()); 0122 scopeConfig.writeEntry("realtime", m_aRealtime->isChecked()); 0123 scopeConfig.sync(); 0124 } 0125 0126 QString AbstractScopeWidget::configName() 0127 { 0128 return "Scope_" + m_widgetName; 0129 } 0130 0131 void AbstractScopeWidget::prodHUDThread() 0132 { 0133 if (this->visibleRegion().isEmpty()) { 0134 #ifdef DEBUG_ASW 0135 qCDebug(KDENLIVE_LOG) << "Scope " << m_widgetName << " is not visible. Not calculating HUD."; 0136 #endif 0137 } else { 0138 if (m_semaphoreHUD.tryAcquire(1)) { 0139 Q_ASSERT(!m_threadHUD.isRunning()); 0140 0141 m_newHUDFrames.fetchAndStoreRelaxed(0); 0142 m_newHUDUpdates.fetchAndStoreRelaxed(0); 0143 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0144 m_threadHUD = QtConcurrent::run(this, &AbstractScopeWidget::renderHUD, m_accelFactorHUD); 0145 #else 0146 m_threadHUD = QtConcurrent::run(&AbstractScopeWidget::renderHUD, this, m_accelFactorHUD); 0147 #endif 0148 #ifdef DEBUG_ASW 0149 qCDebug(KDENLIVE_LOG) << "HUD thread started in " << m_widgetName; 0150 #endif 0151 0152 } 0153 #ifdef DEBUG_ASW 0154 else { 0155 qCDebug(KDENLIVE_LOG) << "HUD semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadHUD.isRunning(); 0156 } 0157 #endif 0158 } 0159 } 0160 0161 void AbstractScopeWidget::prodScopeThread() 0162 { 0163 // Only start a new thread if the scope is actually visible 0164 // and not hidden by another widget on the stack and if user want the scope to update. 0165 if (this->visibleRegion().isEmpty() || (!m_aAutoRefresh->isChecked() && !m_requestForcedUpdate)) { 0166 #ifdef DEBUG_ASW 0167 qCDebug(KDENLIVE_LOG) << "Scope " << m_widgetName << " is not visible. Not calculating scope."; 0168 #endif 0169 } else { 0170 // Try to acquire the semaphore. This must only succeed if m_threadScope is not running 0171 // anymore. Therefore the semaphore must NOT be released before m_threadScope ends. 0172 // If acquiring the semaphore fails, the thread is still running. 0173 if (m_semaphoreScope.tryAcquire(1)) { 0174 Q_ASSERT(!m_threadScope.isRunning()); 0175 0176 m_newScopeFrames.fetchAndStoreRelaxed(0); 0177 m_newScopeUpdates.fetchAndStoreRelaxed(0); 0178 0179 Q_ASSERT(m_accelFactorScope > 0); 0180 0181 // See https://doc.qt.io/qt-5/qtconcurrentrun.html about 0182 // running member functions in a thread 0183 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0184 m_threadScope = QtConcurrent::run(this, &AbstractScopeWidget::renderScope, m_accelFactorScope); 0185 #else 0186 m_threadScope = QtConcurrent::run(&AbstractScopeWidget::renderScope, this, m_accelFactorScope); 0187 #endif 0188 m_requestForcedUpdate = false; 0189 0190 #ifdef DEBUG_ASW 0191 qCDebug(KDENLIVE_LOG) << "Scope thread started in " << m_widgetName; 0192 #endif 0193 0194 } else { 0195 #ifdef DEBUG_ASW 0196 qCDebug(KDENLIVE_LOG) << "Scope semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadScope.isRunning(); 0197 #endif 0198 } 0199 } 0200 } 0201 void AbstractScopeWidget::prodBackgroundThread() 0202 { 0203 if (this->visibleRegion().isEmpty()) { 0204 #ifdef DEBUG_ASW 0205 qCDebug(KDENLIVE_LOG) << "Scope " << m_widgetName << " is not visible. Not calculating background."; 0206 #endif 0207 } else { 0208 if (m_semaphoreBackground.tryAcquire(1)) { 0209 Q_ASSERT(!m_threadBackground.isRunning()); 0210 0211 m_newBackgroundFrames.fetchAndStoreRelaxed(0); 0212 m_newBackgroundUpdates.fetchAndStoreRelaxed(0); 0213 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0214 m_threadBackground = QtConcurrent::run(this, &AbstractScopeWidget::renderBackground, m_accelFactorBackground); 0215 #else 0216 m_threadBackground = QtConcurrent::run(&AbstractScopeWidget::renderBackground, this, m_accelFactorBackground); 0217 #endif 0218 0219 #ifdef DEBUG_ASW 0220 qCDebug(KDENLIVE_LOG) << "Background thread started in " << m_widgetName; 0221 #endif 0222 0223 } else { 0224 #ifdef DEBUG_ASW 0225 qCDebug(KDENLIVE_LOG) << "Background semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadBackground.isRunning(); 0226 #endif 0227 } 0228 } 0229 } 0230 0231 void AbstractScopeWidget::forceUpdate(bool doUpdate) 0232 { 0233 #ifdef DEBUG_ASW 0234 qCDebug(KDENLIVE_LOG) << "Forced update called in " << widgetName() << ". Arg: " << doUpdate; 0235 #endif 0236 0237 if (!doUpdate) { 0238 return; 0239 } 0240 m_requestForcedUpdate = true; 0241 m_newHUDUpdates.fetchAndAddRelaxed(1); 0242 m_newScopeUpdates.fetchAndAddRelaxed(1); 0243 m_newBackgroundUpdates.fetchAndAddRelaxed(1); 0244 prodHUDThread(); 0245 prodScopeThread(); 0246 prodBackgroundThread(); 0247 } 0248 void AbstractScopeWidget::forceUpdateHUD() 0249 { 0250 m_newHUDUpdates.fetchAndAddRelaxed(1); 0251 prodHUDThread(); 0252 } 0253 void AbstractScopeWidget::forceUpdateScope() 0254 { 0255 m_newScopeUpdates.fetchAndAddRelaxed(1); 0256 m_requestForcedUpdate = true; 0257 prodScopeThread(); 0258 } 0259 void AbstractScopeWidget::forceUpdateBackground() 0260 { 0261 m_newBackgroundUpdates.fetchAndAddRelaxed(1); 0262 prodBackgroundThread(); 0263 } 0264 0265 ///// Events ///// 0266 0267 void AbstractScopeWidget::resizeEvent(QResizeEvent *event) 0268 { 0269 // Update the dimension of the available rect for painting 0270 m_scopeRect = scopeRect(); 0271 forceUpdate(); 0272 0273 QWidget::resizeEvent(event); 0274 } 0275 0276 void AbstractScopeWidget::showEvent(QShowEvent *event) 0277 { 0278 QWidget::showEvent(event); 0279 m_scopeRect = scopeRect(); 0280 } 0281 0282 void AbstractScopeWidget::paintEvent(QPaintEvent *) 0283 { 0284 QPainter davinci; 0285 bool ok = davinci.begin(this); 0286 if (!ok) { 0287 if (!m_scopeWarningPrinted) { 0288 qDebug() << "Warning: Could not initialise painter for drawing scope."; 0289 m_scopeWarningPrinted = true; 0290 } 0291 return; 0292 } 0293 davinci.drawImage(m_scopeRect.topLeft(), m_imgBackground); 0294 davinci.drawImage(m_scopeRect.topLeft(), m_imgScope); 0295 davinci.drawImage(m_scopeRect.topLeft(), m_imgHUD); 0296 } 0297 0298 void AbstractScopeWidget::mousePressEvent(QMouseEvent *event) 0299 { 0300 if (event->button() == Qt::LeftButton) { 0301 // Rescaling mode starts 0302 m_rescaleActive = true; 0303 m_rescalePropertiesLocked = false; 0304 m_rescaleFirstRescaleDone = false; 0305 m_rescaleStartPoint = event->pos(); 0306 m_rescaleModifiers = event->modifiers(); 0307 } 0308 } 0309 void AbstractScopeWidget::mouseReleaseEvent(QMouseEvent *event) 0310 { 0311 m_rescaleActive = false; 0312 m_rescalePropertiesLocked = false; 0313 0314 if (!m_aAutoRefresh->isChecked()) { 0315 m_requestForcedUpdate = true; 0316 } 0317 prodHUDThread(); 0318 prodScopeThread(); 0319 prodBackgroundThread(); 0320 QWidget::mouseReleaseEvent(event); 0321 } 0322 void AbstractScopeWidget::mouseMoveEvent(QMouseEvent *event) 0323 { 0324 m_mousePos = event->pos(); 0325 m_mouseWithinWidget = true; 0326 Q_EMIT signalMousePositionChanged(); 0327 0328 QPoint movement = event->pos() - m_rescaleStartPoint; 0329 0330 if (m_rescaleActive) { 0331 if (m_rescalePropertiesLocked) { 0332 // Direction is known, now adjust parameters 0333 0334 // Reset the starting point to make the next moveEvent relative to the current one 0335 m_rescaleStartPoint = event->pos(); 0336 0337 if (!m_rescaleFirstRescaleDone) { 0338 // We have just learned the desired direction; Normalize the movement to one pixel 0339 // to avoid a jump by m_rescaleMinDist 0340 0341 if (movement.x() != 0) { 0342 movement.setX(movement.x() / abs(movement.x())); 0343 } 0344 if (movement.y() != 0) { 0345 movement.setY(movement.y() / abs(movement.y())); 0346 } 0347 0348 m_rescaleFirstRescaleDone = true; 0349 } 0350 0351 handleMouseDrag(movement, m_rescaleDirection, m_rescaleModifiers); 0352 0353 } else { 0354 // Detect the movement direction here. 0355 // This algorithm relies on the aspect ratio of dy/dx (size and signum). 0356 if (movement.manhattanLength() > m_rescaleMinDist) { 0357 float diff = float(movement.y()) / movement.x(); 0358 0359 if (std::fabs(diff) > m_rescaleVerticalThreshold || movement.x() == 0) { 0360 m_rescaleDirection = North; 0361 } else if (std::fabs(diff) < 1 / m_rescaleVerticalThreshold) { 0362 m_rescaleDirection = East; 0363 } else if (diff < 0) { 0364 m_rescaleDirection = Northeast; 0365 } else { 0366 m_rescaleDirection = Southeast; 0367 } 0368 #ifdef DEBUG_ASW 0369 qCDebug(KDENLIVE_LOG) << "Diff is " << diff << "; chose " << directions[m_rescaleDirection] << " as direction"; 0370 #endif 0371 m_rescalePropertiesLocked = true; 0372 } 0373 } 0374 } 0375 } 0376 0377 void AbstractScopeWidget::leaveEvent(QEvent *) 0378 { 0379 m_mouseWithinWidget = false; 0380 Q_EMIT signalMousePositionChanged(); 0381 } 0382 0383 void AbstractScopeWidget::slotContextMenuRequested(const QPoint &pos) 0384 { 0385 m_menu->exec(this->mapToGlobal(pos)); 0386 } 0387 0388 uint AbstractScopeWidget::calculateAccelFactorHUD(uint oldMseconds, uint) 0389 { 0390 return uint(std::ceil(float(oldMseconds) * REALTIME_FPS / 1000.f)); 0391 } 0392 uint AbstractScopeWidget::calculateAccelFactorScope(uint oldMseconds, uint) 0393 { 0394 return uint(std::ceil(float(oldMseconds) * REALTIME_FPS / 1000.f)); 0395 } 0396 uint AbstractScopeWidget::calculateAccelFactorBackground(uint oldMseconds, uint) 0397 { 0398 return uint(std::ceil(float(oldMseconds) * REALTIME_FPS / 1000.f)); 0399 } 0400 0401 ///// Slots ///// 0402 0403 void AbstractScopeWidget::slotHUDRenderingFinished(uint mseconds, uint oldFactor) 0404 { 0405 #ifdef DEBUG_ASW 0406 qCDebug(KDENLIVE_LOG) << "HUD rendering has finished in " << mseconds << " ms, waiting for termination in " << m_widgetName; 0407 #endif 0408 m_threadHUD.waitForFinished(); 0409 m_imgHUD = m_threadHUD.result(); 0410 0411 m_semaphoreHUD.release(1); 0412 this->update(); 0413 0414 if (m_aRealtime->isChecked()) { 0415 int accel; 0416 accel = int(calculateAccelFactorHUD(mseconds, oldFactor)); 0417 if (m_accelFactorHUD < 1) { 0418 accel = 1; 0419 } 0420 m_accelFactorHUD = accel; 0421 } 0422 0423 if ((m_newHUDFrames > 0 && m_aAutoRefresh->isChecked()) || m_newHUDUpdates > 0) { 0424 #ifdef DEBUG_ASW 0425 qCDebug(KDENLIVE_LOG) << "Trying to start a new HUD thread for " << m_widgetName << ". New frames/updates: " << m_newHUDFrames << '/' 0426 << m_newHUDUpdates; 0427 #endif 0428 prodHUDThread(); 0429 } 0430 } 0431 0432 void AbstractScopeWidget::slotScopeRenderingFinished(uint mseconds, uint oldFactor) 0433 { 0434 // The signal can be received before the thread has really finished. So we 0435 // need to wait until it has really finished before starting a new thread. 0436 #ifdef DEBUG_ASW 0437 qCDebug(KDENLIVE_LOG) << "Scope rendering has finished in " << mseconds << " ms, waiting for termination in " << m_widgetName; 0438 #endif 0439 m_threadScope.waitForFinished(); 0440 m_imgScope = m_threadScope.result(); 0441 0442 // The scope thread has finished. Now we can release the semaphore, allowing a new thread. 0443 // See prodScopeThread where the semaphore is acquired again. 0444 m_semaphoreScope.release(1); 0445 this->update(); 0446 0447 // Calculate the acceleration factor hint to get «realtime» updates. 0448 if (m_aRealtime->isChecked()) { 0449 int accel; 0450 accel = int(calculateAccelFactorScope(mseconds, oldFactor)); 0451 if (accel < 1) { 0452 // If mseconds happens to be 0. 0453 accel = 1; 0454 } 0455 // Don't directly calculate with m_accelFactorScope as we are dealing with concurrency. 0456 // If m_accelFactorScope is set to 0 at the wrong moment, who knows what might happen 0457 // then :) Therefore use a local variable. 0458 m_accelFactorScope = accel; 0459 } 0460 0461 if ((m_newScopeFrames > 0 && m_aAutoRefresh->isChecked()) || m_newScopeUpdates > 0) { 0462 #ifdef DEBUG_ASW 0463 qCDebug(KDENLIVE_LOG) << "Trying to start a new scope thread for " << m_widgetName << ". New frames/updates: " << m_newScopeFrames << '/' 0464 << m_newScopeUpdates; 0465 #endif 0466 prodScopeThread(); 0467 } 0468 } 0469 0470 void AbstractScopeWidget::slotBackgroundRenderingFinished(uint mseconds, uint oldFactor) 0471 { 0472 #ifdef DEBUG_ASW 0473 qCDebug(KDENLIVE_LOG) << "Background rendering has finished in " << mseconds << " ms, waiting for termination in " << m_widgetName; 0474 #endif 0475 m_threadBackground.waitForFinished(); 0476 m_imgBackground = m_threadBackground.result(); 0477 0478 m_semaphoreBackground.release(1); 0479 this->update(); 0480 0481 if (m_aRealtime->isChecked()) { 0482 int accel; 0483 accel = int(calculateAccelFactorBackground(mseconds, oldFactor)); 0484 if (m_accelFactorBackground < 1) { 0485 accel = 1; 0486 } 0487 m_accelFactorBackground = accel; 0488 } 0489 0490 if ((m_newBackgroundFrames > 0 && m_aAutoRefresh->isChecked()) || m_newBackgroundUpdates > 0) { 0491 #ifdef DEBUG_ASW 0492 qCDebug(KDENLIVE_LOG) << "Trying to start a new background thread for " << m_widgetName << ". New frames/updates: " << m_newBackgroundFrames << '/' 0493 << m_newBackgroundUpdates; 0494 #endif 0495 prodBackgroundThread(); 0496 } 0497 } 0498 0499 void AbstractScopeWidget::slotRenderZoneUpdated() 0500 { 0501 m_newHUDFrames.fetchAndAddRelaxed(1); 0502 m_newScopeFrames.fetchAndAddRelaxed(1); 0503 m_newBackgroundFrames.fetchAndAddRelaxed(1); 0504 0505 #ifdef DEBUG_ASW 0506 qCDebug(KDENLIVE_LOG) << "Data incoming at " << widgetName() << ". New frames total HUD/Scope/Background: " << m_newHUDFrames << '/' << m_newScopeFrames 0507 << '/' << m_newBackgroundFrames; 0508 #endif 0509 0510 if (this->visibleRegion().isEmpty()) { 0511 #ifdef DEBUG_ASW 0512 qCDebug(KDENLIVE_LOG) << "Scope of widget " << m_widgetName << " is not at the top, not rendering."; 0513 #endif 0514 } else { 0515 if (m_aAutoRefresh->isChecked()) { 0516 prodHUDThread(); 0517 prodScopeThread(); 0518 prodBackgroundThread(); 0519 } 0520 } 0521 } 0522 0523 void AbstractScopeWidget::slotResetRealtimeFactor(bool realtimeChecked) 0524 { 0525 if (!realtimeChecked) { 0526 m_accelFactorHUD = 1; 0527 m_accelFactorScope = 1; 0528 m_accelFactorBackground = 1; 0529 } 0530 } 0531 0532 bool AbstractScopeWidget::autoRefreshEnabled() const 0533 { 0534 return m_aAutoRefresh->isChecked(); 0535 } 0536 0537 void AbstractScopeWidget::slotAutoRefreshToggled(bool autoRefresh) 0538 { 0539 #ifdef DEBUG_ASW 0540 qCDebug(KDENLIVE_LOG) << "Auto-refresh switched to " << autoRefresh << " in " << widgetName() << " (Visible: " << isVisible() << '/' 0541 << this->visibleRegion().isEmpty() << ')'; 0542 #endif 0543 if (isVisible()) { 0544 // Notify listeners whether we accept new frames now 0545 Q_EMIT requestAutoRefresh(autoRefresh); 0546 } 0547 // TODO only if depends on input 0548 if (autoRefresh) { 0549 // forceUpdate(); 0550 m_requestForcedUpdate = true; 0551 } 0552 } 0553 0554 void AbstractScopeWidget::handleMouseDrag(const QPoint &, const RescaleDirection, const Qt::KeyboardModifiers) {} 0555 0556 #ifdef DEBUG_ASW 0557 #undef DEBUG_ASW 0558 #endif