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.

710 lines
25KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla patchbay widget code
  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 doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. try:
  20. from PyQt5.QtWidgets import QGraphicsView
  21. except:
  22. from PyQt4.QtGui import QGraphicsView
  23. #QPrinter, QPrintDialog
  24. #QImage
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Imports (Custom Stuff)
  27. import patchcanvas
  28. from carla_widgets import *
  29. # ------------------------------------------------------------------------------------------------------------
  30. # Try Import OpenGL
  31. try:
  32. #try:
  33. from PyQt5.QtOpenGL import QGLWidget
  34. #except:
  35. #from PyQt4.QtOpenGL import QGLWidget
  36. hasGL = True
  37. except:
  38. hasGL = False
  39. # ------------------------------------------------------------------------------------------------
  40. # Patchbay widget
  41. class CarlaPatchbayW(QGraphicsView):
  42. def __init__(self, parent):
  43. QGraphicsView.__init__(self, parent)
  44. # -------------------------------------------------------------
  45. # Internal stuff
  46. self.fParent = parent
  47. self.fPluginCount = 0
  48. self.fPluginList = []
  49. # -------------------------------------------------------------
  50. # Set-up Canvas
  51. self.scene = patchcanvas.PatchScene(self, self) # FIXME?
  52. self.setScene(self.scene)
  53. #self.setRenderHint(QPainter.Antialiasing, bool(self.fSavedSettings["Canvas/Antialiasing"] == patchcanvas.ANTIALIASING_FULL))
  54. #if self.fSavedSettings["Canvas/UseOpenGL"] and hasGL:
  55. #self.setViewport(QGLWidget(self))
  56. #self.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings["Canvas/HighQualityAntialiasing"])
  57. #pOptions = patchcanvas.options_t()
  58. #pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  59. #pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  60. #pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  61. #pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  62. #pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  63. pFeatures = patchcanvas.features_t()
  64. pFeatures.group_info = False
  65. pFeatures.group_rename = False
  66. pFeatures.port_info = False
  67. pFeatures.port_rename = False
  68. pFeatures.handle_group_pos = True
  69. #patchcanvas.setOptions(pOptions)
  70. patchcanvas.setFeatures(pFeatures)
  71. patchcanvas.init("Carla", self.scene, CanvasCallback, True)
  72. #patchcanvas.setCanvasSize(0, 0, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT)
  73. #patchcanvas.setInitialPos(DEFAULT_CANVAS_WIDTH / 2, DEFAULT_CANVAS_HEIGHT / 2)
  74. #self.setSceneRect(0, 0, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT)
  75. # -------------------------------------------------------------
  76. # Connect actions to functions
  77. if parent is None:
  78. return
  79. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  80. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  81. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  82. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  83. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  84. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  85. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  86. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  87. parent.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  88. parent.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange)
  89. parent.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh)
  90. parent.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit)
  91. parent.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn)
  92. parent.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut)
  93. parent.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset)
  94. parent.ui.act_canvas_print.triggered.connect(self.slot_canvasPrint)
  95. parent.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage)
  96. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  97. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  98. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  99. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  100. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  101. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  102. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  103. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  104. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  105. parent.ShowGuiCallback.connect(self.slot_handleShowGuiCallback)
  106. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  107. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  108. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  109. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  110. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  111. parent.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback)
  112. parent.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback)
  113. parent.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback)
  114. parent.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback)
  115. parent.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback)
  116. parent.PatchbayPortRenamedCallback.connect(self.slot_handlePatchbayPortRenamedCallback)
  117. parent.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
  118. parent.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)
  119. parent.PatchbayIconChangedCallback.connect(self.slot_handlePatchbayIconChangedCallback)
  120. #self.ui.miniCanvasPreview-miniCanvasMoved(double, double)"), SLOT("slot_miniCanvasMoved(double, double)"))
  121. #self.ui.graphicsView.horizontalScrollBar()-valueChanged.connect(self.slot_horizontalScrollBarChanged)
  122. #self.ui.graphicsView.verticalScrollBar()-valueChanged.connect(self.slot_verticalScrollBarChanged)
  123. #self.scene-sceneGroupMoved(int, int, QPointF)"), SLOT("slot_canvasItemMoved(int, int, QPointF)"))
  124. #self.scene-scaleChanged(double)"), SLOT("slot_canvasScaleChanged(double)"))
  125. # -----------------------------------------------------------------
  126. def getPluginCount(self):
  127. return self.fPluginCount
  128. # -----------------------------------------------------------------
  129. def addPlugin(self, pluginId, isProjectLoading):
  130. pitem = PluginEdit(self, pluginId)
  131. self.fPluginList.append(pitem)
  132. self.fPluginCount += 1
  133. def removePlugin(self, pluginId):
  134. if pluginId >= self.fPluginCount:
  135. return
  136. pitem = self.fPluginList[pluginId]
  137. if pitem is None:
  138. return
  139. self.fPluginCount -= 1
  140. self.fPluginList.pop(pluginId)
  141. pitem.close()
  142. del pitem
  143. # push all plugins 1 slot back
  144. for i in range(pluginId, self.fPluginCount):
  145. self.fPluginList[i].fPluginId = i # FIXME ?
  146. def renamePlugin(self, pluginId, newName):
  147. if pluginId >= self.fPluginCount:
  148. return
  149. pitem = self.fPluginList[pluginId]
  150. if pitem is None:
  151. return
  152. pitem.setName(newName)
  153. def removeAllPlugins(self):
  154. for i in range(self.fPluginCount):
  155. pitem = self.fPluginList[i]
  156. if pitem is None:
  157. break
  158. pitem.close()
  159. del pitem
  160. self.fPluginCount = 0
  161. self.fPluginList = []
  162. # -----------------------------------------------------------------
  163. def engineStarted(self):
  164. pass
  165. def engineStopped(self):
  166. patchcanvas.clear()
  167. # -----------------------------------------------------------------
  168. def idleFast(self):
  169. pass
  170. def idleSlow(self):
  171. for i in range(self.fPluginCount):
  172. pitem = self.fPluginList[i]
  173. if pitem is None:
  174. break
  175. pitem.idleSlow()
  176. # -----------------------------------------------------------------
  177. def recheckPluginHints(self, hints):
  178. pass
  179. # -----------------------------------------------------------------
  180. @pyqtSlot()
  181. def slot_pluginsEnable(self):
  182. if not Carla.host.is_engine_running():
  183. return
  184. for i in range(self.fPluginCount):
  185. Carla.host.set_active(i, True)
  186. @pyqtSlot()
  187. def slot_pluginsDisable(self):
  188. if not Carla.host.is_engine_running():
  189. return
  190. for i in range(self.fPluginCount):
  191. Carla.host.set_active(i, False)
  192. @pyqtSlot()
  193. def slot_pluginsVolume100(self):
  194. if not Carla.host.is_engine_running():
  195. return
  196. for i in range(self.fPluginCount):
  197. pitem = self.fPluginList[i]
  198. if pitem is None:
  199. break
  200. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  201. pitem.setParameterValue(PARAMETER_VOLUME, 1.0)
  202. Carla.host.set_volume(i, 1.0)
  203. @pyqtSlot()
  204. def slot_pluginsMute(self):
  205. if not Carla.host.is_engine_running():
  206. return
  207. for i in range(self.fPluginCount):
  208. pitem = self.fPluginList[i]
  209. if pitem is None:
  210. break
  211. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  212. pitem.setParameterValue(PARAMETER_VOLUME, 0.0)
  213. Carla.host.set_volume(i, 0.0)
  214. @pyqtSlot()
  215. def slot_pluginsWet100(self):
  216. if not Carla.host.is_engine_running():
  217. return
  218. for i in range(self.fPluginCount):
  219. pitem = self.fPluginList[i]
  220. if pitem is None:
  221. break
  222. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  223. pitem.setParameterValue(PARAMETER_DRYWET, 1.0)
  224. Carla.host.set_drywet(i, 1.0)
  225. @pyqtSlot()
  226. def slot_pluginsBypass(self):
  227. if not Carla.host.is_engine_running():
  228. return
  229. for i in range(self.fPluginCount):
  230. pitem = self.fPluginList[i]
  231. if pitem is None:
  232. break
  233. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  234. pitem.setParameterValue(PARAMETER_DRYWET, 0.0)
  235. Carla.host.set_drywet(i, 0.0)
  236. @pyqtSlot()
  237. def slot_pluginsCenter(self):
  238. if not Carla.host.is_engine_running():
  239. return
  240. for i in range(self.fPluginCount):
  241. pitem = self.fPluginList[i]
  242. if pitem is None:
  243. break
  244. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  245. pitem.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  246. pitem.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  247. Carla.host.set_balance_left(i, -1.0)
  248. Carla.host.set_balance_right(i, 1.0)
  249. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_PANNING:
  250. pitem.setParameterValue(PARAMETER_PANNING, 1.0)
  251. Carla.host.set_panning(i, 1.0)
  252. # -----------------------------------------------------------------
  253. @pyqtSlot()
  254. def slot_configureCarla(self):
  255. if self.fParent is None or not self.fParent.openSettings(False, False):
  256. return
  257. #self.loadSettings(False)
  258. patchcanvas.clear()
  259. #pOptions = patchcanvas.options_t()
  260. #pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  261. #pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  262. #pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  263. #pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  264. #pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  265. pFeatures = patchcanvas.features_t()
  266. pFeatures.group_info = False
  267. pFeatures.group_rename = False
  268. pFeatures.port_info = False
  269. pFeatures.port_rename = False
  270. pFeatures.handle_group_pos = True
  271. #patchcanvas.setOptions(pOptions)
  272. patchcanvas.setFeatures(pFeatures)
  273. patchcanvas.init("Carla", self.scene, CanvasCallback, False)
  274. if Carla.host.is_engine_running():
  275. Carla.host.patchbay_refresh()
  276. # -----------------------------------------------------------------
  277. @pyqtSlot(int, int, float)
  278. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  279. if pluginId >= self.fPluginCount:
  280. return
  281. pitem = self.fPluginList[pluginId]
  282. if pitem is None:
  283. return
  284. pitem.setParameterValue(index, value)
  285. @pyqtSlot(int, int, float)
  286. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  287. if pluginId >= self.fPluginCount:
  288. return
  289. pitem = self.fPluginList[pluginId]
  290. if pitem is None:
  291. return
  292. pitem.setParameterDefault(index, value)
  293. @pyqtSlot(int, int, int)
  294. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  295. if pluginId >= self.fPluginCount:
  296. return
  297. pitem = self.fPluginList[pluginId]
  298. if pitem is None:
  299. return
  300. pitem.setParameterMidiChannel(index, channel)
  301. @pyqtSlot(int, int, int)
  302. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  303. if pluginId >= self.fPluginCount:
  304. return
  305. pitem = self.fPluginList[pluginId]
  306. if pitem is None:
  307. return
  308. pitem.setParameterMidiControl(index, cc)
  309. # -----------------------------------------------------------------
  310. @pyqtSlot(int, int)
  311. def slot_handleProgramChangedCallback(self, pluginId, index):
  312. if pluginId >= self.fPluginCount:
  313. return
  314. pitem = self.fPluginList[pluginId]
  315. if pitem is None:
  316. return
  317. pitem.setProgram(index)
  318. @pyqtSlot(int, int)
  319. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  320. if pluginId >= self.fPluginCount:
  321. return
  322. pitem = self.fPluginList[pluginId]
  323. if pitem is None:
  324. return
  325. pitem.setMidiProgram(index)
  326. # -----------------------------------------------------------------
  327. @pyqtSlot(int, int, int, int)
  328. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  329. if pluginId >= self.fPluginCount:
  330. return
  331. pitem = self.fPluginList[pluginId]
  332. if pitem is None:
  333. return
  334. pitem.sendNoteOn(channel, note)
  335. @pyqtSlot(int, int, int)
  336. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  337. if pluginId >= self.fPluginCount:
  338. return
  339. pitem = self.fPluginList[pluginId]
  340. if pitem is None:
  341. return
  342. pitem.sendNoteOff(channel, note)
  343. # -----------------------------------------------------------------
  344. @pyqtSlot(int, int)
  345. def slot_handleShowGuiCallback(self, pluginId, state):
  346. pass
  347. # -----------------------------------------------------------------
  348. @pyqtSlot(int)
  349. def slot_handleUpdateCallback(self, pluginId):
  350. if pluginId >= self.fPluginCount:
  351. return
  352. pitem = self.fPluginList[pluginId]
  353. if pitem is None:
  354. return
  355. pitem.updateInfo()
  356. @pyqtSlot(int)
  357. def slot_handleReloadInfoCallback(self, pluginId):
  358. if pluginId >= self.fPluginCount:
  359. return
  360. pitem = self.fPluginList[pluginId]
  361. if pitem is None:
  362. return
  363. pitem.reloadInfo()
  364. @pyqtSlot(int)
  365. def slot_handleReloadParametersCallback(self, pluginId):
  366. if pluginId >= self.fPluginCount:
  367. return
  368. pitem = self.fPluginList[pluginId]
  369. if pitem is None:
  370. return
  371. pitem.reloadParameters()
  372. @pyqtSlot(int)
  373. def slot_handleReloadProgramsCallback(self, pluginId):
  374. if pluginId >= self.fPluginCount:
  375. return
  376. pitem = self.fPluginList[pluginId]
  377. if pitem is None:
  378. return
  379. pitem.reloadPrograms()
  380. @pyqtSlot(int)
  381. def slot_handleReloadAllCallback(self, pluginId):
  382. if pluginId >= self.fPluginCount:
  383. return
  384. pitem = self.fPluginList[pluginId]
  385. if pitem is None:
  386. return
  387. pitem.reloadAll()
  388. # -----------------------------------------------------------------
  389. @pyqtSlot(int, int, str)
  390. def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, clientName):
  391. pcSplit = patchcanvas.SPLIT_UNDEF
  392. pcIcon = patchcanvas.ICON_APPLICATION
  393. if clientIcon == PATCHBAY_ICON_HARDWARE:
  394. pcSplit = patchcanvas.SPLIT_YES
  395. pcIcon = patchcanvas.ICON_HARDWARE
  396. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  397. pcIcon = patchcanvas.ICON_DISTRHO
  398. elif clientIcon == PATCHBAY_ICON_FILE:
  399. pcIcon = patchcanvas.ICON_FILE
  400. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  401. pcIcon = patchcanvas.ICON_PLUGIN
  402. patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
  403. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  404. @pyqtSlot(int, str)
  405. def slot_handlePatchbayClientRemovedCallback(self, clientId, clientName):
  406. #if not self.fEngineStarted: return
  407. patchcanvas.removeGroup(clientId)
  408. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  409. @pyqtSlot(int, str)
  410. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  411. patchcanvas.renameGroup(clientId, newClientName)
  412. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  413. @pyqtSlot(int, int, int, str)
  414. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  415. if (portFlags & PATCHBAY_PORT_IS_INPUT):
  416. portMode = patchcanvas.PORT_MODE_INPUT
  417. elif (portFlags & PATCHBAY_PORT_IS_OUTPUT):
  418. portMode = patchcanvas.PORT_MODE_OUTPUT
  419. else:
  420. portMode = patchcanvas.PORT_MODE_NULL
  421. if (portFlags & PATCHBAY_PORT_IS_AUDIO):
  422. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  423. elif (portFlags & PATCHBAY_PORT_IS_MIDI):
  424. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  425. else:
  426. portType = patchcanvas.PORT_TYPE_NULL
  427. patchcanvas.addPort(clientId, portId, portName, portMode, portType)
  428. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  429. @pyqtSlot(int, int, str)
  430. def slot_handlePatchbayPortRemovedCallback(self, groupId, portId, fullPortName):
  431. #if not self.fEngineStarted: return
  432. patchcanvas.removePort(portId)
  433. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  434. @pyqtSlot(int, int, str)
  435. def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName):
  436. patchcanvas.renamePort(portId, newPortName)
  437. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  438. @pyqtSlot(int, int, int)
  439. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, portOutId, portInId):
  440. patchcanvas.connectPorts(connectionId, portOutId, portInId)
  441. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  442. @pyqtSlot(int)
  443. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId):
  444. #if not self.fEngineStarted: return
  445. patchcanvas.disconnectPorts(connectionId)
  446. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  447. @pyqtSlot(int, int)
  448. def slot_handlePatchbayIconChangedCallback(self, clientId, clientIcon):
  449. pcIcon = patchcanvas.ICON_APPLICATION
  450. if clientIcon == PATCHBAY_ICON_HARDWARE:
  451. pcIcon = patchcanvas.ICON_HARDWARE
  452. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  453. pcIcon = patchcanvas.ICON_DISTRHO
  454. elif clientIcon == PATCHBAY_ICON_FILE:
  455. pcIcon = patchcanvas.ICON_FILE
  456. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  457. pcIcon = patchcanvas.ICON_PLUGIN
  458. patchcanvas.setGroupIcon(clientId, pcIcon)
  459. # -----------------------------------------------------------------
  460. @pyqtSlot()
  461. def slot_canvasArrange(self):
  462. patchcanvas.arrange()
  463. @pyqtSlot()
  464. def slot_canvasRefresh(self):
  465. patchcanvas.clear()
  466. if Carla.host.is_engine_running():
  467. Carla.host.patchbay_refresh()
  468. #QTimer.singleShot(1000 if self.fSavedSettings['Canvas/EyeCandy'] else 0, self.ui.miniCanvasPreview, SLOT("update()"))
  469. @pyqtSlot()
  470. def slot_canvasZoomFit(self):
  471. self.scene.zoom_fit()
  472. @pyqtSlot()
  473. def slot_canvasZoomIn(self):
  474. self.scene.zoom_in()
  475. @pyqtSlot()
  476. def slot_canvasZoomOut(self):
  477. self.scene.zoom_out()
  478. @pyqtSlot()
  479. def slot_canvasZoomReset(self):
  480. self.scene.zoom_reset()
  481. @pyqtSlot()
  482. def slot_canvasPrint(self):
  483. self.scene.clearSelection()
  484. self.fExportPrinter = QPrinter()
  485. dialog = QPrintDialog(self.fExportPrinter, self)
  486. if dialog.exec_():
  487. painter = QPainter(self.fExportPrinter)
  488. painter.save()
  489. painter.setRenderHint(QPainter.Antialiasing)
  490. painter.setRenderHint(QPainter.TextAntialiasing)
  491. self.scene.render(painter)
  492. painter.restore()
  493. @pyqtSlot()
  494. def slot_canvasSaveImage(self):
  495. newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)"))
  496. if newPath:
  497. self.scene.clearSelection()
  498. # FIXME - must be a better way...
  499. if newPath.endswith((".jpg", ".jpG", ".jPG", ".JPG", ".JPg", ".Jpg")):
  500. imgFormat = "JPG"
  501. elif newPath.endswith((".png", ".pnG", ".pNG", ".PNG", ".PNg", ".Png")):
  502. imgFormat = "PNG"
  503. else:
  504. # File-dialog may not auto-add the extension
  505. imgFormat = "PNG"
  506. newPath += ".png"
  507. self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32)
  508. painter = QPainter(self.fExportImage)
  509. painter.save()
  510. painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this
  511. painter.setRenderHint(QPainter.TextAntialiasing)
  512. self.scene.render(painter)
  513. self.fExportImage.save(newPath, imgFormat, 100)
  514. painter.restore()
  515. # ------------------------------------------------------------------------------------------------
  516. # Canvas callback
  517. def CanvasCallback(action, value1, value2, valueStr):
  518. if action == patchcanvas.ACTION_GROUP_INFO:
  519. pass
  520. elif action == patchcanvas.ACTION_GROUP_RENAME:
  521. pass
  522. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  523. groupId = value1
  524. patchcanvas.splitGroup(groupId)
  525. Carla.gui.ui.miniCanvasPreview.update()
  526. elif action == patchcanvas.ACTION_GROUP_JOIN:
  527. groupId = value1
  528. patchcanvas.joinGroup(groupId)
  529. Carla.gui.ui.miniCanvasPreview.update()
  530. elif action == patchcanvas.ACTION_PORT_INFO:
  531. pass
  532. elif action == patchcanvas.ACTION_PORT_RENAME:
  533. pass
  534. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  535. portIdA = value1
  536. portIdB = value2
  537. if not Carla.host.patchbay_connect(portIdA, portIdB):
  538. print("Connection failed:", cString(Carla.host.get_last_error()))
  539. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  540. connectionId = value1
  541. if not Carla.host.patchbay_disconnect(connectionId):
  542. print("Disconnect failed:", cString(Carla.host.get_last_error()))