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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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, QApplication, QComboBox, QCursor, QDialog, QMenu, QProgressBar
  21. from math import isnan
  22. # ------------------------------------------------------------------------------------------------------------
  23. # Imports (Custom)
  24. import ui_inputdialog_value
  25. def fixValue(value, minimum, maximum):
  26. if isnan(value):
  27. print("Parameter is NaN! - %f" % value)
  28. return minimum
  29. if value < minimum:
  30. print("Parameter too low! - %f/%f" % (value, minimum))
  31. return minimum
  32. if value > maximum:
  33. print("Parameter too high! - %f/%f" % (value, maximum))
  34. return maximum
  35. return value
  36. # ------------------------------------------------------------------------------------------------------------
  37. # Custom InputDialog with Scale Points support
  38. class CustomInputDialog(QDialog):
  39. def __init__(self, parent, label, current, minimum, maximum, step, scalePoints):
  40. QDialog.__init__(self, parent)
  41. self.ui = ui_inputdialog_value.Ui_Dialog()
  42. self.ui.setupUi(self)
  43. self.ui.label.setText(label)
  44. self.ui.doubleSpinBox.setMinimum(minimum)
  45. self.ui.doubleSpinBox.setMaximum(maximum)
  46. self.ui.doubleSpinBox.setValue(current)
  47. self.ui.doubleSpinBox.setSingleStep(step)
  48. if not scalePoints:
  49. self.ui.groupBox.setVisible(False)
  50. self.resize(200, 0)
  51. else:
  52. text = "<table>"
  53. for scalePoint in scalePoints:
  54. text += "<tr><td align='right'>%f</td><td align='left'> - %s</td></tr>" % (scalePoint['value'], scalePoint['label'])
  55. text += "</table>"
  56. self.ui.textBrowser.setText(text)
  57. self.resize(200, 300)
  58. self.fRetValue = current
  59. self.connect(self, SIGNAL("accepted()"), SLOT("slot_setReturnValue()"))
  60. def returnValue(self):
  61. return self.fRetValue
  62. @pyqtSlot()
  63. def slot_setReturnValue(self):
  64. self.fRetValue = self.ui.doubleSpinBox.value()
  65. def done(self, r):
  66. QDialog.done(self, r)
  67. self.close()
  68. # ------------------------------------------------------------------------------------------------------------
  69. # ProgressBar used for ParamSpinBox
  70. class ParamProgressBar(QProgressBar):
  71. def __init__(self, parent):
  72. QProgressBar.__init__(self, parent)
  73. self.fLeftClickDown = False
  74. self.fMinimum = 0.0
  75. self.fMaximum = 1.0
  76. self.fRealValue = 0.0
  77. self.fLabel = ""
  78. self.fPreLabel = " "
  79. self.fTextCall = None
  80. self.setFormat("(none)")
  81. # Fake internal value, 10'000 precision
  82. QProgressBar.setMinimum(self, 0)
  83. QProgressBar.setMaximum(self, 10000)
  84. QProgressBar.setValue(self, 0)
  85. def setMinimum(self, value):
  86. self.fMinimum = value
  87. def setMaximum(self, value):
  88. self.fMaximum = value
  89. def setValue(self, value):
  90. self.fRealValue = value
  91. vper = float(value - self.fMinimum) / float(self.fMaximum - self.fMinimum)
  92. QProgressBar.setValue(self, int(vper * 10000))
  93. def setLabel(self, label):
  94. self.fLabel = label.strip()
  95. if self.fLabel == "(coef)":
  96. self.fLabel = ""
  97. self.fPreLabel = "*"
  98. self.update()
  99. def setTextCall(self, textCall):
  100. self.fTextCall = textCall
  101. def handleMouseEventPos(self, pos):
  102. xper = float(pos.x()) / float(self.width())
  103. value = xper * (self.fMaximum - self.fMinimum) + self.fMinimum
  104. if value < self.fMinimum:
  105. value = self.fMinimum
  106. elif value > self.fMaximum:
  107. value = self.fMaximum
  108. self.emit(SIGNAL("valueChanged(double)"), value)
  109. def mousePressEvent(self, event):
  110. if event.button() == Qt.LeftButton:
  111. self.handleMouseEventPos(event.pos())
  112. self.fLeftClickDown = True
  113. else:
  114. self.fLeftClickDown = False
  115. QProgressBar.mousePressEvent(self, event)
  116. def mouseMoveEvent(self, event):
  117. if self.fLeftClickDown:
  118. self.handleMouseEventPos(event.pos())
  119. QProgressBar.mouseMoveEvent(self, event)
  120. def mouseReleaseEvent(self, event):
  121. self.fLeftClickDown = False
  122. QProgressBar.mouseReleaseEvent(self, event)
  123. def paintEvent(self, event):
  124. if self.fTextCall is not None:
  125. self.setFormat("%s %s %s" % (self.fPreLabel, self.fTextCall(), self.fLabel))
  126. else:
  127. self.setFormat("%s %f %s" % (self.fPreLabel, self.fRealValue, self.fLabel))
  128. QProgressBar.paintEvent(self, event)
  129. # ------------------------------------------------------------------------------------------------------------
  130. # Special SpinBox used for parameters
  131. class ParamSpinBox(QAbstractSpinBox):
  132. def __init__(self, parent):
  133. QAbstractSpinBox.__init__(self, parent)
  134. self.fMinimum = 0.0
  135. self.fMaximum = 1.0
  136. self.fDefault = 0.0
  137. self.fValue = None
  138. self.fStep = 0.0
  139. self.fStepSmall = 0.0
  140. self.fStepLarge = 0.0
  141. self.fReadOnly = False
  142. self.fScalePoints = None
  143. self.fHaveScalePoints = False
  144. self.fBar = ParamProgressBar(self)
  145. self.fBar.setContextMenuPolicy(Qt.NoContextMenu)
  146. self.fBar.show()
  147. self.fName = ""
  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 setDefault(self, value):
  153. value = fixValue(value, self.fMinimum, self.fMaximum)
  154. self.fDefault = value
  155. def setMinimum(self, value):
  156. self.fMinimum = value
  157. self.fBar.setMinimum(value)
  158. def setMaximum(self, value):
  159. self.fMaximum = value
  160. self.fBar.setMaximum(value)
  161. def setValue(self, value, send=True):
  162. value = fixValue(value, self.fMinimum, self.fMaximum)
  163. if self.fValue == value:
  164. return False
  165. self.fValue = value
  166. self.fBar.setValue(value)
  167. if self.fHaveScalePoints:
  168. self._setScalePointValue(value)
  169. if send:
  170. self.emit(SIGNAL("valueChanged(double)"), value)
  171. self.update()
  172. return True
  173. def setStep(self, value):
  174. if value == 0.0:
  175. self.fStep = 0.001
  176. else:
  177. self.fStep = value
  178. if self.fStepSmall > value:
  179. self.fStepSmall = value
  180. if self.fStepLarge < value:
  181. self.fStepLarge = value
  182. def setStepSmall(self, value):
  183. if value == 0.0:
  184. self.fStepSmall = 0.0001
  185. elif value > self.fStep:
  186. self.fStepSmall = self.fStep
  187. else:
  188. self.fStepSmall = value
  189. def setStepLarge(self, value):
  190. if value == 0.0:
  191. self.fStepLarge = 0.1
  192. elif value < self.fStep:
  193. self.fStepLarge = self.fStep
  194. else:
  195. self.fStepLarge = value
  196. def setLabel(self, label):
  197. self.fBar.setLabel(label)
  198. def setName(self, name):
  199. self.fName = name
  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. QAbstractSpinBox.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. clipboard = QApplication.instance().clipboard()
  273. pasteText = clipboard.text()
  274. pasteValue = None
  275. if pasteText:
  276. try:
  277. pasteValue = float(pasteText)
  278. except:
  279. pass
  280. if pasteValue is None:
  281. actPaste = menu.addAction(self.tr("Paste"))
  282. else:
  283. actPaste = menu.addAction(self.tr("Paste (%s)" % pasteValue))
  284. menu.addSeparator()
  285. actSet = menu.addAction(self.tr("Set value..."))
  286. if self.fReadOnly:
  287. actReset.setEnabled(False)
  288. actPaste.setEnabled(False)
  289. actSet.setEnabled(False)
  290. actSel = menu.exec_(QCursor.pos())
  291. if actSel == actSet:
  292. dialog = CustomInputDialog(self, self.fName, self.fValue, self.fMinimum, self.fMaximum, self.fStep, self.fScalePoints)
  293. if dialog.exec_():
  294. value = dialog.returnValue()
  295. self.setValue(value)
  296. elif actSel == actCopy:
  297. clipboard.setText("%f" % self.fValue)
  298. elif actSel == actPaste:
  299. self.setValue(pasteValue)
  300. elif actSel == actReset:
  301. self.setValue(self.fDefault)
  302. @pyqtSlot()
  303. def slot_updateProgressBarGeometry(self):
  304. self.fBar.setGeometry(self.lineEdit().geometry())
  305. if self.fHaveScalePoints:
  306. self.fBox.setGeometry(self.lineEdit().geometry())
  307. def _getNearestScalePoint(self, realValue):
  308. finalValue = 0.0
  309. for i in range(len(self.fScalePoints)):
  310. scaleValue = self.fScalePoints[i]["value"]
  311. if i == 0:
  312. finalValue = scaleValue
  313. else:
  314. srange1 = abs(realValue - scaleValue)
  315. srange2 = abs(realValue - finalValue)
  316. if srange2 > srange1:
  317. finalValue = scaleValue
  318. return finalValue
  319. def _setScalePointValue(self, value):
  320. value = self._getNearestScalePoint(value)
  321. for i in range(self.fBox.count()):
  322. if float(self.fBox.itemText(i).split(" - ", 1)[0] == value):
  323. self.fBox.setCurrentIndex(i)
  324. break