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.

paramspinbox.py 13KB

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
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
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
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Parameter SpinBox, 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 GPL.txt file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. from PyQt4.QtCore import pyqtSlot, Qt, QTimer, SIGNAL, SLOT
  20. from PyQt4.QtGui import QAbstractSpinBox, QComboBox, QCursor, QDialog, QInputDialog, QMenu, QPainter, QProgressBar, QValidator
  21. #from PyQt4.QtGui import QStyleFactory
  22. from math import isnan
  23. # ------------------------------------------------------------------------------------------------------------
  24. # Imports (Custom)
  25. import ui_inputdialog_value
  26. def fixValue(value, minimum, maximum):
  27. if isnan(value):
  28. print("Parameter is NaN! - %f" % value)
  29. return minimum
  30. if value < minimum:
  31. print("Parameter too low! - %f/%f" % (value, minimum))
  32. return minimum
  33. if value > maximum:
  34. print("Parameter too high! - %f/%f" % (value, maximum))
  35. return maximum
  36. return value
  37. #QPlastiqueStyle = QStyleFactory.create("Plastique")
  38. # ------------------------------------------------------------------------------------------------------------
  39. # Custom InputDialog with Scale Points support
  40. class CustomInputDialog(QDialog):
  41. def __init__(self, parent, label, current, minimum, maximum, step, scalePoints):
  42. QDialog.__init__(self, parent)
  43. self.ui = ui_inputdialog_value.Ui_Dialog()
  44. self.ui.setupUi(self)
  45. self.ui.label.setText(label)
  46. self.ui.doubleSpinBox.setMinimum(minimum)
  47. self.ui.doubleSpinBox.setMaximum(maximum)
  48. self.ui.doubleSpinBox.setValue(current)
  49. self.ui.doubleSpinBox.setSingleStep(step)
  50. if not scalePoints:
  51. self.ui.groupBox.setVisible(False)
  52. self.resize(200, 0)
  53. else:
  54. text = "<table>"
  55. for scalePoint in scalePoints:
  56. text += "<tr><td align='right'>%f</td><td align='left'> - %s</td></tr>" % (scalePoint['value'], scalePoint['label'])
  57. text += "</table>"
  58. self.ui.textBrowser.setText(text)
  59. self.resize(200, 300)
  60. self.fRetValue = current
  61. self.connect(self, SIGNAL("accepted()"), self.setReturnValue)
  62. def returnValue(self):
  63. return self.fRetValue
  64. def setReturnValue(self):
  65. self.fRetValue = self.ui.doubleSpinBox.value()
  66. def done(self, r):
  67. QDialog.done(self, r)
  68. self.close()
  69. # ------------------------------------------------------------------------------------------------------------
  70. # ProgressBar used for ParamSpinBox
  71. class ParamProgressBar(QProgressBar):
  72. def __init__(self, parent):
  73. QProgressBar.__init__(self, parent)
  74. self.fLeftClickDown = False
  75. self.fMinimum = 0.0
  76. self.fMaximum = 1.0
  77. self.fRealValue = 0.0
  78. self.fLabel = ""
  79. self.fPreLabel = " "
  80. self.fTextCall = None
  81. self.setFormat("(none)")
  82. # Fake internal value, 10'000 precision
  83. QProgressBar.setMinimum(self, 0)
  84. QProgressBar.setMaximum(self, 10000)
  85. QProgressBar.setValue(self, 0)
  86. def setMinimum(self, value):
  87. self.fMinimum = value
  88. def setMaximum(self, value):
  89. self.fMaximum = value
  90. def setValue(self, value):
  91. self.fRealValue = value
  92. vper = float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  93. QProgressBar.setValue(self, int(vper * 10000))
  94. def setLabel(self, label):
  95. self.fLabel = label.strip()
  96. if self.fLabel == "(coef)":
  97. self.fLabel = ""
  98. self.fPreLabel = "*"
  99. self.update()
  100. def setTextCall(self, textCall):
  101. self.fTextCall = textCall
  102. def handleMouseEventPos(self, pos):
  103. xper = float(pos.x()) / float(self.width())
  104. value = xper * (self.fMaximum - self.fMinimum) + self.fMinimum
  105. if value < self.fMinimum:
  106. value = self.fMinimum
  107. elif value > self.fMaximum:
  108. value = self.fMaximum
  109. self.emit(SIGNAL("valueChanged(double)"), value)
  110. def mousePressEvent(self, event):
  111. if event.button() == Qt.LeftButton:
  112. self.handleMouseEventPos(event.pos())
  113. self.fLeftClickDown = True
  114. else:
  115. self.fLeftClickDown = False
  116. QProgressBar.mousePressEvent(self, event)
  117. def mouseMoveEvent(self, event):
  118. if self.fLeftClickDown:
  119. self.handleMouseEventPos(event.pos())
  120. QProgressBar.mouseMoveEvent(self, event)
  121. def mouseReleaseEvent(self, event):
  122. self.fLeftClickDown = False
  123. QProgressBar.mouseReleaseEvent(self, event)
  124. def paintEvent(self, event):
  125. if self.fTextCall is not None:
  126. self.setFormat("%s %s %s" % (self.fPreLabel, self.fTextCall(), self.fLabel))
  127. else:
  128. self.setFormat("%s %f %s" % (self.fPreLabel, self.fRealValue, self.fLabel))
  129. QProgressBar.paintEvent(self, event)
  130. # ------------------------------------------------------------------------------------------------------------
  131. # Special SpinBox used for parameters
  132. class ParamSpinBox(QAbstractSpinBox):
  133. def __init__(self, parent):
  134. QAbstractSpinBox.__init__(self, parent)
  135. self.fMinimum = 0.0
  136. self.fMaximum = 1.0
  137. self.fDefault = 0.0
  138. self.fValue = None
  139. self.fStep = 0.0
  140. self.fStepSmall = 0.0
  141. self.fStepLarge = 0.0
  142. self.fReadOnly = False
  143. self.fScalePoints = None
  144. self.fHaveScalePoints = False
  145. self.fBar = ParamProgressBar(self)
  146. self.fBar.setContextMenuPolicy(Qt.NoContextMenu)
  147. self.fBar.show()
  148. self.lineEdit().setVisible(False)
  149. self.connect(self, SIGNAL("customContextMenuRequested(QPoint)"), SLOT("slot_showCustomMenu()"))
  150. self.connect(self.fBar, SIGNAL("valueChanged(double)"), SLOT("slot_progressBarValueChanged(double)"))
  151. QTimer.singleShot(0, self, SLOT("slot_updateProgressBarGeometry()"))
  152. #def force_plastique_style(self):
  153. #self.setStyle(QPlastiqueStyle)
  154. def setDefault(self, value):
  155. value = fixValue(value, self.fMinimum, self.fMaximum)
  156. self.fDefault = value
  157. def setMinimum(self, value):
  158. self.fMinimum = value
  159. self.fBar.setMinimum(value)
  160. def setMaximum(self, value):
  161. self.fMaximum = value
  162. self.fBar.setMaximum(value)
  163. def setValue(self, value, send=True):
  164. value = fixValue(value, self.fMinimum, self.fMaximum)
  165. if self.fValue == value:
  166. return False
  167. self.fValue = value
  168. self.fBar.setValue(value)
  169. if self.fHaveScalePoints:
  170. self._setScalePointValue(value)
  171. if send:
  172. self.emit(SIGNAL("valueChanged(double)"), value)
  173. self.update()
  174. return True
  175. def setStep(self, value):
  176. if value == 0.0:
  177. self.fStep = 0.001
  178. else:
  179. self.fStep = value
  180. if self.fStepSmall > value:
  181. self.fStepSmall = value
  182. if self.fStepLarge < value:
  183. self.fStepLarge = value
  184. def setStepSmall(self, value):
  185. if value == 0.0:
  186. self.fStepSmall = 0.0001
  187. elif value > self.fStep:
  188. self.fStepSmall = self.fStep
  189. else:
  190. self.fStepSmall = value
  191. def setStepLarge(self, value):
  192. if value == 0.0:
  193. self.fStepLarge = 0.1
  194. elif value < self.fStep:
  195. self.fStepLarge = self.fStep
  196. else:
  197. self.fStepLarge = value
  198. def setLabel(self, label):
  199. self.fBar.setLabel(label)
  200. def setTextCallback(self, textCall):
  201. self.fBar.setTextCall(textCall)
  202. def setReadOnly(self, yesNo):
  203. self.setButtonSymbols(QAbstractSpinBox.UpDownArrows if yesNo else QAbstractSpinBox.NoButtons)
  204. self.fReadOnly = yesNo
  205. ParamSpinBox.setReadOnly(self, yesNo)
  206. def setScalePoints(self, scalePoints, useScalePoints):
  207. if len(scalePoints) == 0:
  208. self.fScalePoints = None
  209. self.fHaveScalePoints = False
  210. return
  211. self.fScalePoints = scalePoints
  212. self.fHaveScalePoints = useScalePoints
  213. if useScalePoints:
  214. # Hide ProgressBar and create a ComboBox
  215. self.fBar.close()
  216. self.fBox = QComboBox(self)
  217. self.fBox.setContextMenuPolicy(Qt.NoContextMenu)
  218. self.fBox.show()
  219. self.slot_updateProgressBarGeometry()
  220. for scalePoint in scalePoints:
  221. self.fBox.addItem("%f - %s" % (scalePoint['value'], scalePoint['label']))
  222. if self.fValue != None:
  223. self._setScalePointValue(self.fValue)
  224. self.connect(self.fBox, SIGNAL("currentIndexChanged(QString)"), SLOT("slot_comboBoxIndexChanged(QString)"))
  225. def stepBy(self, steps):
  226. if steps == 0 or self.fValue is None:
  227. return
  228. value = self.fValue + (self.fStep * steps)
  229. if value < self.fMinimum:
  230. value = self.fMinimum
  231. elif value > self.fMaximum:
  232. value = self.fMaximum
  233. self.setValue(value)
  234. def stepEnabled(self):
  235. if self.fReadOnly or self.fValue is None:
  236. return QAbstractSpinBox.StepNone
  237. if self.fValue <= self.fMinimum:
  238. return QAbstractSpinBox.StepUpEnabled
  239. if self.fValue >= self.fMaximum:
  240. return QAbstractSpinBox.StepDownEnabled
  241. return (QAbstractSpinBox.StepUpEnabled | QAbstractSpinBox.StepDownEnabled)
  242. def updateAll(self):
  243. self.update()
  244. self.fBar.update()
  245. if self.fHaveScalePoints:
  246. self.fBox.update()
  247. def resizeEvent(self, event):
  248. QTimer.singleShot(0, self, SLOT("slot_updateProgressBarGeometry()"))
  249. QAbstractSpinBox.resizeEvent(self, event)
  250. @pyqtSlot(str)
  251. def slot_comboBoxIndexChanged(self, boxText):
  252. if self.fReadOnly:
  253. return
  254. value = float(boxText.split(" - ", 1)[0])
  255. lastScaleValue = self.fScalePoints[-1]["value"]
  256. if value == lastScaleValue:
  257. value = self.fMaximum
  258. self.setValue(value)
  259. @pyqtSlot(float)
  260. def slot_progressBarValueChanged(self, value):
  261. if self.fReadOnly:
  262. return
  263. step = int((value - self.fMinimum) / self.fStep + 0.5)
  264. realValue = self.fMinimum + (step * self.fStep)
  265. self.setValue(realValue)
  266. @pyqtSlot()
  267. def slot_showCustomMenu(self):
  268. menu = QMenu(self)
  269. actReset = menu.addAction(self.tr("Reset (%f)" % self.fDefault))
  270. menu.addSeparator()
  271. actCopy = menu.addAction(self.tr("Copy (%f)" % self.fValue))
  272. if True or self.fReadOnly:
  273. actPaste = menu.addAction(self.tr("Paste"))
  274. else:
  275. actPaste = menu.addAction(self.tr("Paste (%s)" % "TODO"))
  276. menu.addSeparator()
  277. actSet = menu.addAction(self.tr("Set value..."))
  278. if self.fReadOnly:
  279. actReset.setEnabled(False)
  280. actPaste.setEnabled(False)
  281. actSet.setEnabled(False)
  282. # TODO - NOT IMPLEMENTED YET
  283. actCopy.setEnabled(False)
  284. actSel = menu.exec_(QCursor.pos())
  285. if actSel == actSet:
  286. dialog = CustomInputDialog(self, self.parent().label.text(), self.fValue, self.fMinimum, self.fMaximum, self.fStep, self.fScalePoints)
  287. if dialog.exec_():
  288. value = dialog.returnValue()
  289. self.setValue(value)
  290. elif actSel == actCopy:
  291. pass
  292. elif actSel == actPaste:
  293. pass
  294. elif actSel == actReset:
  295. self.setValue(self.fDefault)
  296. @pyqtSlot()
  297. def slot_updateProgressBarGeometry(self):
  298. self.fBar.setGeometry(self.lineEdit().geometry())
  299. if self.fHaveScalePoints:
  300. self.fBox.setGeometry(self.lineEdit().geometry())
  301. def _getNearestScalePoint(self, realValue):
  302. finalValue = 0.0
  303. for i in range(len(self.fScalePoints)):
  304. scaleValue = self.fScalePoints[i]["value"]
  305. if i == 0:
  306. finalValue = scaleValue
  307. else:
  308. srange1 = abs(realValue - scaleValue)
  309. srange2 = abs(realValue - finalValue)
  310. if srange2 > srange1:
  311. finalValue = scaleValue
  312. return finalValue
  313. def _setScalePointValue(self, value):
  314. value = self._getNearestScalePoint(value)
  315. for i in range(self.fBox.count()):
  316. if float(self.fBox.itemText(i).split(" - ", 1)[0] == value):
  317. self.fBox.setCurrentIndex(i)
  318. break