Cleanup
[carla.git] / source / frontend / widgets / scalabledial.py
blob0d842c73880c4ea2a8e6cdab7afbba01d33a36fa
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 cos, floor, pi, sin
10 from qt_compat import qt_config
12 if qt_config == 5:
13 from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
14 from PyQt5.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
15 from PyQt5.QtSvg import QSvgWidget
16 elif qt_config == 6:
17 from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
18 from PyQt6.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
19 from PyQt6.QtSvgWidgets import QSvgWidget
21 from .commondial import CommonDial
22 from carla_shared import fontMetricsHorizontalAdvance
24 # ---------------------------------------------------------------------------------------------------------------------
25 # Widget Class
27 class ScalableDial(CommonDial):
28 def __init__(self, parent, index=0):
29 CommonDial.__init__(self, parent, index)
31 self.fImage = QSvgWidget(":/scalable/dial_03.svg")
32 self.fImageNum = "01"
34 if self.fImage.sizeHint().width() > self.fImage.sizeHint().height():
35 self.fImageOrientation = self.HORIZONTAL
36 else:
37 self.fImageOrientation = self.VERTICAL
39 self.updateSizes()
41 def getBaseSize(self):
42 return self.fImageBaseSize
44 def updateSizes(self):
45 if isinstance(self.fImage, QPixmap):
46 self.fImageWidth = self.fImage.width()
47 self.fImageHeight = self.fImage.height()
48 else:
49 self.fImageWidth = self.fImage.sizeHint().width()
50 self.fImageHeight = self.fImage.sizeHint().height()
52 if self.fImageWidth < 1:
53 self.fImageWidth = 1
55 if self.fImageHeight < 1:
56 self.fImageHeight = 1
58 if self.fImageOrientation == self.HORIZONTAL:
59 self.fImageBaseSize = self.fImageHeight
60 self.fImageLayersCount = self.fImageWidth / self.fImageHeight
61 else:
62 self.fImageBaseSize = self.fImageWidth
63 self.fImageLayersCount = self.fImageHeight / self.fImageWidth
65 self.setMinimumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)
66 self.setMaximumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)
68 if not self.fLabel:
69 self.fLabelHeight = 0
70 self.fLabelWidth = 0
71 return
73 metrics = QFontMetrics(self.fLabelFont)
74 self.fLabelWidth = fontMetricsHorizontalAdvance(metrics, self.fLabel)
75 self.fLabelHeight = metrics.height()
77 self.fLabelPos.setX(float(self.fImageBaseSize)/2.0 - float(self.fLabelWidth)/2.0)
79 if self.fImageNum in ("01", "02", "07", "08", "09", "10"):
80 self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight)
81 elif self.fImageNum in ("11",):
82 self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight*2/3)
83 else:
84 self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight/2)
86 self.fLabelGradient.setStart(0, float(self.fImageBaseSize)/2.0)
87 self.fLabelGradient.setFinalStop(0, self.fImageBaseSize + self.fLabelHeight + 5)
89 self.fLabelGradientRect = QRectF(float(self.fImageBaseSize)/8.0, float(self.fImageBaseSize)/2.0,
90 float(self.fImageBaseSize*3)/4.0, self.fImageBaseSize+self.fLabelHeight+5)
92 def setImage(self, imageId):
93 self.fImageNum = "%02i" % imageId
94 if imageId in (2,6,7,8,9,10,11,12,13):
95 img = ":/bitmaps/dial_%s%s.png" % (self.fImageNum, "" if self.isEnabled() else "d")
96 else:
97 img = ":/scalable/dial_%s%s.svg" % (self.fImageNum, "" if self.isEnabled() else "d")
99 if img.endswith(".png"):
100 if not isinstance(self.fImage, QPixmap):
101 self.fImage = QPixmap()
102 else:
103 if not isinstance(self.fImage, QSvgWidget):
104 self.fImage = QSvgWidget()
106 self.fImage.load(img)
108 if self.fImage.width() > self.fImage.height():
109 self.fImageOrientation = self.HORIZONTAL
110 else:
111 self.fImageOrientation = self.VERTICAL
113 # special svgs
114 if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
115 # reserved for carla-wet, carla-vol, carla-pan and color
116 if self.fImageNum == "03":
117 self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR
119 # reserved for carla-L and carla-R
120 elif self.fImageNum == "04":
121 self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L
123 # reserved for zita
124 elif self.fImageNum == "06":
125 self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA
127 self.updateSizes()
128 self.update()
130 @pyqtSlot()
131 def slot_updateImage(self):
132 self.setImage(int(self.fImageNum))
134 def minimumSizeHint(self):
135 return QSize(self.fImageBaseSize, self.fImageBaseSize)
137 def sizeHint(self):
138 return QSize(self.fImageBaseSize, self.fImageBaseSize)
140 def changeEvent(self, event):
141 CommonDial.changeEvent(self, event)
143 # Force svg update if enabled state changes
144 if event.type() == QEvent.EnabledChange:
145 self.slot_updateImage()
147 def paintDial(self, painter):
148 if self.isEnabled():
149 normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
150 curLayer = int((self.fImageLayersCount - 1) * normValue)
152 if self.fImageOrientation == self.HORIZONTAL:
153 xpos = self.fImageBaseSize * curLayer
154 ypos = 0.0
155 else:
156 xpos = 0.0
157 ypos = self.fImageBaseSize * curLayer
159 source = QRectF(xpos, ypos, self.fImageBaseSize, self.fImageBaseSize)
161 if isinstance(self.fImage, QPixmap):
162 target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
163 painter.drawPixmap(target, self.fImage, source)
164 else:
165 self.fImage.renderer().render(painter, source)
167 # Custom knobs (Dry/Wet and Volume)
168 if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
169 # knob color
170 colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6)
171 colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6)
173 # draw small circle
174 ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
175 ballPath = QPainterPath()
176 ballPath.addEllipse(ballRect)
177 #painter.drawRect(ballRect)
178 tmpValue = (0.375 + 0.75*normValue)
179 ballValue = tmpValue - floor(tmpValue)
180 ballPoint = ballPath.pointAtPercent(ballValue)
182 # draw arc
183 startAngle = 218*16
184 spanAngle = -255*16*normValue
186 if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET:
187 painter.setBrush(colorBlue)
188 painter.setPen(QPen(colorBlue, 0))
189 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
191 gradient = QConicalGradient(15.5, 15.5, -45)
192 gradient.setColorAt(0.0, colorBlue)
193 gradient.setColorAt(0.125, colorBlue)
194 gradient.setColorAt(0.625, colorGreen)
195 gradient.setColorAt(0.75, colorGreen)
196 gradient.setColorAt(0.76, colorGreen)
197 gradient.setColorAt(1.0, colorGreen)
198 painter.setBrush(gradient)
199 painter.setPen(QPen(gradient, 3))
201 else:
202 painter.setBrush(colorBlue)
203 painter.setPen(QPen(colorBlue, 0))
204 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
206 painter.setBrush(colorBlue)
207 painter.setPen(QPen(colorBlue, 3))
209 painter.drawArc(QRectF(4.0, 4.0, 26.0, 26.0), int(startAngle), int(spanAngle))
211 # Custom knobs (L and R)
212 elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
213 # knob color
214 color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6)
216 # draw small circle
217 ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
218 ballPath = QPainterPath()
219 ballPath.addEllipse(ballRect)
220 #painter.drawRect(ballRect)
221 tmpValue = (0.375 + 0.75*normValue)
222 ballValue = tmpValue - floor(tmpValue)
223 ballPoint = ballPath.pointAtPercent(ballValue)
225 painter.setBrush(color)
226 painter.setPen(QPen(color, 0))
227 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))
229 # draw arc
230 if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L:
231 startAngle = 218*16
232 spanAngle = -255*16*normValue
233 else:
234 startAngle = 322.0*16
235 spanAngle = 255.0*16*(1.0-normValue)
237 painter.setPen(QPen(color, 2.5))
238 painter.drawArc(QRectF(3.5, 3.5, 22.0, 22.0), int(startAngle), int(spanAngle))
240 # Custom knobs (Color)
241 elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
242 # knob color
243 color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6)
245 # draw small circle
246 ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
247 ballPath = QPainterPath()
248 ballPath.addEllipse(ballRect)
249 tmpValue = (0.375 + 0.75*normValue)
250 ballValue = tmpValue - floor(tmpValue)
251 ballPoint = ballPath.pointAtPercent(ballValue)
253 # draw arc
254 startAngle = 218*16
255 spanAngle = -255*16*normValue
257 painter.setBrush(color)
258 painter.setPen(QPen(color, 0))
259 painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
261 painter.setBrush(color)
262 painter.setPen(QPen(color, 3))
263 painter.drawArc(QRectF(4.0, 4.8, 26.0, 26.0), int(startAngle), int(spanAngle))
265 # Custom knobs (Zita)
266 elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA:
267 a = normValue * pi * 1.5 - 2.35
268 r = 10.0
269 x = 10.5
270 y = 10.5
271 x += r * sin(a)
272 y -= r * cos(a)
273 painter.setBrush(Qt.black)
274 painter.setPen(QPen(Qt.black, 2))
275 painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y))
277 # Custom knobs
278 else:
279 return
281 if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
282 self.fHoverStep += 1 if self.fIsHovered else -1
283 QTimer.singleShot(20, self.update)
285 else: # isEnabled()
286 target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
287 if isinstance(self.fImage, QPixmap):
288 painter.drawPixmap(target, self.fImage, target)
289 else:
290 self.fImage.renderer().render(painter, target)
292 # ---------------------------------------------------------------------------------------------------------------------