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.

279 lines
9.0KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Common 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 isnan
  20. from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF
  21. from PyQt5.QtGui import QColor, QFont, QLinearGradient, QPainter
  22. from PyQt5.QtWidgets import QDial
  23. # ---------------------------------------------------------------------------------------------------------------------
  24. # Widget Class
  25. # to be implemented by subclasses
  26. #def updateSizes(self):
  27. #def paintDial(self, painter):
  28. class CommonDial(QDial):
  29. # enum CustomPaintMode
  30. CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient)
  31. CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3)
  32. CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3)
  33. CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4)
  34. CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4)
  35. CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3)
  36. CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3)
  37. CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6)
  38. CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient
  39. # enum Orientation
  40. HORIZONTAL = 0
  41. VERTICAL = 1
  42. HOVER_MIN = 0
  43. HOVER_MAX = 9
  44. MODE_DEFAULT = 0
  45. MODE_LINEAR = 1
  46. # signals
  47. dragStateChanged = pyqtSignal(bool)
  48. realValueChanged = pyqtSignal(float)
  49. def __init__(self, parent, index):
  50. QDial.__init__(self, parent)
  51. self.fDialMode = self.MODE_LINEAR
  52. self.fMinimum = 0.0
  53. self.fMaximum = 1.0
  54. self.fRealValue = 0.0
  55. self.fPrecision = 10000
  56. self.fIsInteger = False
  57. self.fIsHovered = False
  58. self.fIsPressed = False
  59. self.fHoverStep = self.HOVER_MIN
  60. self.fLastDragPos = None
  61. self.fLastDragValue = 0.0
  62. self.fIndex = index
  63. self.fLabel = ""
  64. self.fLabelPos = QPointF(0.0, 0.0)
  65. self.fLabelFont = QFont(self.font())
  66. self.fLabelFont.setPixelSize(8)
  67. self.fLabelWidth = 0
  68. self.fLabelHeight = 0
  69. if self.palette().window().color().lightness() > 100:
  70. # Light background
  71. c = self.palette().dark().color()
  72. self.fLabelGradientColor1 = c
  73. self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0)
  74. self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
  75. else:
  76. # Dark background
  77. self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
  78. self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
  79. self.fLabelGradientColorT = [Qt.white, Qt.darkGray]
  80. self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
  81. self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1)
  82. self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1)
  83. self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2)
  84. self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0)
  85. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL
  86. self.fCustomPaintColor = QColor(0xff, 0xff, 0xff)
  87. # Fake internal value, custom precision
  88. QDial.setMinimum(self, 0)
  89. QDial.setMaximum(self, self.fPrecision)
  90. QDial.setValue(self, 0)
  91. self.valueChanged.connect(self.slot_valueChanged)
  92. def forceWhiteLabelGradientText(self):
  93. self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
  94. self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
  95. self.fLabelGradientColorT = [Qt.white, Qt.darkGray]
  96. def setLabelColor(self, enabled, disabled):
  97. self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
  98. self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
  99. self.fLabelGradientColorT = [enabled, disabled]
  100. def getIndex(self):
  101. return self.fIndex
  102. def setIndex(self, index):
  103. self.fIndex = index
  104. def setPrecision(self, value, isInteger):
  105. self.fPrecision = value
  106. self.fIsInteger = isInteger
  107. QDial.setMaximum(self, int(value))
  108. def setMinimum(self, value):
  109. self.fMinimum = value
  110. def setMaximum(self, value):
  111. self.fMaximum = value
  112. def rvalue(self):
  113. return self.fRealValue
  114. def setValue(self, value, emitSignal=False):
  115. if self.fRealValue == value or isnan(value):
  116. return
  117. if value <= self.fMinimum:
  118. qtValue = 0
  119. self.fRealValue = self.fMinimum
  120. elif value >= self.fMaximum:
  121. qtValue = int(self.fPrecision)
  122. self.fRealValue = self.fMaximum
  123. else:
  124. qtValue = round(float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum) * self.fPrecision)
  125. self.fRealValue = value
  126. # Block change signal, we'll handle it ourselves
  127. self.blockSignals(True)
  128. QDial.setValue(self, qtValue)
  129. self.blockSignals(False)
  130. if emitSignal:
  131. self.realValueChanged.emit(self.fRealValue)
  132. def setCustomPaintMode(self, paintMode):
  133. if self.fCustomPaintMode == paintMode:
  134. return
  135. self.fCustomPaintMode = paintMode
  136. self.update()
  137. def setCustomPaintColor(self, color):
  138. if self.fCustomPaintColor == color:
  139. return
  140. self.fCustomPaintColor = color
  141. self.update()
  142. def setLabel(self, label):
  143. if self.fLabel == label:
  144. return
  145. self.fLabel = label
  146. self.updateSizes()
  147. self.update()
  148. @pyqtSlot(int)
  149. def slot_valueChanged(self, value):
  150. self.fRealValue = float(value)/self.fPrecision * (self.fMaximum - self.fMinimum) + self.fMinimum
  151. self.realValueChanged.emit(self.fRealValue)
  152. def enterEvent(self, event):
  153. self.fIsHovered = True
  154. if self.fHoverStep == self.HOVER_MIN:
  155. self.fHoverStep = self.HOVER_MIN + 1
  156. QDial.enterEvent(self, event)
  157. def leaveEvent(self, event):
  158. self.fIsHovered = False
  159. if self.fHoverStep == self.HOVER_MAX:
  160. self.fHoverStep = self.HOVER_MAX - 1
  161. QDial.leaveEvent(self, event)
  162. def mousePressEvent(self, event):
  163. if self.fDialMode == self.MODE_DEFAULT:
  164. QDial.mousePressEvent(self, event)
  165. return
  166. if event.button() == Qt.LeftButton:
  167. self.fIsPressed = True
  168. self.fLastDragPos = event.pos()
  169. self.fLastDragValue = self.fRealValue
  170. self.dragStateChanged.emit(True)
  171. def mouseMoveEvent(self, event):
  172. if self.fDialMode == self.MODE_DEFAULT:
  173. QDial.mouseMoveEvent(self, event)
  174. return
  175. if not self.fIsPressed:
  176. return
  177. diff = (self.fMaximum - self.fMinimum) / 4.0
  178. pos = event.pos()
  179. dx = diff * float(pos.x() - self.fLastDragPos.x()) / self.width()
  180. dy = diff * float(pos.y() - self.fLastDragPos.y()) / self.height()
  181. value = self.fLastDragValue + dx - dy
  182. if value < self.fMinimum:
  183. value = self.fMinimum
  184. elif value > self.fMaximum:
  185. value = self.fMaximum
  186. elif self.fIsInteger:
  187. value = float(round(value))
  188. self.setValue(value, True)
  189. def mouseReleaseEvent(self, event):
  190. if self.fDialMode == self.MODE_DEFAULT:
  191. QDial.mouseReleaseEvent(self, event)
  192. return
  193. if self.fIsPressed:
  194. self.fIsPressed = False
  195. self.dragStateChanged.emit(False)
  196. def paintEvent(self, event):
  197. painter = QPainter(self)
  198. event.accept()
  199. painter.save()
  200. painter.setRenderHint(QPainter.Antialiasing, True)
  201. if self.fLabel:
  202. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
  203. painter.setPen(self.fLabelGradientColor2)
  204. painter.setBrush(self.fLabelGradient)
  205. painter.drawRect(self.fLabelGradientRect)
  206. painter.setFont(self.fLabelFont)
  207. painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
  208. painter.drawText(self.fLabelPos, self.fLabel)
  209. self.paintDial(painter)
  210. painter.restore()
  211. def resizeEvent(self, event):
  212. QDial.resizeEvent(self, event)
  213. self.updateSizes()
  214. # ---------------------------------------------------------------------------------------------------------------------