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.

278 lines
11KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Pixmap Dial, a custom Qt widget
  4. # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the doc/GPL.txt file.
  17. # ---------------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from math import cos, floor, pi, sin
  20. from PyQt5.QtCore import pyqtSlot, Qt, QEvent, QPointF, QRectF, QTimer, QSize
  21. from PyQt5.QtGui import QColor, QConicalGradient, QFontMetrics, QPainterPath, QPen, QPixmap
  22. from PyQt5.QtWidgets import QDial
  23. from .commondial import CommonDial
  24. from carla_shared import fontMetricsHorizontalAdvance
  25. # ---------------------------------------------------------------------------------------------------------------------
  26. # Widget Class
  27. class PixmapDial(CommonDial):
  28. def __init__(self, parent, index=0):
  29. CommonDial.__init__(self, parent, index)
  30. self.fPixmap = QPixmap(":/bitmaps/dial_01d.png")
  31. self.fPixmapNum = "01"
  32. if self.fPixmap.width() > self.fPixmap.height():
  33. self.fPixmapOrientation = self.HORIZONTAL
  34. else:
  35. self.fPixmapOrientation = self.VERTICAL
  36. self.updateSizes()
  37. def getBaseSize(self):
  38. return self.fPixmapBaseSize
  39. def updateSizes(self):
  40. self.fPixmapWidth = self.fPixmap.width()
  41. self.fPixmapHeight = self.fPixmap.height()
  42. if self.fPixmapWidth < 1:
  43. self.fPixmapWidth = 1
  44. if self.fPixmapHeight < 1:
  45. self.fPixmapHeight = 1
  46. if self.fPixmapOrientation == self.HORIZONTAL:
  47. self.fPixmapBaseSize = self.fPixmapHeight
  48. self.fPixmapLayersCount = self.fPixmapWidth / self.fPixmapHeight
  49. else:
  50. self.fPixmapBaseSize = self.fPixmapWidth
  51. self.fPixmapLayersCount = self.fPixmapHeight / self.fPixmapWidth
  52. self.setMinimumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5)
  53. self.setMaximumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5)
  54. if not self.fLabel:
  55. self.fLabelHeight = 0
  56. self.fLabelWidth = 0
  57. return
  58. metrics = QFontMetrics(self.fLabelFont)
  59. self.fLabelWidth = fontMetricsHorizontalAdvance(metrics, self.fLabel)
  60. self.fLabelHeight = metrics.height()
  61. self.fLabelPos.setX(float(self.fPixmapBaseSize)/2.0 - float(self.fLabelWidth)/2.0)
  62. if self.fPixmapNum in ("01", "02", "07", "08", "09", "10"):
  63. self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight)
  64. elif self.fPixmapNum in ("11",):
  65. self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight*2/3)
  66. else:
  67. self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight/2)
  68. self.fLabelGradient.setStart(0, float(self.fPixmapBaseSize)/2.0)
  69. self.fLabelGradient.setFinalStop(0, self.fPixmapBaseSize + self.fLabelHeight + 5)
  70. self.fLabelGradientRect = QRectF(float(self.fPixmapBaseSize)/8.0, float(self.fPixmapBaseSize)/2.0, float(self.fPixmapBaseSize*3)/4.0, self.fPixmapBaseSize+self.fLabelHeight+5)
  71. def setPixmap(self, pixmapId):
  72. self.fPixmapNum = "%02i" % pixmapId
  73. self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d"))
  74. if self.fPixmap.width() > self.fPixmap.height():
  75. self.fPixmapOrientation = self.HORIZONTAL
  76. else:
  77. self.fPixmapOrientation = self.VERTICAL
  78. # special pixmaps
  79. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
  80. # reserved for carla-wet, carla-vol, carla-pan and color
  81. if self.fPixmapNum == "03":
  82. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR
  83. # reserved for carla-L and carla-R
  84. elif self.fPixmapNum == "04":
  85. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L
  86. # reserved for zita
  87. elif self.fPixmapNum == "06":
  88. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA
  89. self.updateSizes()
  90. self.update()
  91. @pyqtSlot()
  92. def slot_updatePixmap(self):
  93. self.setPixmap(int(self.fPixmapNum))
  94. def minimumSizeHint(self):
  95. return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize)
  96. def sizeHint(self):
  97. return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize)
  98. def changeEvent(self, event):
  99. CommonDial.changeEvent(self, event)
  100. # Force pixmap update if enabled state changes
  101. if event.type() == QEvent.EnabledChange:
  102. self.setPixmap(int(self.fPixmapNum))
  103. def paintDial(self, painter):
  104. if self.isEnabled():
  105. normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  106. target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
  107. curLayer = int((self.fPixmapLayersCount - 1) * normValue)
  108. if self.fPixmapOrientation == self.HORIZONTAL:
  109. xpos = self.fPixmapBaseSize * curLayer
  110. ypos = 0.0
  111. else:
  112. xpos = 0.0
  113. ypos = self.fPixmapBaseSize * curLayer
  114. source = QRectF(xpos, ypos, self.fPixmapBaseSize, self.fPixmapBaseSize)
  115. painter.drawPixmap(target, self.fPixmap, source)
  116. # Custom knobs (Dry/Wet and Volume)
  117. if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
  118. # knob color
  119. colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6)
  120. colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6)
  121. # draw small circle
  122. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  123. ballPath = QPainterPath()
  124. ballPath.addEllipse(ballRect)
  125. #painter.drawRect(ballRect)
  126. tmpValue = (0.375 + 0.75*normValue)
  127. ballValue = tmpValue - floor(tmpValue)
  128. ballPoint = ballPath.pointAtPercent(ballValue)
  129. # draw arc
  130. startAngle = 216*16
  131. spanAngle = -252*16*normValue
  132. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET:
  133. painter.setBrush(colorBlue)
  134. painter.setPen(QPen(colorBlue, 0))
  135. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  136. gradient = QConicalGradient(15.5, 15.5, -45)
  137. gradient.setColorAt(0.0, colorBlue)
  138. gradient.setColorAt(0.125, colorBlue)
  139. gradient.setColorAt(0.625, colorGreen)
  140. gradient.setColorAt(0.75, colorGreen)
  141. gradient.setColorAt(0.76, colorGreen)
  142. gradient.setColorAt(1.0, colorGreen)
  143. painter.setBrush(gradient)
  144. painter.setPen(QPen(gradient, 3))
  145. else:
  146. painter.setBrush(colorBlue)
  147. painter.setPen(QPen(colorBlue, 0))
  148. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  149. painter.setBrush(colorBlue)
  150. painter.setPen(QPen(colorBlue, 3))
  151. painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)
  152. # Custom knobs (L and R)
  153. elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
  154. # knob color
  155. color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6)
  156. # draw small circle
  157. ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
  158. ballPath = QPainterPath()
  159. ballPath.addEllipse(ballRect)
  160. #painter.drawRect(ballRect)
  161. tmpValue = (0.375 + 0.75*normValue)
  162. ballValue = tmpValue - floor(tmpValue)
  163. ballPoint = ballPath.pointAtPercent(ballValue)
  164. painter.setBrush(color)
  165. painter.setPen(QPen(color, 0))
  166. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))
  167. # draw arc
  168. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L:
  169. startAngle = 216*16
  170. spanAngle = -252.0*16*normValue
  171. else:
  172. startAngle = 324.0*16
  173. spanAngle = 252.0*16*(1.0-normValue)
  174. painter.setPen(QPen(color, 2))
  175. painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle)
  176. # Custom knobs (Color)
  177. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
  178. # knob color
  179. color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6)
  180. # draw small circle
  181. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  182. ballPath = QPainterPath()
  183. ballPath.addEllipse(ballRect)
  184. tmpValue = (0.375 + 0.75*normValue)
  185. ballValue = tmpValue - floor(tmpValue)
  186. ballPoint = ballPath.pointAtPercent(ballValue)
  187. # draw arc
  188. startAngle = 216*16
  189. spanAngle = -252*16*normValue
  190. painter.setBrush(color)
  191. painter.setPen(QPen(color, 0))
  192. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  193. painter.setBrush(color)
  194. painter.setPen(QPen(color, 3))
  195. painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)
  196. # Custom knobs (Zita)
  197. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA:
  198. a = normValue * pi * 1.5 - 2.35
  199. r = 10.0
  200. x = 10.5
  201. y = 10.5
  202. x += r * sin(a)
  203. y -= r * cos(a)
  204. painter.setBrush(Qt.black)
  205. painter.setPen(QPen(Qt.black, 2))
  206. painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y))
  207. # Custom knobs
  208. else:
  209. painter.restore()
  210. return
  211. if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
  212. self.fHoverStep += 1 if self.fIsHovered else -1
  213. QTimer.singleShot(20, self.update)
  214. else: # isEnabled()
  215. target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
  216. painter.drawPixmap(target, self.fPixmap, target)
  217. # ---------------------------------------------------------------------------------------------------------------------