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.

296 lines
11KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Pixmap Dial, a custom Qt4 widget
  4. # Copyright (C) 2011-2013 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. # TODO - SLOTS
  20. from math import floor
  21. from PyQt5.QtCore import Qt, QPointF, QRectF, QTimer, QSize
  22. from PyQt5.QtGui import QColor, QConicalGradient, QFont, QFontMetrics
  23. from PyQt5.QtGui import QLinearGradient, QPainter, QPainterPath, QPen, QPixmap
  24. from PyQt5.QtWidgets import QDial
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Widget Class
  27. class PixmapDial(QDial):
  28. # enum CustomPaint
  29. CUSTOM_PAINT_NULL = 0
  30. CUSTOM_PAINT_CARLA_WET = 1
  31. CUSTOM_PAINT_CARLA_VOL = 2
  32. CUSTOM_PAINT_CARLA_L = 3
  33. CUSTOM_PAINT_CARLA_R = 4
  34. # enum Orientation
  35. HORIZONTAL = 0
  36. VERTICAL = 1
  37. HOVER_MIN = 0
  38. HOVER_MAX = 9
  39. def __init__(self, parent):
  40. QDial.__init__(self, parent)
  41. self.fPixmap = QPixmap(":/bitmaps/dial_01d.png")
  42. self.fPixmapNum = "01"
  43. self.fCustomPaint = self.CUSTOM_PAINT_NULL
  44. self.fHovered = False
  45. self.fHoverStep = self.HOVER_MIN
  46. if self.fPixmap.width() > self.fPixmap.height():
  47. self.fOrientation = self.HORIZONTAL
  48. else:
  49. self.fOrientation = self.VERTICAL
  50. self.fLabel = ""
  51. self.fLabelPos = QPointF(0.0, 0.0)
  52. self.fLabelFont = QFont()
  53. self.fLabelFont.setPointSize(6)
  54. self.fLabelWidth = 0
  55. self.fLabelHeight = 0
  56. self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
  57. if self.palette().window().color().lightness() > 100:
  58. # Light background
  59. c = self.palette().dark().color()
  60. self.fColor1 = c
  61. self.fColor2 = QColor(c.red(), c.green(), c.blue(), 0)
  62. self.fColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
  63. else:
  64. # Dark background
  65. self.fColor1 = QColor(0, 0, 0, 255)
  66. self.fColor2 = QColor(0, 0, 0, 0)
  67. self.fColorT = [Qt.white, Qt.darkGray]
  68. self.updateSizes()
  69. def getSize(self):
  70. return self.fSize
  71. def setCustomPaint(self, paint):
  72. self.fCustomPaint = paint
  73. self.fLabelPos.setY(self.fSize + self.fLabelHeight/2)
  74. self.update()
  75. def setEnabled(self, enabled):
  76. if self.isEnabled() != enabled:
  77. self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if enabled else "d"))
  78. self.updateSizes()
  79. self.update()
  80. QDial.setEnabled(self, enabled)
  81. def setLabel(self, label):
  82. self.fLabel = label
  83. self.fLabelWidth = QFontMetrics(self.fLabelFont).width(label)
  84. self.fLabelHeight = QFontMetrics(self.fLabelFont).height()
  85. self.fLabelPos.setX(float(self.fSize)/2.0 - float(self.fLabelWidth)/2.0)
  86. self.fLabelPos.setY(self.fSize + self.fLabelHeight)
  87. self.fLabelGradient.setColorAt(0.0, self.fColor1)
  88. self.fLabelGradient.setColorAt(0.6, self.fColor1)
  89. self.fLabelGradient.setColorAt(1.0, self.fColor2)
  90. self.fLabelGradient.setStart(0, float(self.fSize)/2.0)
  91. self.fLabelGradient.setFinalStop(0, self.fSize + self.fLabelHeight + 5)
  92. self.fLabelGradientRect = QRectF(float(self.fSize)/8.0, float(self.fSize)/2.0, float(self.fSize*6)/8.0, self.fSize+self.fLabelHeight+5)
  93. self.update()
  94. def setPixmap(self, pixmapId):
  95. self.fPixmapNum = "%02i" % pixmapId
  96. self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d"))
  97. if self.fPixmap.width() > self.fPixmap.height():
  98. self.fOrientation = self.HORIZONTAL
  99. else:
  100. self.fOrientation = self.VERTICAL
  101. self.updateSizes()
  102. self.update()
  103. def minimumSizeHint(self):
  104. return QSize(self.fSize, self.fSize)
  105. def sizeHint(self):
  106. return QSize(self.fSize, self.fSize)
  107. def updateSizes(self):
  108. self.fWidth = self.fPixmap.width()
  109. self.fHeight = self.fPixmap.height()
  110. if self.fWidth < 1:
  111. self.fWidth = 1
  112. if self.fHeight < 1:
  113. self.fHeight = 1
  114. if self.fOrientation == self.HORIZONTAL:
  115. self.fSize = self.fHeight
  116. self.fCount = self.fWidth / self.fHeight
  117. else:
  118. self.fSize = self.fWidth
  119. self.fCount = self.fHeight / self.fWidth
  120. self.setMinimumSize(self.fSize, self.fSize + self.fLabelHeight + 5)
  121. self.setMaximumSize(self.fSize, self.fSize + self.fLabelHeight + 5)
  122. def enterEvent(self, event):
  123. self.fHovered = True
  124. if self.fHoverStep == self.HOVER_MIN:
  125. self.fHoverStep = self.HOVER_MIN + 1
  126. QDial.enterEvent(self, event)
  127. def leaveEvent(self, event):
  128. self.fHovered = False
  129. if self.fHoverStep == self.HOVER_MAX:
  130. self.fHoverStep = self.HOVER_MAX - 1
  131. QDial.leaveEvent(self, event)
  132. def paintEvent(self, event):
  133. event.accept()
  134. painter = QPainter(self)
  135. painter.save()
  136. painter.setRenderHint(QPainter.Antialiasing, True)
  137. if self.fLabel:
  138. if self.fCustomPaint == self.CUSTOM_PAINT_NULL:
  139. painter.setPen(self.fColor2)
  140. painter.setBrush(self.fLabelGradient)
  141. painter.drawRect(self.fLabelGradientRect)
  142. painter.setFont(self.fLabelFont)
  143. painter.setPen(self.fColorT[0 if self.isEnabled() else 1])
  144. painter.drawText(self.fLabelPos, self.fLabel)
  145. if self.isEnabled():
  146. current = float(self.value() - self.minimum())
  147. divider = float(self.maximum() - self.minimum())
  148. if divider == 0.0:
  149. return
  150. value = current / divider
  151. target = QRectF(0.0, 0.0, self.fSize, self.fSize)
  152. per = int((self.fCount - 1) * value)
  153. if self.fOrientation == self.HORIZONTAL:
  154. xpos = self.fSize * per
  155. ypos = 0.0
  156. else:
  157. xpos = 0.0
  158. ypos = self.fSize * per
  159. source = QRectF(xpos, ypos, self.fSize, self.fSize)
  160. painter.drawPixmap(target, self.fPixmap, source)
  161. # Custom knobs (Dry/Wet and Volume)
  162. if self.fCustomPaint in (self.CUSTOM_PAINT_CARLA_WET, self.CUSTOM_PAINT_CARLA_VOL):
  163. # knob color
  164. colorGreen = QColor(0x5D, 0xE7, 0x3D, 191 + self.fHoverStep*7)
  165. colorBlue = QColor(0x3E, 0xB8, 0xBE, 191 + self.fHoverStep*7)
  166. # draw small circle
  167. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  168. ballPath = QPainterPath()
  169. ballPath.addEllipse(ballRect)
  170. #painter.drawRect(ballRect)
  171. tmpValue = (0.375 + 0.75*value)
  172. ballValue = tmpValue - floor(tmpValue)
  173. ballPoint = ballPath.pointAtPercent(ballValue)
  174. # draw arc
  175. startAngle = 216*16
  176. spanAngle = -252*16*value
  177. if self.fCustomPaint == self.CUSTOM_PAINT_CARLA_WET:
  178. painter.setBrush(colorBlue)
  179. painter.setPen(QPen(colorBlue, 0))
  180. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  181. gradient = QConicalGradient(15.5, 15.5, -45)
  182. gradient.setColorAt(0.0, colorBlue)
  183. gradient.setColorAt(0.125, colorBlue)
  184. gradient.setColorAt(0.625, colorGreen)
  185. gradient.setColorAt(0.75, colorGreen)
  186. gradient.setColorAt(0.76, colorGreen)
  187. gradient.setColorAt(1.0, colorGreen)
  188. painter.setBrush(gradient)
  189. painter.setPen(QPen(gradient, 3))
  190. else:
  191. painter.setBrush(colorBlue)
  192. painter.setPen(QPen(colorBlue, 0))
  193. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  194. painter.setBrush(colorBlue)
  195. painter.setPen(QPen(colorBlue, 3))
  196. painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)
  197. # Custom knobs (L and R)
  198. elif self.fCustomPaint in (self.CUSTOM_PAINT_CARLA_L, self.CUSTOM_PAINT_CARLA_R):
  199. # knob color
  200. color = QColor(0xAD + self.fHoverStep*5, 0xD5 + self.fHoverStep*4, 0x4B + self.fHoverStep*5)
  201. # draw small circle
  202. ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
  203. ballPath = QPainterPath()
  204. ballPath.addEllipse(ballRect)
  205. #painter.drawRect(ballRect)
  206. tmpValue = (0.375 + 0.75*value)
  207. ballValue = tmpValue - floor(tmpValue)
  208. ballPoint = ballPath.pointAtPercent(ballValue)
  209. painter.setBrush(color)
  210. painter.setPen(QPen(color, 0))
  211. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))
  212. # draw arc
  213. if self.fCustomPaint == self.CUSTOM_PAINT_CARLA_L:
  214. startAngle = 216*16
  215. spanAngle = -252.0*16*value
  216. elif self.fCustomPaint == self.CUSTOM_PAINT_CARLA_R:
  217. startAngle = 324.0*16
  218. spanAngle = 252.0*16*(1.0-value)
  219. else:
  220. return
  221. painter.setPen(QPen(color, 2))
  222. painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle)
  223. if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
  224. self.fHoverStep += 1 if self.fHovered else -1
  225. QTimer.singleShot(20, self, SLOT("update()"))
  226. else: # isEnabled()
  227. target = QRectF(0.0, 0.0, self.fSize, self.fSize)
  228. painter.drawPixmap(target, self.fPixmap, target)
  229. painter.restore()
  230. def resizeEvent(self, event):
  231. self.updateSizes()
  232. QDial.resizeEvent(self, event)