Cleanup
[carla.git] / source / frontend / widgets / digitalpeakmeter.py
blob3707b296c98bea13b7cb6264e786eb8bff68f341
1 #!/usr/bin/env python3
2 # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # ---------------------------------------------------------------------------------------------------------------------
6 # Imports (Global)
8 from math import sqrt
10 from qt_compat import qt_config
12 if qt_config == 5:
13 from PyQt5.QtCore import qCritical, Qt, QSize, QLineF, QRectF
14 from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
15 from PyQt5.QtWidgets import QWidget
16 elif qt_config == 6:
17 from PyQt6.QtCore import qCritical, Qt, QSize, QLineF, QRectF
18 from PyQt6.QtGui import QColor, QLinearGradient, QPainter, QPen, QPixmap
19 from PyQt6.QtWidgets import QWidget
21 # ---------------------------------------------------------------------------------------------------------------------
22 # Widget Class
24 class DigitalPeakMeter(QWidget):
25 # enum Color
26 COLOR_GREEN = 1
27 COLOR_BLUE = 2
29 # enum Orientation
30 HORIZONTAL = 1
31 VERTICAL = 2
33 # enum Style
34 STYLE_DEFAULT = 1
35 STYLE_OPENAV = 2
36 STYLE_RNCBC = 3
37 STYLE_CALF = 4
39 # -----------------------------------------------------------------------------------------------------------------
41 def __init__(self, parent):
42 QWidget.__init__(self, parent)
43 self.setAttribute(Qt.WA_OpaquePaintEvent)
45 # defaults are VERTICAL, COLOR_GREEN, STYLE_DEFAULT
47 self.fChannelCount = 0
48 self.fChannelData = []
49 self.fLastChannelData = []
51 self.fMeterColor = self.COLOR_GREEN
52 self.fMeterColorBase = QColor(93, 231, 61)
53 self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
55 self.fMeterLinesEnabled = True
56 self.fMeterOrientation = self.VERTICAL
57 self.fMeterStyle = self.STYLE_DEFAULT
59 self.fMeterBackground = QColor("#070707")
60 self.fMeterGradient = QLinearGradient(0, 0, 0, 0)
61 self.fMeterPixmaps = ()
63 self.fSmoothMultiplier = 2
65 self.updateGrandient()
67 # -----------------------------------------------------------------------------------------------------------------
69 def channelCount(self):
70 return self.fChannelCount
72 def setChannelCount(self, count):
73 if self.fChannelCount == count:
74 return
76 if count < 0:
77 qCritical(f"DigitalPeakMeter::setChannelCount({count}) - channel count must be a positive integer or zero")
78 return
80 self.fChannelCount = count
81 self.fChannelData = []
82 self.fLastChannelData = []
84 for _ in range(count):
85 self.fChannelData.append(0.0)
86 self.fLastChannelData.append(0.0)
88 if self.fMeterStyle == self.STYLE_CALF:
89 if self.fChannelCount > 0:
90 self.setFixedSize(100, 12*self.fChannelCount)
91 else:
92 self.setMinimumSize(0, 0)
93 self.setMaximumSize(9999, 9999)
95 # -----------------------------------------------------------------------------------------------------------------
97 def meterColor(self):
98 return self.fMeterColor
100 def setMeterColor(self, color):
101 if self.fMeterColor == color:
102 return
104 if color not in (self.COLOR_GREEN, self.COLOR_BLUE):
105 qCritical(f"DigitalPeakMeter::setMeterColor({color}) - invalid color")
106 return
108 if color == self.COLOR_GREEN:
109 self.fMeterColorBase = QColor(93, 231, 61)
110 self.fMeterColorBaseAlt = QColor(15, 110, 15, 100)
111 elif color == self.COLOR_BLUE:
112 self.fMeterColorBase = QColor(82, 238, 248)
113 self.fMeterColorBaseAlt = QColor(15, 15, 110, 100)
115 self.fMeterColor = color
117 self.updateGrandient()
119 # -----------------------------------------------------------------------------------------------------------------
121 def meterLinesEnabled(self):
122 return self.fMeterLinesEnabled
124 def setMeterLinesEnabled(self, yesNo):
125 if self.fMeterLinesEnabled == yesNo:
126 return
128 self.fMeterLinesEnabled = yesNo
130 # -----------------------------------------------------------------------------------------------------------------
132 def meterOrientation(self):
133 return self.fMeterOrientation
135 def setMeterOrientation(self, orientation):
136 if self.fMeterOrientation == orientation:
137 return
139 if orientation not in (self.HORIZONTAL, self.VERTICAL):
140 qCritical(f"DigitalPeakMeter::setMeterOrientation({orientation}) - invalid orientation")
141 return
143 self.fMeterOrientation = orientation
145 self.updateGrandient()
147 # -----------------------------------------------------------------------------------------------------------------
149 def meterStyle(self):
150 return self.fMeterStyle
152 def setMeterStyle(self, style):
153 if self.fMeterStyle == style:
154 return
156 if style not in (self.STYLE_DEFAULT, self.STYLE_OPENAV, self.STYLE_RNCBC, self.STYLE_CALF):
157 qCritical(f"DigitalPeakMeter::setMeterStyle({style}) - invalid style")
158 return
160 if style == self.STYLE_DEFAULT:
161 self.fMeterBackground = QColor("#070707")
162 elif style == self.STYLE_OPENAV:
163 self.fMeterBackground = QColor("#1A1A1A")
164 elif style == self.STYLE_RNCBC:
165 self.fMeterBackground = QColor("#070707")
166 elif style == self.STYLE_CALF:
167 self.fMeterBackground = QColor("#000")
169 if style == self.STYLE_CALF:
170 self.fMeterPixmaps = (QPixmap(":/bitmaps/meter_calf_off.png"), QPixmap(":/bitmaps/meter_calf_on.png"))
171 if self.fChannelCount > 0:
172 self.setFixedSize(100, 12*self.fChannelCount)
173 else:
174 self.fMeterPixmaps = ()
175 self.setMinimumSize(0, 0)
176 self.setMaximumSize(9999, 9999)
178 self.fMeterStyle = style
180 self.updateGrandient()
182 # -----------------------------------------------------------------------------------------------------------------
184 def smoothMultiplier(self):
185 return self.fSmoothMultiplier
187 def setSmoothMultiplier(self, value):
188 if self.fSmoothMultiplier == value:
189 return
191 if not isinstance(value, int):
192 qCritical("DigitalPeakMeter::setSmoothMultiplier() - value must be an integer")
193 return
194 if value < 0:
195 qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be >= 0")
196 return
197 if value > 5:
198 qCritical(f"DigitalPeakMeter::setSmoothMultiplier({value}) - value must be < 5")
199 return
201 self.fSmoothMultiplier = value
203 # -----------------------------------------------------------------------------------------------------------------
205 def displayMeter(self, meter, level, forced = False):
206 if not isinstance(meter, int):
207 qCritical("DigitalPeakMeter::displayMeter(,) - meter value must be an integer")
208 return
209 if not isinstance(level, float):
210 qCritical(f"DigitalPeakMeter::displayMeter({meter},) - level value must be a float")
211 return
212 if meter <= 0 or meter > self.fChannelCount:
213 qCritical(f"DigitalPeakMeter::displayMeter({meter}, {level}) - invalid meter number")
214 return
216 i = meter - 1
218 if self.fSmoothMultiplier > 0 and not forced:
219 level = (
220 (self.fLastChannelData[i] * float(self.fSmoothMultiplier) + level)
221 / float(self.fSmoothMultiplier + 1)
224 if level < 0.001:
225 level = 0.0
226 elif level > 0.999:
227 level = 1.0
229 if self.fChannelData[i] != level:
230 self.fChannelData[i] = level
231 self.update()
233 self.fLastChannelData[i] = level
235 # -----------------------------------------------------------------------------------------------------------------
237 def updateGrandient(self):
238 self.fMeterGradient = QLinearGradient(0, 0, 1, 1)
240 if self.fMeterStyle == self.STYLE_DEFAULT:
241 if self.fMeterOrientation == self.HORIZONTAL:
242 self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
243 self.fMeterGradient.setColorAt(0.2, self.fMeterColorBase)
244 self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
245 self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
246 self.fMeterGradient.setColorAt(0.8, Qt.yellow)
247 self.fMeterGradient.setColorAt(1.0, Qt.red)
249 elif self.fMeterOrientation == self.VERTICAL:
250 self.fMeterGradient.setColorAt(0.0, Qt.red)
251 self.fMeterGradient.setColorAt(0.2, Qt.yellow)
252 self.fMeterGradient.setColorAt(0.4, self.fMeterColorBase)
253 self.fMeterGradient.setColorAt(0.6, self.fMeterColorBase)
254 self.fMeterGradient.setColorAt(0.8, self.fMeterColorBase)
255 self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
257 elif self.fMeterStyle == self.STYLE_RNCBC:
258 if self.fMeterColor == self.COLOR_BLUE:
259 c1 = QColor(40,160,160)
260 c2 = QColor(60,220,160)
261 elif self.fMeterColor == self.COLOR_GREEN:
262 c1 = QColor( 40,160,40)
263 c2 = QColor(160,220,20)
265 if self.fMeterOrientation == self.HORIZONTAL:
266 self.fMeterGradient.setColorAt(0.0, c1)
267 self.fMeterGradient.setColorAt(0.2, c1)
268 self.fMeterGradient.setColorAt(0.6, c2)
269 self.fMeterGradient.setColorAt(0.7, QColor(220,220, 20))
270 self.fMeterGradient.setColorAt(0.8, QColor(240,160, 20))
271 self.fMeterGradient.setColorAt(0.9, QColor(240, 0, 20))
272 self.fMeterGradient.setColorAt(1.0, QColor(240, 0, 20))
274 elif self.fMeterOrientation == self.VERTICAL:
275 self.fMeterGradient.setColorAt(0.0, QColor(240, 0, 20))
276 self.fMeterGradient.setColorAt(0.1, QColor(240, 0, 20))
277 self.fMeterGradient.setColorAt(0.2, QColor(240,160, 20))
278 self.fMeterGradient.setColorAt(0.3, QColor(220,220, 20))
279 self.fMeterGradient.setColorAt(0.4, c2)
280 self.fMeterGradient.setColorAt(0.8, c1)
281 self.fMeterGradient.setColorAt(1.0, c1)
283 elif self.fMeterStyle in (self.STYLE_OPENAV, self.STYLE_CALF):
284 self.fMeterGradient.setColorAt(0.0, self.fMeterColorBase)
285 self.fMeterGradient.setColorAt(1.0, self.fMeterColorBase)
287 self.updateGrandientFinalStop()
289 def updateGrandientFinalStop(self):
290 if self.fMeterOrientation == self.HORIZONTAL:
291 self.fMeterGradient.setFinalStop(self.width(), 0)
293 elif self.fMeterOrientation == self.VERTICAL:
294 self.fMeterGradient.setFinalStop(0, self.height())
296 # -----------------------------------------------------------------------------------------------------------------
298 def minimumSizeHint(self):
299 return QSize(20, 10) if self.fMeterOrientation == self.HORIZONTAL else QSize(10, 20)
301 def sizeHint(self):
302 return QSize(self.width(), self.height())
304 # -----------------------------------------------------------------------------------------------------------------
306 def drawCalf(self, event):
307 painter = QPainter(self)
308 event.accept()
310 # no channels, draw black
311 if self.fChannelCount == 0:
312 painter.setPen(QPen(Qt.black, 2))
313 painter.setBrush(Qt.black)
314 painter.drawRect(0, 0, self.width(), self.height())
315 return
317 for i in range(self.fChannelCount):
318 painter.drawPixmap(0, 12*i, self.fMeterPixmaps[0])
320 meterPos = 4
321 meterSize = 12
323 # draw levels
324 for level in self.fChannelData:
325 if level != 0.0:
326 blevel = int(sqrt(level)*26.0)*3
327 painter.drawPixmap(5, meterPos, blevel, 4, self.fMeterPixmaps[1], 0, 0, blevel, 4)
328 meterPos += meterSize
330 def paintEvent(self, event):
331 if self.fMeterStyle == self.STYLE_CALF:
332 self.drawCalf(event)
333 return
335 painter = QPainter(self)
336 event.accept()
338 width = self.width()
339 height = self.height()
341 # draw background
342 painter.setPen(QPen(self.fMeterBackground, 2))
343 painter.setBrush(self.fMeterBackground)
344 painter.drawRect(0, 0, width, height)
346 if self.fChannelCount == 0:
347 return
349 meterPad = 0
350 meterPos = 0
351 meterSize = (height if self.fMeterOrientation == self.HORIZONTAL else width)/self.fChannelCount
353 # set pen/brush for levels
354 if self.fMeterStyle == self.STYLE_OPENAV:
355 colorTrans = QColor(self.fMeterColorBase)
356 colorTrans.setAlphaF(0.5)
357 painter.setBrush(colorTrans)
358 painter.setPen(QPen(self.fMeterColorBase, 1))
359 del colorTrans
360 meterPad += 2
361 meterSize -= 2
363 else:
364 painter.setPen(QPen(self.fMeterBackground, 0))
365 painter.setBrush(self.fMeterGradient)
367 # draw levels
368 for level in self.fChannelData:
369 if level == 0.0:
370 pass
371 elif self.fMeterOrientation == self.HORIZONTAL:
372 painter.drawRect(QRectF(0, meterPos, sqrt(level) * float(width), meterSize))
373 elif self.fMeterOrientation == self.VERTICAL:
374 painter.drawRect(QRectF(meterPos, height - sqrt(level) * float(height), meterSize, height))
376 meterPos += meterSize+meterPad
378 if not self.fMeterLinesEnabled:
379 return
381 # draw lines
382 if self.fMeterOrientation == self.HORIZONTAL:
383 # Variables
384 lsmall = float(width)
385 lfull = float(height - 1)
387 if self.fMeterStyle == self.STYLE_OPENAV:
388 painter.setPen(QColor(37, 37, 37, 100))
389 painter.drawLine(QLineF(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0))
390 painter.drawLine(QLineF(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0))
391 painter.drawLine(QLineF(lsmall * 0.75, 2, lsmall * 0.75, lfull-2.0))
393 if self.fChannelCount > 1:
394 painter.drawLine(QLineF(1, lfull/2-1, lsmall-1, lfull/2-1))
396 else:
397 # Base
398 painter.setBrush(Qt.black)
399 painter.setPen(QPen(self.fMeterColorBaseAlt, 1))
400 painter.drawLine(QLineF(lsmall * 0.25, 2, lsmall * 0.25, lfull-2.0))
401 painter.drawLine(QLineF(lsmall * 0.50, 2, lsmall * 0.50, lfull-2.0))
403 # Yellow
404 painter.setPen(QColor(110, 110, 15, 100))
405 painter.drawLine(QLineF(lsmall * 0.70, 2, lsmall * 0.70, lfull-2.0))
406 painter.drawLine(QLineF(lsmall * 0.83, 2, lsmall * 0.83, lfull-2.0))
408 # Orange
409 painter.setPen(QColor(180, 110, 15, 100))
410 painter.drawLine(QLineF(lsmall * 0.90, 2, lsmall * 0.90, lfull-2.0))
412 # Red
413 painter.setPen(QColor(110, 15, 15, 100))
414 painter.drawLine(QLineF(lsmall * 0.96, 2, lsmall * 0.96, lfull-2.0))
416 elif self.fMeterOrientation == self.VERTICAL:
417 # Variables
418 lsmall = float(height)
419 lfull = float(width - 1)
421 if self.fMeterStyle == self.STYLE_OPENAV:
422 painter.setPen(QColor(37, 37, 37, 100))
423 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25)))
424 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.50), lfull-2.0, lsmall - (lsmall * 0.50)))
425 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.75), lfull-2.0, lsmall - (lsmall * 0.75)))
427 if self.fChannelCount > 1:
428 painter.drawLine(QLineF(lfull/2-1, 1, lfull/2-1, lsmall-1))
430 else:
431 # Base
432 painter.setBrush(Qt.black)
433 painter.setPen(QPen(self.fMeterColorBaseAlt, 1))
434 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.25), lfull-2.0, lsmall - (lsmall * 0.25)))
435 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.50), lfull-2.0, lsmall - (lsmall * 0.50)))
437 # Yellow
438 painter.setPen(QColor(110, 110, 15, 100))
439 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.70), lfull-2.0, lsmall - (lsmall * 0.70)))
440 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.82), lfull-2.0, lsmall - (lsmall * 0.82)))
442 # Orange
443 painter.setPen(QColor(180, 110, 15, 100))
444 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.90), lfull-2.0, lsmall - (lsmall * 0.90)))
446 # Red
447 painter.setPen(QColor(110, 15, 15, 100))
448 painter.drawLine(QLineF(2, lsmall - (lsmall * 0.96), lfull-2.0, lsmall - (lsmall * 0.96)))
450 # -----------------------------------------------------------------------------------------------------------------
452 def resizeEvent(self, event):
453 QWidget.resizeEvent(self, event)
454 self.updateGrandientFinalStop()
456 # ---------------------------------------------------------------------------------------------------------------------