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.

pixmapdial.py 17KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Pixmap Dial, a custom Qt4 widget
  4. # Copyright (C) 2011-2014 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 (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. from math import cos, floor, pi, sin
  23. if config_UseQt5:
  24. from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF, QTimer, QSize
  25. from PyQt5.QtGui import QColor, QConicalGradient, QFont, QFontMetrics
  26. from PyQt5.QtGui import QLinearGradient, QPainter, QPainterPath, QPen, QPixmap
  27. from PyQt5.QtWidgets import QDial
  28. else:
  29. from PyQt4.QtCore import pyqtSignal, pyqtSlot, Qt, QPointF, QRectF, QTimer, QSize
  30. from PyQt4.QtGui import QColor, QConicalGradient, QFont, QFontMetrics
  31. from PyQt4.QtGui import QDial, QLinearGradient, QPainter, QPainterPath, QPen, QPixmap
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Widget Class
  34. class PixmapDial(QDial):
  35. # enum CustomPaintMode
  36. CUSTOM_PAINT_MODE_NULL = 0 # default (NOTE: only this mode has label gradient)
  37. CUSTOM_PAINT_MODE_CARLA_WET = 1 # color blue-green gradient (reserved #3)
  38. CUSTOM_PAINT_MODE_CARLA_VOL = 2 # color blue (reserved #3)
  39. CUSTOM_PAINT_MODE_CARLA_L = 3 # color yellow (reserved #4)
  40. CUSTOM_PAINT_MODE_CARLA_R = 4 # color yellow (reserved #4)
  41. CUSTOM_PAINT_MODE_CARLA_PAN = 5 # color yellow (reserved #3)
  42. CUSTOM_PAINT_MODE_COLOR = 6 # color, selectable (reserved #3)
  43. CUSTOM_PAINT_MODE_ZITA = 7 # custom zita knob (reserved #6)
  44. CUSTOM_PAINT_MODE_NO_GRADIENT = 8 # skip label gradient
  45. # enum Orientation
  46. HORIZONTAL = 0
  47. VERTICAL = 1
  48. HOVER_MIN = 0
  49. HOVER_MAX = 9
  50. # signals
  51. realValueChanged = pyqtSignal(float)
  52. def __init__(self, parent, index=0):
  53. QDial.__init__(self, parent)
  54. self.fMinimum = 0.0
  55. self.fMaximum = 1.0
  56. self.fRealValue = 0.0
  57. self.fIsHovered = False
  58. self.fHoverStep = self.HOVER_MIN
  59. self.fIndex = index
  60. self.fPixmap = QPixmap(":/bitmaps/dial_01d.png")
  61. self.fPixmapNum = "01"
  62. if self.fPixmap.width() > self.fPixmap.height():
  63. self.fPixmapOrientation = self.HORIZONTAL
  64. else:
  65. self.fPixmapOrientation = self.VERTICAL
  66. self.fLabel = ""
  67. self.fLabelPos = QPointF(0.0, 0.0)
  68. self.fLabelFont = QFont(self.font())
  69. self.fLabelFont.setPointSize(6)
  70. self.fLabelWidth = 0
  71. self.fLabelHeight = 0
  72. if self.palette().window().color().lightness() > 100:
  73. # Light background
  74. c = self.palette().dark().color()
  75. self.fLabelGradientColor1 = c
  76. self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0)
  77. self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()]
  78. else:
  79. # Dark background
  80. self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
  81. self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
  82. self.fLabelGradientColorT = [Qt.white, Qt.darkGray]
  83. self.fLabelGradient = QLinearGradient(0, 0, 0, 1)
  84. self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1)
  85. self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1)
  86. self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2)
  87. self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0)
  88. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL
  89. self.fCustomPaintColor = QColor(0xff, 0xff, 0xff)
  90. self.updateSizes()
  91. # Fake internal value, 10'000 precision
  92. QDial.setMinimum(self, 0)
  93. QDial.setMaximum(self, 10000)
  94. QDial.setValue(self, 0)
  95. self.valueChanged.connect(self.slot_valueChanged)
  96. def getIndex(self):
  97. return self.fIndex
  98. def getBaseSize(self):
  99. return self.fPixmapBaseSize
  100. def forceWhiteLabelGradientText(self):
  101. self.fLabelGradientColor1 = QColor(0, 0, 0, 255)
  102. self.fLabelGradientColor2 = QColor(0, 0, 0, 0)
  103. self.fLabelGradientColorT = [Qt.white, Qt.darkGray]
  104. #def setMidWhiteText(self):
  105. #self.fColor1 = QColor(0, 0, 0, 255)
  106. #self.fColor2 = QColor(0, 0, 0, 0)
  107. #self.fColorT = [QColor("#BBB"), Qt.darkGray]
  108. def updateSizes(self):
  109. self.fPixmapWidth = self.fPixmap.width()
  110. self.fPixmapHeight = self.fPixmap.height()
  111. if self.fPixmapWidth < 1:
  112. self.fPixmapWidth = 1
  113. if self.fPixmapHeight < 1:
  114. self.fPixmapHeight = 1
  115. if self.fPixmapOrientation == self.HORIZONTAL:
  116. self.fPixmapBaseSize = self.fPixmapHeight
  117. self.fPixmapLayersCount = self.fPixmapWidth / self.fPixmapHeight
  118. else:
  119. self.fPixmapBaseSize = self.fPixmapWidth
  120. self.fPixmapLayersCount = self.fPixmapHeight / self.fPixmapWidth
  121. self.setMinimumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5)
  122. self.setMaximumSize(self.fPixmapBaseSize, self.fPixmapBaseSize + self.fLabelHeight + 5)
  123. if not self.fLabel:
  124. self.fLabelHeight = 0
  125. self.fLabelWidth = 0
  126. return
  127. self.fLabelWidth = QFontMetrics(self.fLabelFont).width(self.fLabel)
  128. self.fLabelHeight = QFontMetrics(self.fLabelFont).height()
  129. self.fLabelPos.setX(float(self.fPixmapBaseSize)/2.0 - float(self.fLabelWidth)/2.0)
  130. if self.fPixmapNum in ("01", "02"):
  131. self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight)
  132. else:
  133. self.fLabelPos.setY(self.fPixmapBaseSize + self.fLabelHeight/2)
  134. self.fLabelGradient.setStart(0, float(self.fPixmapBaseSize)/2.0)
  135. self.fLabelGradient.setFinalStop(0, self.fPixmapBaseSize + self.fLabelHeight + 5)
  136. self.fLabelGradientRect = QRectF(float(self.fPixmapBaseSize)/8.0, float(self.fPixmapBaseSize)/2.0, float(self.fPixmapBaseSize*3)/4.0, self.fPixmapBaseSize+self.fLabelHeight+5)
  137. def setCustomPaintMode(self, paintMode):
  138. if self.fCustomPaintMode == paintMode:
  139. return
  140. self.fCustomPaintMode = paintMode
  141. self.update()
  142. def setCustomPaintColor(self, color):
  143. if self.fCustomPaintColor == color:
  144. return
  145. self.fCustomPaintColor = color
  146. self.update()
  147. def setLabel(self, label):
  148. if self.fLabel == label:
  149. return
  150. self.fLabel = label
  151. self.updateSizes()
  152. self.update()
  153. def setEnabled(self, enabled):
  154. if self.isEnabled() == enabled:
  155. return
  156. QDial.setEnabled(self, enabled)
  157. self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if enabled else "d"))
  158. self.updateSizes()
  159. self.update()
  160. def setIndex(self, index):
  161. self.fIndex = index
  162. def setPixmap(self, pixmapId):
  163. self.fPixmapNum = "%02i" % pixmapId
  164. self.fPixmap.load(":/bitmaps/dial_%s%s.png" % (self.fPixmapNum, "" if self.isEnabled() else "d"))
  165. if self.fPixmap.width() > self.fPixmap.height():
  166. self.fPixmapOrientation = self.HORIZONTAL
  167. else:
  168. self.fPixmapOrientation = self.VERTICAL
  169. # special pixmaps
  170. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
  171. # reserved for carla-wet, carla-vol, carla-pan and color
  172. if self.fPixmapNum == "03":
  173. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_COLOR
  174. # reserved for carla-L and carla-R
  175. elif self.fPixmapNum == "04":
  176. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_CARLA_L
  177. # reserved for zita
  178. elif self.fPixmapNum == "06":
  179. self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_ZITA
  180. self.updateSizes()
  181. self.update()
  182. def setMinimum(self, value):
  183. self.fMinimum = value
  184. def setMaximum(self, value):
  185. self.fMaximum = value
  186. def setValue(self, value):
  187. if self.fRealValue == value:
  188. return
  189. self.fRealValue = value
  190. normValue = float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  191. QDial.setValue(self, int(normValue * 10000))
  192. @pyqtSlot(int)
  193. def slot_valueChanged(self, value):
  194. self.fRealValue = float(value)/10000.0 * (self.fMaximum - self.fMinimum) + self.fMinimum
  195. self.realValueChanged.emit(self.fRealValue)
  196. def minimumSizeHint(self):
  197. return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize)
  198. def sizeHint(self):
  199. return QSize(self.fPixmapBaseSize, self.fPixmapBaseSize)
  200. def enterEvent(self, event):
  201. self.fIsHovered = True
  202. if self.fHoverStep == self.HOVER_MIN:
  203. self.fHoverStep = self.HOVER_MIN + 1
  204. QDial.enterEvent(self, event)
  205. def leaveEvent(self, event):
  206. self.fIsHovered = False
  207. if self.fHoverStep == self.HOVER_MAX:
  208. self.fHoverStep = self.HOVER_MAX - 1
  209. QDial.leaveEvent(self, event)
  210. def paintEvent(self, event):
  211. painter = QPainter(self)
  212. event.accept()
  213. painter.save()
  214. painter.setRenderHint(QPainter.Antialiasing, True)
  215. if self.fLabel:
  216. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_NULL:
  217. painter.setPen(self.fLabelGradientColor2)
  218. painter.setBrush(self.fLabelGradient)
  219. painter.drawRect(self.fLabelGradientRect)
  220. painter.setFont(self.fLabelFont)
  221. painter.setPen(self.fLabelGradientColorT[0 if self.isEnabled() else 1])
  222. painter.drawText(self.fLabelPos, self.fLabel)
  223. if self.isEnabled():
  224. normValue = float(self.fRealValue - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  225. target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
  226. curLayer = int((self.fPixmapLayersCount - 1) * normValue)
  227. if self.fPixmapOrientation == self.HORIZONTAL:
  228. xpos = self.fPixmapBaseSize * curLayer
  229. ypos = 0.0
  230. else:
  231. xpos = 0.0
  232. ypos = self.fPixmapBaseSize * curLayer
  233. source = QRectF(xpos, ypos, self.fPixmapBaseSize, self.fPixmapBaseSize)
  234. painter.drawPixmap(target, self.fPixmap, source)
  235. # Custom knobs (Dry/Wet and Volume)
  236. if self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_WET, self.CUSTOM_PAINT_MODE_CARLA_VOL):
  237. # knob color
  238. colorGreen = QColor(0x5D, 0xE7, 0x3D).lighter(100 + self.fHoverStep*6)
  239. colorBlue = QColor(0x3E, 0xB8, 0xBE).lighter(100 + self.fHoverStep*6)
  240. # draw small circle
  241. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  242. ballPath = QPainterPath()
  243. ballPath.addEllipse(ballRect)
  244. #painter.drawRect(ballRect)
  245. tmpValue = (0.375 + 0.75*normValue)
  246. ballValue = tmpValue - floor(tmpValue)
  247. ballPoint = ballPath.pointAtPercent(ballValue)
  248. # draw arc
  249. startAngle = 216*16
  250. spanAngle = -252*16*normValue
  251. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_WET:
  252. painter.setBrush(colorBlue)
  253. painter.setPen(QPen(colorBlue, 0))
  254. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  255. gradient = QConicalGradient(15.5, 15.5, -45)
  256. gradient.setColorAt(0.0, colorBlue)
  257. gradient.setColorAt(0.125, colorBlue)
  258. gradient.setColorAt(0.625, colorGreen)
  259. gradient.setColorAt(0.75, colorGreen)
  260. gradient.setColorAt(0.76, colorGreen)
  261. gradient.setColorAt(1.0, colorGreen)
  262. painter.setBrush(gradient)
  263. painter.setPen(QPen(gradient, 3))
  264. else:
  265. painter.setBrush(colorBlue)
  266. painter.setPen(QPen(colorBlue, 0))
  267. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  268. painter.setBrush(colorBlue)
  269. painter.setPen(QPen(colorBlue, 3))
  270. painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)
  271. # Custom knobs (L and R)
  272. elif self.fCustomPaintMode in (self.CUSTOM_PAINT_MODE_CARLA_L, self.CUSTOM_PAINT_MODE_CARLA_R):
  273. # knob color
  274. color = QColor(0xAD, 0xD5, 0x48).lighter(100 + self.fHoverStep*6)
  275. # draw small circle
  276. ballRect = QRectF(7.0, 8.0, 11.0, 12.0)
  277. ballPath = QPainterPath()
  278. ballPath.addEllipse(ballRect)
  279. #painter.drawRect(ballRect)
  280. tmpValue = (0.375 + 0.75*normValue)
  281. ballValue = tmpValue - floor(tmpValue)
  282. ballPoint = ballPath.pointAtPercent(ballValue)
  283. painter.setBrush(color)
  284. painter.setPen(QPen(color, 0))
  285. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.0, 2.0))
  286. # draw arc
  287. if self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_CARLA_L:
  288. startAngle = 216*16
  289. spanAngle = -252.0*16*normValue
  290. else:
  291. startAngle = 324.0*16
  292. spanAngle = 252.0*16*(1.0-normValue)
  293. painter.setPen(QPen(color, 2))
  294. painter.drawArc(3.5, 4.5, 22.0, 22.0, startAngle, spanAngle)
  295. # Custom knobs (Color)
  296. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_COLOR:
  297. # knob color
  298. color = self.fCustomPaintColor.lighter(100 + self.fHoverStep*6)
  299. # draw small circle
  300. ballRect = QRectF(8.0, 8.0, 15.0, 15.0)
  301. ballPath = QPainterPath()
  302. ballPath.addEllipse(ballRect)
  303. tmpValue = (0.375 + 0.75*normValue)
  304. ballValue = tmpValue - floor(tmpValue)
  305. ballPoint = ballPath.pointAtPercent(ballValue)
  306. # draw arc
  307. startAngle = 216*16
  308. spanAngle = -252*16*normValue
  309. painter.setBrush(color)
  310. painter.setPen(QPen(color, 0))
  311. painter.drawEllipse(QRectF(ballPoint.x(), ballPoint.y(), 2.2, 2.2))
  312. painter.setBrush(color)
  313. painter.setPen(QPen(color, 3))
  314. painter.drawArc(4.0, 4.0, 26.0, 26.0, startAngle, spanAngle)
  315. # Custom knobs (Zita)
  316. elif self.fCustomPaintMode == self.CUSTOM_PAINT_MODE_ZITA:
  317. a = normValue * pi * 1.5 - 2.35
  318. r = 10.0
  319. x = 10.5
  320. y = 10.5
  321. x += r * sin(a)
  322. y -= r * cos(a)
  323. painter.setBrush(Qt.black)
  324. painter.setPen(QPen(Qt.black, 2))
  325. painter.drawLine(QPointF(11.0, 11.0), QPointF(x, y))
  326. # Custom knobs
  327. else:
  328. painter.restore()
  329. return
  330. if self.HOVER_MIN < self.fHoverStep < self.HOVER_MAX:
  331. self.fHoverStep += 1 if self.fIsHovered else -1
  332. QTimer.singleShot(20, self.update)
  333. else: # isEnabled()
  334. target = QRectF(0.0, 0.0, self.fPixmapBaseSize, self.fPixmapBaseSize)
  335. painter.drawPixmap(target, self.fPixmap, target)
  336. painter.restore()
  337. def resizeEvent(self, event):
  338. QDial.resizeEvent(self, event)
  339. self.updateSizes()
  340. # ------------------------------------------------------------------------------------------------------------
  341. # Main Testing
  342. if __name__ == '__main__':
  343. import sys
  344. from PyQt4.QtGui import QApplication
  345. import resources_rc
  346. app = QApplication(sys.argv)
  347. gui = PixmapDial(None)
  348. #gui.setEnabled(True)
  349. #gui.setEnabled(False)
  350. gui.setPixmap(3)
  351. gui.setLabel("hahaha")
  352. gui.show()
  353. sys.exit(app.exec_())