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