Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Kdenlive
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
259
Issues
259
List
Boards
Labels
Service Desk
Milestones
Merge Requests
14
Merge Requests
14
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Multimedia
Kdenlive
Commits
6cc358a0
Commit
6cc358a0
authored
Dec 03, 2010
by
Simon Eugster
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Made AbstractAudioScope and AbstractGfxScope inherit from AbstractScope; Done for audio
svn path=/trunk/kdenlive/; revision=5129
parent
399d340d
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
796 additions
and
652 deletions
+796
-652
src/CMakeLists.txt
src/CMakeLists.txt
+1
-0
src/abstractgfxscopewidget.cpp
src/abstractgfxscopewidget.cpp
+459
-0
src/abstractgfxscopewidget.h
src/abstractgfxscopewidget.h
+272
-0
src/abstractscopewidget.cpp
src/abstractscopewidget.cpp
+1
-1
src/abstractscopewidget.h
src/abstractscopewidget.h
+2
-2
src/audioscopes/abstractaudioscopewidget.cpp
src/audioscopes/abstractaudioscopewidget.cpp
+5
-428
src/audioscopes/abstractaudioscopewidget.h
src/audioscopes/abstractaudioscopewidget.h
+7
-189
src/audioscopes/audiospectrum.cpp
src/audioscopes/audiospectrum.cpp
+16
-9
src/audioscopes/audiospectrum.h
src/audioscopes/audiospectrum.h
+11
-1
src/histogram.cpp
src/histogram.cpp
+2
-2
src/histogram.h
src/histogram.h
+2
-2
src/mainwindow.cpp
src/mainwindow.cpp
+6
-6
src/rgbparade.cpp
src/rgbparade.cpp
+2
-2
src/rgbparade.h
src/rgbparade.h
+2
-2
src/vectorscope.cpp
src/vectorscope.cpp
+2
-2
src/vectorscope.h
src/vectorscope.h
+2
-2
src/waveform.cpp
src/waveform.cpp
+2
-2
src/waveform.h
src/waveform.h
+2
-2
No files found.
src/CMakeLists.txt
View file @
6cc358a0
...
...
@@ -220,6 +220,7 @@ set(kdenlive_SRCS
tracksconfigdialog.cpp
configtrackscommand.cpp
abstractscopewidget.cpp
abstractgfxscopewidget.cpp
audioscopes/abstractaudioscopewidget.cpp
audioscopes/audiospectrum.cpp
rebuildgroupcommand.cpp
...
...
src/abstractgfxscopewidget.cpp
0 → 100644
View file @
6cc358a0
/***************************************************************************
* Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
* This file is part of kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "qtconcurrentrun.h"
#include "abstractgfxscopewidget.h"
#include "renderer.h"
#include "monitor.h"
#include <QFuture>
#include <QColor>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
const
int
REALTIME_FPS
=
30
;
const
QColor
light
(
250
,
238
,
226
,
255
);
const
QColor
dark
(
40
,
40
,
39
,
255
);
const
QColor
dark2
(
25
,
25
,
23
,
255
);
const
QPen
AbstractGfxScopeWidget
::
penThick
(
QBrush
(
QColor
(
250
,
250
,
250
)),
2
,
Qt
::
SolidLine
);
const
QPen
AbstractGfxScopeWidget
::
penThin
(
QBrush
(
QColor
(
250
,
250
,
250
)),
1
,
Qt
::
SolidLine
);
const
QPen
AbstractGfxScopeWidget
::
penLight
(
QBrush
(
QColor
(
200
,
200
,
250
,
150
)),
1
,
Qt
::
SolidLine
);
const
QPen
AbstractGfxScopeWidget
::
penLightDots
(
QBrush
(
QColor
(
200
,
200
,
250
,
150
)),
1
,
Qt
::
DotLine
);
const
QPen
AbstractGfxScopeWidget
::
penDark
(
QBrush
(
QColor
(
0
,
0
,
20
,
250
)),
1
,
Qt
::
SolidLine
);
const
QPen
AbstractGfxScopeWidget
::
penDarkDots
(
QBrush
(
QColor
(
0
,
0
,
20
,
250
)),
1
,
Qt
::
DotLine
);
AbstractGfxScopeWidget
::
AbstractGfxScopeWidget
(
Monitor
*
projMonitor
,
Monitor
*
clipMonitor
,
bool
trackMouse
,
QWidget
*
parent
)
:
QWidget
(
parent
),
m_projMonitor
(
projMonitor
),
m_clipMonitor
(
clipMonitor
),
m_mousePos
(
0
,
0
),
m_mouseWithinWidget
(
false
),
offset
(
5
),
m_accelFactorHUD
(
1
),
m_accelFactorScope
(
1
),
m_accelFactorBackground
(
1
),
m_semaphoreHUD
(
1
),
m_semaphoreScope
(
1
),
m_semaphoreBackground
(
1
),
initialDimensionUpdateDone
(
false
),
m_requestForcedUpdate
(
false
)
{
m_scopePalette
=
QPalette
();
m_scopePalette
.
setBrush
(
QPalette
::
Window
,
QBrush
(
dark2
));
m_scopePalette
.
setBrush
(
QPalette
::
Base
,
QBrush
(
dark
));
m_scopePalette
.
setBrush
(
QPalette
::
Button
,
QBrush
(
dark
));
m_scopePalette
.
setBrush
(
QPalette
::
Text
,
QBrush
(
light
));
m_scopePalette
.
setBrush
(
QPalette
::
WindowText
,
QBrush
(
light
));
m_scopePalette
.
setBrush
(
QPalette
::
ButtonText
,
QBrush
(
light
));
this
->
setPalette
(
m_scopePalette
);
this
->
setAutoFillBackground
(
true
);
m_aAutoRefresh
=
new
QAction
(
i18n
(
"Auto Refresh"
),
this
);
m_aAutoRefresh
->
setCheckable
(
true
);
m_aRealtime
=
new
QAction
(
i18n
(
"Realtime (with precision loss)"
),
this
);
m_aRealtime
->
setCheckable
(
true
);
m_menu
=
new
QMenu
(
this
);
m_menu
->
setPalette
(
m_scopePalette
);
m_menu
->
addAction
(
m_aAutoRefresh
);
m_menu
->
addAction
(
m_aRealtime
);
this
->
setContextMenuPolicy
(
Qt
::
CustomContextMenu
);
m_activeRender
=
(
m_clipMonitor
->
isActive
())
?
m_clipMonitor
->
render
:
m_projMonitor
->
render
;
bool
b
=
true
;
b
&=
connect
(
this
,
SIGNAL
(
customContextMenuRequested
(
QPoint
)),
this
,
SLOT
(
customContextMenuRequested
(
QPoint
)));
//b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
b
&=
connect
(
m_activeRender
,
SIGNAL
(
frameUpdated
(
QImage
)),
this
,
SLOT
(
slotRenderZoneUpdated
(
QImage
)));
b
&=
connect
(
this
,
SIGNAL
(
signalHUDRenderingFinished
(
uint
,
uint
)),
this
,
SLOT
(
slotHUDRenderingFinished
(
uint
,
uint
)));
b
&=
connect
(
this
,
SIGNAL
(
signalScopeRenderingFinished
(
uint
,
uint
)),
this
,
SLOT
(
slotScopeRenderingFinished
(
uint
,
uint
)));
b
&=
connect
(
this
,
SIGNAL
(
signalBackgroundRenderingFinished
(
uint
,
uint
)),
this
,
SLOT
(
slotBackgroundRenderingFinished
(
uint
,
uint
)));
b
&=
connect
(
m_aRealtime
,
SIGNAL
(
toggled
(
bool
)),
this
,
SLOT
(
slotResetRealtimeFactor
(
bool
)));
b
&=
connect
(
m_aAutoRefresh
,
SIGNAL
(
toggled
(
bool
)),
this
,
SLOT
(
slotAutoRefreshToggled
(
bool
)));
Q_ASSERT
(
b
);
// Enable mouse tracking if desired.
// Causes the mouseMoved signal to be emitted when the mouse moves inside the
// widget, even when no mouse button is pressed.
this
->
setMouseTracking
(
trackMouse
);
}
AbstractGfxScopeWidget
::~
AbstractGfxScopeWidget
()
{
writeConfig
();
delete
m_menu
;
delete
m_aAutoRefresh
;
delete
m_aRealtime
;
}
void
AbstractGfxScopeWidget
::
init
()
{
m_widgetName
=
widgetName
();
readConfig
();
}
void
AbstractGfxScopeWidget
::
readConfig
()
{
KSharedConfigPtr
config
=
KGlobal
::
config
();
KConfigGroup
scopeConfig
(
config
,
configName
());
m_aAutoRefresh
->
setChecked
(
scopeConfig
.
readEntry
(
"autoRefresh"
,
true
));
m_aRealtime
->
setChecked
(
scopeConfig
.
readEntry
(
"realtime"
,
false
));
scopeConfig
.
sync
();
}
void
AbstractGfxScopeWidget
::
writeConfig
()
{
KSharedConfigPtr
config
=
KGlobal
::
config
();
KConfigGroup
scopeConfig
(
config
,
configName
());
scopeConfig
.
writeEntry
(
"autoRefresh"
,
m_aAutoRefresh
->
isChecked
());
scopeConfig
.
writeEntry
(
"realtime"
,
m_aRealtime
->
isChecked
());
scopeConfig
.
sync
();
}
QString
AbstractGfxScopeWidget
::
configName
()
{
return
"Scope_"
+
m_widgetName
;
}
void
AbstractGfxScopeWidget
::
prodHUDThread
()
{
if
(
this
->
visibleRegion
().
isEmpty
())
{
// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating HUD.";
}
else
{
if
(
m_semaphoreHUD
.
tryAcquire
(
1
))
{
Q_ASSERT
(
!
m_threadHUD
.
isRunning
());
m_newHUDFrames
.
fetchAndStoreRelaxed
(
0
);
m_newHUDUpdates
.
fetchAndStoreRelaxed
(
0
);
m_threadHUD
=
QtConcurrent
::
run
(
this
,
&
AbstractGfxScopeWidget
::
renderHUD
,
m_accelFactorHUD
);
// qDebug() << "HUD thread started in " << m_widgetName;
}
else
{
// qDebug() << "HUD semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadHUD.isRunning();
}
}
}
void
AbstractGfxScopeWidget
::
prodScopeThread
()
{
// Only start a new thread if the scope is actually visible
// and not hidden by another widget on the stack and if user want the scope to update.
if
(
this
->
visibleRegion
().
isEmpty
()
||
(
!
m_aAutoRefresh
->
isChecked
()
&&
!
m_requestForcedUpdate
))
{
// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating scope.";
}
else
{
// Try to acquire the semaphore. This must only succeed if m_threadScope is not running
// anymore. Therefore the semaphore must NOT be released before m_threadScope ends.
// If acquiring the semaphore fails, the thread is still running.
if
(
m_semaphoreScope
.
tryAcquire
(
1
))
{
Q_ASSERT
(
!
m_threadScope
.
isRunning
());
m_newScopeFrames
.
fetchAndStoreRelaxed
(
0
);
m_newScopeUpdates
.
fetchAndStoreRelaxed
(
0
);
Q_ASSERT
(
m_accelFactorScope
>
0
);
// See http://doc.qt.nokia.com/latest/qtconcurrentrun.html#run about
// running member functions in a thread
m_threadScope
=
QtConcurrent
::
run
(
this
,
&
AbstractGfxScopeWidget
::
renderScope
,
m_accelFactorScope
,
m_scopeImage
);
m_requestForcedUpdate
=
false
;
// qDebug() << "Scope thread started in " << m_widgetName;
}
else
{
// qDebug() << "Scope semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadScope.isRunning();
}
}
}
void
AbstractGfxScopeWidget
::
prodBackgroundThread
()
{
if
(
this
->
visibleRegion
().
isEmpty
())
{
// qDebug() << "Scope " << m_widgetName << " is not visible. Not calculating background.";
}
else
{
if
(
m_semaphoreBackground
.
tryAcquire
(
1
))
{
Q_ASSERT
(
!
m_threadBackground
.
isRunning
());
m_newBackgroundFrames
.
fetchAndStoreRelaxed
(
0
);
m_newBackgroundUpdates
.
fetchAndStoreRelaxed
(
0
);
m_threadBackground
=
QtConcurrent
::
run
(
this
,
&
AbstractGfxScopeWidget
::
renderBackground
,
m_accelFactorBackground
);
// qDebug() << "Background thread started in " << m_widgetName;
}
else
{
// qDebug() << "Background semaphore locked, not prodding in " << m_widgetName << ". Thread running: " << m_threadBackground.isRunning();
}
}
}
void
AbstractGfxScopeWidget
::
forceUpdate
(
bool
doUpdate
)
{
// qDebug() << "Force update called in " << widgetName() << ". Arg: " << doUpdate;
if
(
!
doUpdate
)
{
return
;
}
m_requestForcedUpdate
=
true
;
m_newHUDUpdates
.
fetchAndAddRelaxed
(
1
);
m_newScopeUpdates
.
fetchAndAddRelaxed
(
1
);
m_newBackgroundUpdates
.
fetchAndAddRelaxed
(
1
);
prodHUDThread
();
prodScopeThread
();
prodBackgroundThread
();
}
void
AbstractGfxScopeWidget
::
forceUpdateHUD
()
{
m_newHUDUpdates
.
fetchAndAddRelaxed
(
1
);
prodHUDThread
();
}
void
AbstractGfxScopeWidget
::
forceUpdateScope
()
{
m_newScopeUpdates
.
fetchAndAddRelaxed
(
1
);
m_requestForcedUpdate
=
true
;
prodScopeThread
();
}
void
AbstractGfxScopeWidget
::
forceUpdateBackground
()
{
m_newBackgroundUpdates
.
fetchAndAddRelaxed
(
1
);
prodBackgroundThread
();
}
///// Events /////
void
AbstractGfxScopeWidget
::
mouseReleaseEvent
(
QMouseEvent
*
event
)
{
if
(
!
m_aAutoRefresh
->
isChecked
())
{
m_requestForcedUpdate
=
true
;
m_activeRender
->
sendFrameUpdate
();
}
prodHUDThread
();
prodScopeThread
();
prodBackgroundThread
();
QWidget
::
mouseReleaseEvent
(
event
);
}
void
AbstractGfxScopeWidget
::
resizeEvent
(
QResizeEvent
*
event
)
{
// Update the dimension of the available rect for painting
m_scopeRect
=
scopeRect
();
forceUpdate
();
QWidget
::
resizeEvent
(
event
);
}
void
AbstractGfxScopeWidget
::
showEvent
(
QShowEvent
*
event
)
{
QWidget
::
showEvent
(
event
);
m_scopeRect
=
scopeRect
();
}
void
AbstractGfxScopeWidget
::
paintEvent
(
QPaintEvent
*
)
{
QPainter
davinci
(
this
);
davinci
.
drawImage
(
m_scopeRect
.
topLeft
(),
m_imgBackground
);
davinci
.
drawImage
(
m_scopeRect
.
topLeft
(),
m_imgScope
);
davinci
.
drawImage
(
m_scopeRect
.
topLeft
(),
m_imgHUD
);
}
void
AbstractGfxScopeWidget
::
mouseMoveEvent
(
QMouseEvent
*
event
)
{
m_mousePos
=
event
->
pos
();
m_mouseWithinWidget
=
true
;
emit
signalMousePositionChanged
();
}
void
AbstractGfxScopeWidget
::
leaveEvent
(
QEvent
*
)
{
m_mouseWithinWidget
=
false
;
emit
signalMousePositionChanged
();
}
void
AbstractGfxScopeWidget
::
customContextMenuRequested
(
const
QPoint
&
pos
)
{
m_menu
->
exec
(
this
->
mapToGlobal
(
pos
));
}
uint
AbstractGfxScopeWidget
::
calculateAccelFactorHUD
(
uint
oldMseconds
,
uint
)
{
return
ceil
((
float
)
oldMseconds
*
REALTIME_FPS
/
1000
);
}
uint
AbstractGfxScopeWidget
::
calculateAccelFactorScope
(
uint
oldMseconds
,
uint
)
{
return
ceil
((
float
)
oldMseconds
*
REALTIME_FPS
/
1000
);
}
uint
AbstractGfxScopeWidget
::
calculateAccelFactorBackground
(
uint
oldMseconds
,
uint
)
{
return
ceil
((
float
)
oldMseconds
*
REALTIME_FPS
/
1000
);
}
///// Slots /////
void
AbstractGfxScopeWidget
::
slotHUDRenderingFinished
(
uint
mseconds
,
uint
oldFactor
)
{
// qDebug() << "HUD rendering has finished, waiting for termination in " << m_widgetName;
m_threadHUD
.
waitForFinished
();
m_imgHUD
=
m_threadHUD
.
result
();
m_semaphoreHUD
.
release
(
1
);
this
->
update
();
int
accel
;
if
(
m_aRealtime
->
isChecked
())
{
accel
=
calculateAccelFactorHUD
(
mseconds
,
oldFactor
);
if
(
m_accelFactorHUD
<
1
)
{
accel
=
1
;
}
m_accelFactorHUD
=
accel
;
}
if
((
m_newHUDFrames
>
0
&&
m_aAutoRefresh
->
isChecked
())
||
m_newHUDUpdates
>
0
)
{
// qDebug() << "Trying to start a new HUD thread for " << m_widgetName
// << ". New frames/updates: " << m_newHUDFrames << "/" << m_newHUDUpdates;
prodHUDThread
();;
}
}
void
AbstractGfxScopeWidget
::
slotScopeRenderingFinished
(
uint
mseconds
,
uint
oldFactor
)
{
// The signal can be received before the thread has really finished. So we
// need to wait until it has really finished before starting a new thread.
// qDebug() << "Scope rendering has finished, waiting for termination in " << m_widgetName;
m_threadScope
.
waitForFinished
();
m_imgScope
=
m_threadScope
.
result
();
// The scope thread has finished. Now we can release the semaphore, allowing a new thread.
// See prodScopeThread where the semaphore is acquired again.
m_semaphoreScope
.
release
(
1
);
this
->
update
();
// Calculate the acceleration factor hint to get «realtime» updates.
int
accel
;
if
(
m_aRealtime
->
isChecked
())
{
accel
=
calculateAccelFactorScope
(
mseconds
,
oldFactor
);
if
(
accel
<
1
)
{
// If mseconds happens to be 0.
accel
=
1
;
}
// Don't directly calculate with m_accelFactorScope as we are dealing with concurrency.
// If m_accelFactorScope is set to 0 at the wrong moment, who knows what might happen
// then :) Therefore use a local variable.
m_accelFactorScope
=
accel
;
}
if
((
m_newScopeFrames
>
0
&&
m_aAutoRefresh
->
isChecked
())
||
m_newScopeUpdates
>
0
)
{
// qDebug() << "Trying to start a new scope thread for " << m_widgetName
// << ". New frames/updates: " << m_newScopeFrames << "/" << m_newScopeUpdates;
prodScopeThread
();
}
}
void
AbstractGfxScopeWidget
::
slotBackgroundRenderingFinished
(
uint
mseconds
,
uint
oldFactor
)
{
// qDebug() << "Background rendering has finished, waiting for termination in " << m_widgetName;
m_threadBackground
.
waitForFinished
();
m_imgBackground
=
m_threadBackground
.
result
();
m_semaphoreBackground
.
release
(
1
);
this
->
update
();
int
accel
;
if
(
m_aRealtime
->
isChecked
())
{
accel
=
calculateAccelFactorBackground
(
mseconds
,
oldFactor
);
if
(
m_accelFactorBackground
<
1
)
{
accel
=
1
;
}
m_accelFactorBackground
=
accel
;
}
if
((
m_newBackgroundFrames
>
0
&&
m_aAutoRefresh
->
isChecked
())
||
m_newBackgroundUpdates
>
0
)
{
// qDebug() << "Trying to start a new background thread for " << m_widgetName
// << ". New frames/updates: " << m_newBackgroundFrames << "/" << m_newBackgroundUpdates;
prodBackgroundThread
();;
}
}
void
AbstractGfxScopeWidget
::
slotActiveMonitorChanged
(
bool
isClipMonitor
)
{
// qDebug() << "Active monitor has changed in " << m_widgetName << ". Is the clip monitor active now? " << isClipMonitor;
bool
b
=
m_activeRender
->
disconnect
(
this
);
Q_ASSERT
(
b
);
m_activeRender
=
(
isClipMonitor
)
?
m_clipMonitor
->
render
:
m_projMonitor
->
render
;
//b &= connect(m_activeRender, SIGNAL(rendererPosition(int)), this, SLOT(slotRenderZoneUpdated()));
b
&=
connect
(
m_activeRender
,
SIGNAL
(
frameUpdated
(
QImage
)),
this
,
SLOT
(
slotRenderZoneUpdated
(
QImage
)));
Q_ASSERT
(
b
);
// Update the scope for the new monitor.
prodHUDThread
();
prodScopeThread
();
prodBackgroundThread
();
}
void
AbstractGfxScopeWidget
::
slotRenderZoneUpdated
()
{
m_newHUDFrames
.
fetchAndAddRelaxed
(
1
);
m_newScopeFrames
.
fetchAndAddRelaxed
(
1
);
m_newBackgroundFrames
.
fetchAndAddRelaxed
(
1
);
// qDebug() << "Monitor incoming. New frames total HUD/Scope/Background: " << m_newHUDFrames
// << "/" << m_newScopeFrames << "/" << m_newBackgroundFrames;
if
(
this
->
visibleRegion
().
isEmpty
())
{
// qDebug() << "Scope of widget " << m_widgetName << " is not at the top, not rendering.";
}
else
{
if
(
m_aAutoRefresh
->
isChecked
())
{
prodHUDThread
();
prodScopeThread
();
prodBackgroundThread
();
}
}
}
void
AbstractGfxScopeWidget
::
slotRenderZoneUpdated
(
QImage
frame
)
{
m_scopeImage
=
frame
;
slotRenderZoneUpdated
();
}
void
AbstractGfxScopeWidget
::
slotResetRealtimeFactor
(
bool
realtimeChecked
)
{
if
(
!
realtimeChecked
)
{
m_accelFactorHUD
=
1
;
m_accelFactorScope
=
1
;
m_accelFactorBackground
=
1
;
}
}
bool
AbstractGfxScopeWidget
::
autoRefreshEnabled
()
{
return
m_aAutoRefresh
->
isChecked
();
}
void
AbstractGfxScopeWidget
::
slotAutoRefreshToggled
(
bool
autoRefresh
)
{
if
(
isVisible
())
emit
requestAutoRefresh
(
autoRefresh
);
// TODO only if depends on input
if
(
autoRefresh
)
{
//forceUpdate();
m_requestForcedUpdate
=
true
;
m_activeRender
->
sendFrameUpdate
();
}
}
src/abstractgfxscopewidget.h
0 → 100644
View file @
6cc358a0
/***************************************************************************
* Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
* This file is part of kdenlive. See www.kdenlive.org. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
/**
This abstract widget is a proof that abstract things sometimes *are* useful.
The widget expects three layers which
* Will be painted on top of each other on each update
* Are rendered in a separate thread so that the UI is not blocked
* Are rendered only if necessary (e.g., if a layer does not depend
on input images, it will not be re-rendered for incoming frames)
The layer order is as follows:
_____________________
/ \
/ HUD Layer \
/ \
---------------------------
_____________________
/ \
/ Scope Layer \
/ \
---------------------------
_____________________
/ \
/ Background Layer \
/ \
---------------------------
Colors of Scope Widgets are defined in here (and thus don't need to be
re-defined in the implementation of the widget's .ui file).
The custom context menu already contains entries, like for enabling auto-
refresh. It can certainly be extended in the implementation of the widget.
Note: Widgets deriving from this class should connect slotActiveMonitorChanged
to the appropriate signal.
If you intend to write an own widget inheriting from this one, please read
the comments on the unimplemented methods carefully. They are not only here
for optical amusement, but also contain important information.
*/
#ifndef ABSTRACTGFXSCOPEWIDGET_H
#define ABSTRACTGFXSCOPEWIDGET_H
#include <QtCore>
#include <QWidget>
class
QMenu
;
class
Monitor
;
class
Render
;
class
AbstractGfxScopeWidget
:
public
QWidget
{
Q_OBJECT
public:
AbstractGfxScopeWidget
(
Monitor
*
projMonitor
,
Monitor
*
clipMonitor
,
bool
trackMouse
=
false
,
QWidget
*
parent
=
0
);
virtual
~
AbstractGfxScopeWidget
();
// Must be virtual because of inheritance, to avoid memory leaks
QPalette
m_scopePalette
;
/** Initializes widget settings (reads configuration).
Has to be called in the implementing object. */
void
init
();
/** Does this scope have auto-refresh enabled */
bool
autoRefreshEnabled
();
///// Unimplemented /////
virtual
QString
widgetName
()
const
=
0
;
///// Variables /////
static
const
QPen
penThick
;
static
const
QPen
penThin
;
static
const
QPen
penLight
;
static
const
QPen
penLightDots
;
static
const
QPen
penDark
;
static
const
QPen
penDarkDots
;
protected:
///// Variables /////
Monitor
*
m_projMonitor
;
Monitor
*
m_clipMonitor
;
Render
*
m_activeRender
;
/** The context menu. Feel free to add new entries in your implementation. */
QMenu
*
m_menu
;
/** Enables auto refreshing of the scope.
This is when a new frame is shown on the active monitor.
Resize events always force a recalculation. */
QAction
*
m_aAutoRefresh
;
/** Realtime rendering. Should be disabled if it is not supported.
Use the accelerationFactor variable passed to the render functions as a hint of
how many times faster the scope should be calculated. */
QAction
*
m_aRealtime
;
/** The mouse position; Updated when the mouse enters the widget
AND mouse tracking has been enabled. */
QPoint
m_mousePos
;
/** Knows whether the mouse currently lies within the widget or not.
Can e.g. be used for drawing a HUD only when the mouse is in the widget. */
bool
m_mouseWithinWidget
;
/** Offset from the widget's borders */
const
uchar
offset
;
/** The rect on the widget we're painting in.
Can be used by the implementing widget, e.g. in the render methods.
Is updated when necessary (size changes). */
QRect
m_scopeRect
;
/** Images storing the calculated layers. Will be used on repaint events. */
QImage
m_imgHUD
;
QImage
m_imgScope
;
QImage
m_imgBackground
;
/** The acceleration factors can be accessed also by other renderer tasks,
e.g. to display the scope's acceleration factor in the HUD renderer. */
int
m_accelFactorHUD
;
int
m_accelFactorScope
;
int
m_accelFactorBackground
;
/** Reads the widget's configuration.
Can be extended in the implementing subclass (make sure to run readConfig as well). */
virtual
void
readConfig
();
/** Writes the widget configuration.
Implementing widgets have to implement an own method and run it in their destructor. */
void
writeConfig
();
/** Identifier for the widget's configuration. */
QString
configName
();
///// Unimplemented Methods /////
/** Where on the widget we can paint in.
May also update other variables that depend on the widget's size. */
virtual
QRect
scopeRect
()
=
0
;
/** @brief HUD renderer. Must emit signalHUDRenderingFinished(). @see renderScope */
virtual
QImage
renderHUD
(
uint
accelerationFactor
)
=
0
;
/** @brief Scope renderer. Must emit signalScopeRenderingFinished()
when calculation has finished, to allow multi-threading.
accelerationFactor hints how much faster than usual the calculation should be accomplished, if possible. */
virtual
QImage
renderScope
(
uint
accelerationFactor
,
const
QImage
)
=
0
;
/** @brief Background renderer. Must emit signalBackgroundRenderingFinished(). @see renderScope */
virtual
QImage
renderBackground
(
uint
accelerationFactor
)
=
0
;
/** Must return true if the HUD layer depends on the input monitor.
If it does not, then it does not need to be re-calculated when
a new frame from the monitor is incoming. */
virtual
bool
isHUDDependingOnInput
()
const
=
0
;
/** @see isHUDDependingOnInput() */
virtual
bool
isScopeDependingOnInput
()
const
=
0
;