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