Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

293 lines
12KB

  1. #!/usr/bin/env python3
  2. # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  3. # SPDX-License-Identifier: GPL-2.0-or-later
  4. # ---------------------------------------------------------------------------------------------------------------------
  5. # Imports (Global)
  6. from math import cos, floor, pi, sin
  7. from qt_compat import qt_config
  8. if qt_config == 5:
  9. from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
  10. from PyQt5.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
  11. from PyQt5.QtSvg import QSvgWidget
  12. elif qt_config == 6:
  13. from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
  14. from PyQt6.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
  15. from PyQt6.QtSvgWidgets import QSvgWidget
  16. from .commondial import CommonDial
  17. from carla_shared import fontMetricsHorizontalAdvance
  18. # ---------------------------------------------------------------------------------------------------------------------
  19. # Widget Class
  20. class ScalableDial(CommonDial):
  21. def __init__(self, parent, index=0):
  22. CommonDial.__init__(self, parent, index)
  23. self.fImage = QSvgWidget(":/scalable/dial_03.svg")
  24. self.fImageNum = "01"
  25. if self.fImage.sizeHint().width() > self.fImage.sizeHint().height():
  26. self.fImageOrientation = self.HORIZONTAL
  27. else:
  28. self.fImageOrientation = self.VERTICAL
  29. self.updateSizes()
  30. def getBaseSize(self):
  31. return self.fImageBaseSize
  32. def updateSizes(self):
  33. if isinstance(self.fImage, QPixmap):
  34. self.fImageWidth = self.fImage.width()
  35. self.fImageHeight = self.fImage.height()
  36. else:
  37. self.fImageWidth = self.fImage.sizeHint().width()
  38. self.fImageHeight = self.fImage.sizeHint().height()
  39. if self.fImageWidth < 1:
  40. self.fImageWidth = 1
  41. if self.fImageHeight < 1:
  42. self.fImageHeight = 1
  43. if self.fImageOrientation == self.HORIZONTAL:
  44. self.fImageBaseSize = self.fImageHeight
  45. self.fImageLayersCount = self.fImageWidth / self.fImageHeight
  46. else:
  47. self.fImageBaseSize = self.fImageWidth
  48. self.fImageLayersCount = self.fImageHeight / self.fImageWidth
  49. self.setMinimumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)
  50. self.setMaximumSize(self.fImageBaseSize, self.fImageBaseSize + self.fLabelHeight + 5)
  51. if not self.fLabel:
  52. self.fLabelHeight = 0
  53. self.fLabelWidth = 0
  54. return
  55. metrics = QFontMetrics(self.fLabelFont)
  56. self.fLabelWidth = fontMetricsHorizontalAdvance(metrics, self.fLabel)
  57. self.fLabelHeight = metrics.height()
  58. self.fLabelPos.setX(float(self.fImageBaseSize)/2.0 - float(self.fLabelWidth)/2.0)
  59. if self.fImageNum in ("01", "02", "07", "08", "09", "10"):
  60. self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight)
  61. elif self.fImageNum in ("11",):
  62. self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight*2/3)
  63. else:
  64. self.fLabelPos.setY(self.fImageBaseSize + self.fLabelHeight/2)
  65. self.fLabelGradient.setStart(0, float(self.fImageBaseSize)/2.0)
  66. self.fLabelGradient.setFinalStop(0, self.fImageBaseSize + self.fLabelHeight + 5)
  67. self.fLabelGradientRect = QRectF(float(self.fImageBaseSize)/8.0, float(self.fImageBaseSize)/2.0,
  68. float(self.fImageBaseSize*3)/4.0, self.fImageBaseSize+self.fLabelHeight+5)
  69. def setImage(self, imageId):
  70. self.fImageNum = "%02i" % imageId
  71. if imageId in (2,6,7,8,9,10,11,12,13):
  72. img = ":/bitmaps/dial_%s%s.png" % (self.fImageNum, "" if self.isEnabled() else "d")
  73. else:
  74. img = ":/scalable/dial_%s%s.svg" % (self.fImageNum, "" if self.isEnabled() else "d")
  75. if img.endswith(".png"):
  76. if not isinstance(self.fImage, QPixmap):
  77. self.fImage = QPixmap()
  78. else:
  79. if not isinstance(self.fImage, QSvgWidget):
  80. self.fImage = QSvgWidget()
  81. self.fImage.load(img)
  82. if self.fImage.width() > self.fImage.height():
  83. self.fImageOrientation = self.HORIZONTAL
  84. else:
  85. self.fImageOrientation = self.VERTICAL
  86. # special svgs
  87. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
  88. # reserved for carla-wet, carla-vol, carla-pan and color
  89. if self.fImageNum == "03":
  90. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR
  91. # reserved for carla-L and carla-R
  92. elif self.fImageNum == "04":
  93. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L
  94. # reserved for zita
  95. elif self.fImageNum == "06":
  96. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA
  97. self.updateSizes()
  98. self.update()
  99. @pyqtSlot()
  100. def slot_updateImage(self):
  101. self.setImage(int(self.fImageNum))
  102. def minimumSizeHint(self):
  103. return QSize(self.fImageBaseSize, self.fImageBaseSize)
  104. def sizeHint(self):
  105. return QSize(self.fImageBaseSize, self.fImageBaseSize)
  106. def changeEvent(self, event):
  107. CommonDial.changeEvent(self, event)
  108. # Force svg update if enabled state changes
  109. if event.type() == QEvent.EnabledChange:
  110. self.slot_updateImage()
  111. def paintDial(self, painter):
  112. if self.isEnabled():
  113. normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  114. curLayer = int((self.fImageLayersCount - 1) * normValue)
  115. if self.fImageOrientation == self.HORIZONTAL:
  116. xpos = self.fImageBaseSize * curLayer
  117. ypos = 0.0
  118. else:
  119. xpos = 0.0
  120. ypos = self.fImageBaseSize * curLayer
  121. source = QRectF(xpos, ypos, self.fImageBaseSize, self.fImageBaseSize)
  122. if isinstance(self.fImage, QPixmap):
  123. target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
  124. painter.drawPixmap(target, self.fImage, source)
  125. else:
  126. self.fImage.renderer().render(painter, source)
  127. # Custom knobs (Dry/Wet and Volume)
  128. if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
  129. # knob color
  130. colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6)
  131. colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6)
  132. # draw small circle
  133. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  134. ballPath = QPainterPath()
  135. ballPath.addEllipse(ballRect)
  136. #painter.drawRect(ballRect)
  137. tmpValue = (0.375 + 0.75*normValue)
  138. ballValue = tmpValue - floor(tmpValue)
  139. ballPoint = ballPath.pointAtPercent(ballValue)
  140. # draw arc
  141. startAngle = 218*16
  142. spanAngle = -255*16*normValue
  143. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET:
  144. painter.setBrush(colorBlue)
  145. painter.setPen(QPen(colorBlue, 0))
  146. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  147. gradient = QConicalGradient(15.5, 15.5, -45)
  148. gradient.setColorAt(0.0, colorBlue)
  149. gradient.setColorAt(0.125, colorBlue)
  150. gradient.setColorAt(0.625, colorGreen)
  151. gradient.setColorAt(0.75, colorGreen)
  152. gradient.setColorAt(0.76, colorGreen)
  153. gradient.setColorAt(1.0, colorGreen)
  154. painter.setBrush(gradient)
  155. painter.setPen(QPen(gradient, 3))
  156. else:
  157. painter.setBrush(colorBlue)
  158. painter.setPen(QPen(colorBlue, 0))
  159. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  160. painter.setBrush(colorBlue)
  161. painter.setPen(QPen(colorBlue, 3))
  162. painter.drawArc(QRectF(4.0, 4.0, 26.0, 26.0), int(startAngle), int(spanAngle))
  163. # Custom knobs (L and R)
  164. elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
  165. # knob color
  166. color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6)
  167. # draw small circle
  168. ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
  169. ballPath = QPainterPath()
  170. ballPath.addEllipse(ballRect)
  171. #painter.drawRect(ballRect)
  172. tmpValue = (0.375 + 0.75*normValue)
  173. ballValue = tmpValue - floor(tmpValue)
  174. ballPoint = ballPath.pointAtPercent(ballValue)
  175. painter.setBrush(color)
  176. painter.setPen(QPen(color, 0))
  177. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))
  178. # draw arc
  179. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L:
  180. startAngle = 218*16
  181. spanAngle = -255*16*normValue
  182. else:
  183. startAngle = 322.0*16
  184. spanAngle = 255.0*16*(1.0-normValue)
  185. painter.setPen(QPen(color, 2.5))
  186. painter.drawArc(QRectF(3.5, 3.5, 22.0, 22.0), int(startAngle), int(spanAngle))
  187. # Custom knobs (Color)
  188. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
  189. # knob color
  190. color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6)
  191. # draw small circle
  192. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  193. ballPath = QPainterPath()
  194. ballPath.addEllipse(ballRect)
  195. tmpValue = (0.375 + 0.75*normValue)
  196. ballValue = tmpValue - floor(tmpValue)
  197. ballPoint = ballPath.pointAtPercent(ballValue)
  198. # draw arc
  199. startAngle = 218*16
  200. spanAngle = -255*16*normValue
  201. painter.setBrush(color)
  202. painter.setPen(QPen(color, 0))
  203. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  204. painter.setBrush(color)
  205. painter.setPen(QPen(color, 3))
  206. painter.drawArc(QRectF(4.0, 4.8, 26.0, 26.0), int(startAngle), int(spanAngle))
  207. # Custom knobs (Zita)
  208. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA:
  209. a = normValue * pi * 1.5 - 2.35
  210. r = 10.0
  211. x = 10.5
  212. y = 10.5
  213. x += r * sin(a)
  214. y -= r * cos(a)
  215. painter.setBrush(Qt.black)
  216. painter.setPen(QPen(Qt.black, 2))
  217. painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y))
  218. # Custom knobs
  219. else:
  220. return
  221. if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
  222. self.fHoverStep += 1 if self.fIsHovered else -1
  223. QTimer.singleShot(20, self.update)
  224. else: # isEnabled()
  225. target = QRectF(0.0, 0.0, self.fImageBaseSize, self.fImageBaseSize)
  226. if isinstance(self.fImage, QPixmap):
  227. painter.drawPixmap(target, self.fImage, target)
  228. else:
  229. self.fImage.renderer().render(painter, target)
  230. # ---------------------------------------------------------------------------------------------------------------------