1 /******************************************************************************\
2 * Copyright (c) 2004-2020
3 *
4 * Author(s):
5 * Volker Fischer
6 *
7 * Description:
8 * Implements a multi color LED bar
9 *
10 ******************************************************************************
11 *
12 * This program is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the License, or (at your option) any later
15 * version.
16 *
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 *
26 \******************************************************************************/
27
28 #include "levelmeter.h"
29
30 /* Implementation *************************************************************/
CLevelMeter(QWidget * parent)31 CLevelMeter::CLevelMeter ( QWidget* parent ) : QWidget ( parent ), eLevelMeterType ( MT_BAR )
32 {
33 // initialize LED meter
34 QWidget* pLEDMeter = new QWidget();
35 QVBoxLayout* pLEDLayout = new QVBoxLayout ( pLEDMeter );
36 pLEDLayout->setAlignment ( Qt::AlignHCenter );
37 pLEDLayout->setMargin ( 0 );
38 pLEDLayout->setSpacing ( 0 );
39
40 // create LEDs plus the clip LED
41 vecpLEDs.Init ( NUM_LEDS_INCL_CLIP_LED );
42
43 for ( int iLEDIdx = NUM_LEDS_INCL_CLIP_LED - 1; iLEDIdx >= 0; iLEDIdx-- )
44 {
45 // create LED object
46 vecpLEDs[iLEDIdx] = new cLED ( parent );
47
48 // add LED to layout with spacer (do not add spacer on the bottom of the first LED)
49 if ( iLEDIdx < NUM_LEDS_INCL_CLIP_LED - 1 )
50 {
51 pLEDLayout->addStretch();
52 }
53
54 pLEDLayout->addWidget ( vecpLEDs[iLEDIdx]->GetLabelPointer() );
55 }
56
57 // initialize bar meter
58 pBarMeter = new QProgressBar();
59 pBarMeter->setOrientation ( Qt::Vertical );
60 pBarMeter->setRange ( 0, 100 * NUM_STEPS_LED_BAR ); // use factor 100 to reduce quantization (bar is continuous)
61 pBarMeter->setFormat ( "" ); // suppress percent numbers
62
63 // setup stacked layout for meter type switching mechanism
64 pStackedLayout = new QStackedLayout ( this );
65 pStackedLayout->addWidget ( pLEDMeter );
66 pStackedLayout->addWidget ( pBarMeter );
67
68 // according to QScrollArea description: "When using a scroll area to display the
69 // contents of a custom widget, it is important to ensure that the size hint of
70 // the child widget is set to a suitable value."
71 pBarMeter->setMinimumSize ( QSize ( 1, 1 ) );
72 pLEDMeter->setMinimumSize ( QSize ( 1, 1 ) );
73
74 // update the meter type (using the default value of the meter type)
75 SetLevelMeterType ( eLevelMeterType );
76
77 // setup clip indicator timer
78 TimerClip.setSingleShot ( true );
79 TimerClip.setInterval ( CLIP_IND_TIME_OUT_MS );
80
81 // Connections -------------------------------------------------------------
82 QObject::connect ( &TimerClip, &QTimer::timeout, this, &CLevelMeter::ClipReset );
83 }
84
~CLevelMeter()85 CLevelMeter::~CLevelMeter()
86 {
87 // clean up the LED objects
88 for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ )
89 {
90 delete vecpLEDs[iLEDIdx];
91 }
92 }
93
SetLevelMeterType(const ELevelMeterType eNType)94 void CLevelMeter::SetLevelMeterType ( const ELevelMeterType eNType )
95 {
96 eLevelMeterType = eNType;
97
98 switch ( eNType )
99 {
100 case MT_LED:
101 // initialize all LEDs
102 for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ )
103 {
104 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_BLACK );
105 }
106 pStackedLayout->setCurrentIndex ( 0 );
107 break;
108
109 case MT_BAR:
110 pStackedLayout->setCurrentIndex ( 1 );
111 break;
112
113 case MT_SLIM_BAR:
114 // set all LEDs to disabled, otherwise we would not get our desired small width
115 for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ )
116 {
117 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_DISABLED );
118 }
119
120 pStackedLayout->setCurrentIndex ( 1 );
121 break;
122 }
123
124 // update bar meter style and reset clip state
125 SetBarMeterStyleAndClipStatus ( eNType, false );
126 }
127
SetBarMeterStyleAndClipStatus(const ELevelMeterType eNType,const bool bIsClip)128 void CLevelMeter::SetBarMeterStyleAndClipStatus ( const ELevelMeterType eNType, const bool bIsClip )
129 {
130 switch ( eNType )
131 {
132 case MT_SLIM_BAR:
133 if ( bIsClip )
134 {
135 pBarMeter->setStyleSheet ( "QProgressBar { border: 0px solid red;"
136 " margin: 0px;"
137 " padding: 0px;"
138 " width: 4px;"
139 " background: red; }"
140 "QProgressBar::chunk { background: green; }" );
141 }
142 else
143 {
144 pBarMeter->setStyleSheet ( "QProgressBar { border: 0px;"
145 " margin: 0px;"
146 " padding: 0px;"
147 " width: 4px; }"
148 "QProgressBar::chunk { background: green; }" );
149 }
150 break;
151
152 default: /* MT_BAR */
153 if ( bIsClip )
154 {
155 pBarMeter->setStyleSheet ( "QProgressBar { border: 2px solid red;"
156 " margin: 1px;"
157 " padding: 1px;"
158 " width: 15px;"
159 " background: transparent; }"
160 "QProgressBar::chunk { background: green; }" );
161 }
162 else
163 {
164 pBarMeter->setStyleSheet ( "QProgressBar { margin: 1px;"
165 " padding: 1px;"
166 " width: 15px; }"
167 "QProgressBar::chunk { background: green; }" );
168 }
169 break;
170 }
171 }
172
SetValue(const double dValue)173 void CLevelMeter::SetValue ( const double dValue )
174 {
175 switch ( eLevelMeterType )
176 {
177 case MT_LED:
178 // update state of all LEDs for current level value (except of the clip LED)
179 for ( int iLEDIdx = 0; iLEDIdx < NUM_STEPS_LED_BAR; iLEDIdx++ )
180 {
181 // set active LED color if value is above current LED index
182 if ( iLEDIdx < dValue )
183 {
184 // check which color we should use (green, yellow or red)
185 if ( iLEDIdx < YELLOW_BOUND_LED_BAR )
186 {
187 // green region
188 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_GREEN );
189 }
190 else
191 {
192 if ( iLEDIdx < RED_BOUND_LED_BAR )
193 {
194 // yellow region
195 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_YELLOW );
196 }
197 else
198 {
199 // red region
200 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_RED );
201 }
202 }
203 }
204 else
205 {
206 // we use black LED for inactive state
207 vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_BLACK );
208 }
209 }
210 break;
211
212 case MT_BAR:
213 case MT_SLIM_BAR:
214 pBarMeter->setValue ( 100 * dValue );
215 break;
216 }
217
218 // clip indicator management (note that in case of clipping, i.e. full
219 // scale level, the value is above NUM_STEPS_LED_BAR since the minimum
220 // value of int16 is -32768 but we normalize with 32767 -> therefore
221 // we really only show the clipping indicator, if actually the largest
222 // value of int16 is used)
223 if ( dValue > NUM_STEPS_LED_BAR )
224 {
225 switch ( eLevelMeterType )
226 {
227 case MT_LED:
228 vecpLEDs[NUM_STEPS_LED_BAR]->SetColor ( cLED::RL_RED );
229 break;
230
231 case MT_BAR:
232 case MT_SLIM_BAR:
233 SetBarMeterStyleAndClipStatus ( eLevelMeterType, true );
234 break;
235 }
236
237 TimerClip.start();
238 }
239 }
240
ClipReset()241 void CLevelMeter::ClipReset()
242 {
243 // we manually want to reset the clipping indicator: stop timer and reset
244 // clipping indicator GUI element
245 TimerClip.stop();
246
247 switch ( eLevelMeterType )
248 {
249 case MT_LED:
250 vecpLEDs[NUM_STEPS_LED_BAR]->SetColor ( cLED::RL_BLACK );
251 break;
252
253 case MT_BAR:
254 case MT_SLIM_BAR:
255 SetBarMeterStyleAndClipStatus ( eLevelMeterType, false );
256 break;
257 }
258 }
259
cLED(QWidget * parent)260 CLevelMeter::cLED::cLED ( QWidget* parent ) :
261 BitmCubeRoundBlack ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDBlackSmall.png" ) ),
262 BitmCubeRoundGreen ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDGreenSmall.png" ) ),
263 BitmCubeRoundYellow ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDYellowSmall.png" ) ),
264 BitmCubeRoundRed ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDRedSmall.png" ) )
265 {
266 // create LED label
267 pLEDLabel = new QLabel ( "", parent );
268
269 // set initial bitmap
270 pLEDLabel->setPixmap ( BitmCubeRoundBlack );
271 eCurLightColor = RL_BLACK;
272 }
273
SetColor(const ELightColor eNewColor)274 void CLevelMeter::cLED::SetColor ( const ELightColor eNewColor )
275 {
276 // only update LED if color has changed
277 if ( eNewColor != eCurLightColor )
278 {
279 switch ( eNewColor )
280 {
281 case RL_DISABLED:
282 // note that this is required for the compact channel mode
283 pLEDLabel->setPixmap ( QPixmap() );
284 break;
285
286 case RL_BLACK:
287 pLEDLabel->setPixmap ( BitmCubeRoundBlack );
288 break;
289
290 case RL_GREEN:
291 pLEDLabel->setPixmap ( BitmCubeRoundGreen );
292 break;
293
294 case RL_YELLOW:
295 pLEDLabel->setPixmap ( BitmCubeRoundYellow );
296 break;
297
298 case RL_RED:
299 pLEDLabel->setPixmap ( BitmCubeRoundRed );
300 break;
301 }
302 eCurLightColor = eNewColor;
303 }
304 }
305