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.

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