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.

715 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(parent.fSavedSettings["Canvas/Antialiasing"] == patchcanvas.ANTIALIASING_FULL))
  54. if parent.fSavedSettings["Canvas/UseOpenGL"] and hasGL:
  55. self.setViewport(QGLWidget(self))
  56. self.setRenderHint(QPainter.HighQualityAntialiasing, parent.fSavedSettings["Canvas/HighQualityAntialiasing"])
  57. pOptions = patchcanvas.options_t()
  58. pOptions.theme_name = parent.fSavedSettings["Canvas/Theme"]
  59. pOptions.auto_hide_groups = parent.fSavedSettings["Canvas/AutoHideGroups"]
  60. pOptions.use_bezier_lines = parent.fSavedSettings["Canvas/UseBezierLines"]
  61. pOptions.antialiasing = parent.fSavedSettings["Canvas/Antialiasing"]
  62. pOptions.eyecandy = parent.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 saveSettings(self, settings):
  178. pass
  179. # -----------------------------------------------------------------
  180. def recheckPluginHints(self, hints):
  181. pass
  182. # -----------------------------------------------------------------
  183. @pyqtSlot()
  184. def slot_pluginsEnable(self):
  185. if not Carla.host.is_engine_running():
  186. return
  187. for i in range(self.fPluginCount):
  188. Carla.host.set_active(i, True)
  189. @pyqtSlot()
  190. def slot_pluginsDisable(self):
  191. if not Carla.host.is_engine_running():
  192. return
  193. for i in range(self.fPluginCount):
  194. Carla.host.set_active(i, False)
  195. @pyqtSlot()
  196. def slot_pluginsVolume100(self):
  197. if not Carla.host.is_engine_running():
  198. return
  199. for i in range(self.fPluginCount):
  200. pitem = self.fPluginList[i]
  201. if pitem is None:
  202. break
  203. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  204. pitem.setParameterValue(PARAMETER_VOLUME, 1.0)
  205. Carla.host.set_volume(i, 1.0)
  206. @pyqtSlot()
  207. def slot_pluginsMute(self):
  208. if not Carla.host.is_engine_running():
  209. return
  210. for i in range(self.fPluginCount):
  211. pitem = self.fPluginList[i]
  212. if pitem is None:
  213. break
  214. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  215. pitem.setParameterValue(PARAMETER_VOLUME, 0.0)
  216. Carla.host.set_volume(i, 0.0)
  217. @pyqtSlot()
  218. def slot_pluginsWet100(self):
  219. if not Carla.host.is_engine_running():
  220. return
  221. for i in range(self.fPluginCount):
  222. pitem = self.fPluginList[i]
  223. if pitem is None:
  224. break
  225. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  226. pitem.setParameterValue(PARAMETER_DRYWET, 1.0)
  227. Carla.host.set_drywet(i, 1.0)
  228. @pyqtSlot()
  229. def slot_pluginsBypass(self):
  230. if not Carla.host.is_engine_running():
  231. return
  232. for i in range(self.fPluginCount):
  233. pitem = self.fPluginList[i]
  234. if pitem is None:
  235. break
  236. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  237. pitem.setParameterValue(PARAMETER_DRYWET, 0.0)
  238. Carla.host.set_drywet(i, 0.0)
  239. @pyqtSlot()
  240. def slot_pluginsCenter(self):
  241. if not Carla.host.is_engine_running():
  242. return
  243. for i in range(self.fPluginCount):
  244. pitem = self.fPluginList[i]
  245. if pitem is None:
  246. break
  247. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  248. pitem.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  249. pitem.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  250. Carla.host.set_balance_left(i, -1.0)
  251. Carla.host.set_balance_right(i, 1.0)
  252. if pitem.fPluginInfo['hints'] & PLUGIN_CAN_PANNING:
  253. pitem.setParameterValue(PARAMETER_PANNING, 1.0)
  254. Carla.host.set_panning(i, 1.0)
  255. # -----------------------------------------------------------------
  256. @pyqtSlot()
  257. def slot_configureCarla(self):
  258. if self.fParent is None or not self.fParent.openSettingsWindow(False, False):
  259. return
  260. self.fParent.loadSettings(False)
  261. patchcanvas.clear()
  262. pOptions = patchcanvas.options_t()
  263. pOptions.theme_name = self.fParent.fSavedSettings["Canvas/Theme"]
  264. pOptions.auto_hide_groups = self.fParent.fSavedSettings["Canvas/AutoHideGroups"]
  265. pOptions.use_bezier_lines = self.fParent.fSavedSettings["Canvas/UseBezierLines"]
  266. pOptions.antialiasing = self.fParent.fSavedSettings["Canvas/Antialiasing"]
  267. pOptions.eyecandy = self.fParent.fSavedSettings["Canvas/EyeCandy"]
  268. pFeatures = patchcanvas.features_t()
  269. pFeatures.group_info = False
  270. pFeatures.group_rename = False
  271. pFeatures.port_info = False
  272. pFeatures.port_rename = False
  273. pFeatures.handle_group_pos = True
  274. patchcanvas.setOptions(pOptions)
  275. patchcanvas.setFeatures(pFeatures)
  276. patchcanvas.init("Carla", self.scene, CanvasCallback, False)
  277. if Carla.host.is_engine_running():
  278. Carla.host.patchbay_refresh()
  279. # -----------------------------------------------------------------
  280. @pyqtSlot(int, int, float)
  281. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  282. if pluginId >= self.fPluginCount:
  283. return
  284. pitem = self.fPluginList[pluginId]
  285. if pitem is None:
  286. return
  287. pitem.setParameterValue(index, value)
  288. @pyqtSlot(int, int, float)
  289. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  290. if pluginId >= self.fPluginCount:
  291. return
  292. pitem = self.fPluginList[pluginId]
  293. if pitem is None:
  294. return
  295. pitem.setParameterDefault(index, value)
  296. @pyqtSlot(int, int, int)
  297. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  298. if pluginId >= self.fPluginCount:
  299. return
  300. pitem = self.fPluginList[pluginId]
  301. if pitem is None:
  302. return
  303. pitem.setParameterMidiChannel(index, channel)
  304. @pyqtSlot(int, int, int)
  305. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  306. if pluginId >= self.fPluginCount:
  307. return
  308. pitem = self.fPluginList[pluginId]
  309. if pitem is None:
  310. return
  311. pitem.setParameterMidiControl(index, cc)
  312. # -----------------------------------------------------------------
  313. @pyqtSlot(int, int)
  314. def slot_handleProgramChangedCallback(self, pluginId, index):
  315. if pluginId >= self.fPluginCount:
  316. return
  317. pitem = self.fPluginList[pluginId]
  318. if pitem is None:
  319. return
  320. pitem.setProgram(index)
  321. @pyqtSlot(int, int)
  322. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  323. if pluginId >= self.fPluginCount:
  324. return
  325. pitem = self.fPluginList[pluginId]
  326. if pitem is None:
  327. return
  328. pitem.setMidiProgram(index)
  329. # -----------------------------------------------------------------
  330. @pyqtSlot(int, int, int, int)
  331. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  332. if pluginId >= self.fPluginCount:
  333. return
  334. pitem = self.fPluginList[pluginId]
  335. if pitem is None:
  336. return
  337. pitem.sendNoteOn(channel, note)
  338. @pyqtSlot(int, int, int)
  339. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  340. if pluginId >= self.fPluginCount:
  341. return
  342. pitem = self.fPluginList[pluginId]
  343. if pitem is None:
  344. return
  345. pitem.sendNoteOff(channel, note)
  346. # -----------------------------------------------------------------
  347. @pyqtSlot(int, int)
  348. def slot_handleShowGuiCallback(self, pluginId, state):
  349. pass
  350. # -----------------------------------------------------------------
  351. @pyqtSlot(int)
  352. def slot_handleUpdateCallback(self, pluginId):
  353. if pluginId >= self.fPluginCount:
  354. return
  355. pitem = self.fPluginList[pluginId]
  356. if pitem is None:
  357. return
  358. pitem.updateInfo()
  359. @pyqtSlot(int)
  360. def slot_handleReloadInfoCallback(self, pluginId):
  361. if pluginId >= self.fPluginCount:
  362. return
  363. pitem = self.fPluginList[pluginId]
  364. if pitem is None:
  365. return
  366. pitem.reloadInfo()
  367. @pyqtSlot(int)
  368. def slot_handleReloadParametersCallback(self, pluginId):
  369. if pluginId >= self.fPluginCount:
  370. return
  371. pitem = self.fPluginList[pluginId]
  372. if pitem is None:
  373. return
  374. pitem.reloadParameters()
  375. @pyqtSlot(int)
  376. def slot_handleReloadProgramsCallback(self, pluginId):
  377. if pluginId >= self.fPluginCount:
  378. return
  379. pitem = self.fPluginList[pluginId]
  380. if pitem is None:
  381. return
  382. pitem.reloadPrograms()
  383. @pyqtSlot(int)
  384. def slot_handleReloadAllCallback(self, pluginId):
  385. if pluginId >= self.fPluginCount:
  386. return
  387. pitem = self.fPluginList[pluginId]
  388. if pitem is None:
  389. return
  390. pitem.reloadAll()
  391. # -----------------------------------------------------------------
  392. @pyqtSlot(int, int, str)
  393. def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, clientName):
  394. pcSplit = patchcanvas.SPLIT_UNDEF
  395. pcIcon = patchcanvas.ICON_APPLICATION
  396. if clientIcon == PATCHBAY_ICON_HARDWARE:
  397. pcSplit = patchcanvas.SPLIT_YES
  398. pcIcon = patchcanvas.ICON_HARDWARE
  399. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  400. pcIcon = patchcanvas.ICON_DISTRHO
  401. elif clientIcon == PATCHBAY_ICON_FILE:
  402. pcIcon = patchcanvas.ICON_FILE
  403. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  404. pcIcon = patchcanvas.ICON_PLUGIN
  405. patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
  406. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  407. @pyqtSlot(int, str)
  408. def slot_handlePatchbayClientRemovedCallback(self, clientId, clientName):
  409. #if not self.fEngineStarted: return
  410. patchcanvas.removeGroup(clientId)
  411. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  412. @pyqtSlot(int, str)
  413. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  414. patchcanvas.renameGroup(clientId, newClientName)
  415. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  416. @pyqtSlot(int, int, int, str)
  417. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  418. if (portFlags & PATCHBAY_PORT_IS_INPUT):
  419. portMode = patchcanvas.PORT_MODE_INPUT
  420. elif (portFlags & PATCHBAY_PORT_IS_OUTPUT):
  421. portMode = patchcanvas.PORT_MODE_OUTPUT
  422. else:
  423. portMode = patchcanvas.PORT_MODE_NULL
  424. if (portFlags & PATCHBAY_PORT_IS_AUDIO):
  425. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  426. elif (portFlags & PATCHBAY_PORT_IS_MIDI):
  427. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  428. else:
  429. portType = patchcanvas.PORT_TYPE_NULL
  430. patchcanvas.addPort(clientId, portId, portName, portMode, portType)
  431. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  432. @pyqtSlot(int, int, str)
  433. def slot_handlePatchbayPortRemovedCallback(self, groupId, portId, fullPortName):
  434. #if not self.fEngineStarted: return
  435. patchcanvas.removePort(portId)
  436. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  437. @pyqtSlot(int, int, str)
  438. def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName):
  439. patchcanvas.renamePort(portId, newPortName)
  440. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  441. @pyqtSlot(int, int, int)
  442. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, portOutId, portInId):
  443. patchcanvas.connectPorts(connectionId, portOutId, portInId)
  444. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  445. @pyqtSlot(int)
  446. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId):
  447. #if not self.fEngineStarted: return
  448. patchcanvas.disconnectPorts(connectionId)
  449. #QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  450. @pyqtSlot(int, int)
  451. def slot_handlePatchbayIconChangedCallback(self, clientId, clientIcon):
  452. pcIcon = patchcanvas.ICON_APPLICATION
  453. if clientIcon == PATCHBAY_ICON_HARDWARE:
  454. pcIcon = patchcanvas.ICON_HARDWARE
  455. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  456. pcIcon = patchcanvas.ICON_DISTRHO
  457. elif clientIcon == PATCHBAY_ICON_FILE:
  458. pcIcon = patchcanvas.ICON_FILE
  459. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  460. pcIcon = patchcanvas.ICON_PLUGIN
  461. patchcanvas.setGroupIcon(clientId, pcIcon)
  462. # -----------------------------------------------------------------
  463. @pyqtSlot()
  464. def slot_canvasArrange(self):
  465. patchcanvas.arrange()
  466. @pyqtSlot()
  467. def slot_canvasRefresh(self):
  468. patchcanvas.clear()
  469. if Carla.host.is_engine_running():
  470. Carla.host.patchbay_refresh()
  471. #QTimer.singleShot(1000 if self.fSavedSettings['Canvas/EyeCandy'] else 0, self.ui.miniCanvasPreview, SLOT("update()"))
  472. @pyqtSlot()
  473. def slot_canvasZoomFit(self):
  474. self.scene.zoom_fit()
  475. @pyqtSlot()
  476. def slot_canvasZoomIn(self):
  477. self.scene.zoom_in()
  478. @pyqtSlot()
  479. def slot_canvasZoomOut(self):
  480. self.scene.zoom_out()
  481. @pyqtSlot()
  482. def slot_canvasZoomReset(self):
  483. self.scene.zoom_reset()
  484. @pyqtSlot()
  485. def slot_canvasPrint(self):
  486. self.scene.clearSelection()
  487. self.fExportPrinter = QPrinter()
  488. dialog = QPrintDialog(self.fExportPrinter, self)
  489. if dialog.exec_():
  490. painter = QPainter(self.fExportPrinter)
  491. painter.save()
  492. painter.setRenderHint(QPainter.Antialiasing)
  493. painter.setRenderHint(QPainter.TextAntialiasing)
  494. self.scene.render(painter)
  495. painter.restore()
  496. @pyqtSlot()
  497. def slot_canvasSaveImage(self):
  498. newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)"))
  499. if newPath:
  500. self.scene.clearSelection()
  501. # FIXME - must be a better way...
  502. if newPath.endswith((".jpg", ".jpG", ".jPG", ".JPG", ".JPg", ".Jpg")):
  503. imgFormat = "JPG"
  504. elif newPath.endswith((".png", ".pnG", ".pNG", ".PNG", ".PNg", ".Png")):
  505. imgFormat = "PNG"
  506. else:
  507. # File-dialog may not auto-add the extension
  508. imgFormat = "PNG"
  509. newPath += ".png"
  510. self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32)
  511. painter = QPainter(self.fExportImage)
  512. painter.save()
  513. painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this
  514. painter.setRenderHint(QPainter.TextAntialiasing)
  515. self.scene.render(painter)
  516. self.fExportImage.save(newPath, imgFormat, 100)
  517. painter.restore()
  518. # ------------------------------------------------------------------------------------------------
  519. # Canvas callback
  520. def CanvasCallback(action, value1, value2, valueStr):
  521. if action == patchcanvas.ACTION_GROUP_INFO:
  522. pass
  523. elif action == patchcanvas.ACTION_GROUP_RENAME:
  524. pass
  525. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  526. groupId = value1
  527. patchcanvas.splitGroup(groupId)
  528. Carla.gui.ui.miniCanvasPreview.update()
  529. elif action == patchcanvas.ACTION_GROUP_JOIN:
  530. groupId = value1
  531. patchcanvas.joinGroup(groupId)
  532. Carla.gui.ui.miniCanvasPreview.update()
  533. elif action == patchcanvas.ACTION_PORT_INFO:
  534. pass
  535. elif action == patchcanvas.ACTION_PORT_RENAME:
  536. pass
  537. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  538. portIdA = value1
  539. portIdB = value2
  540. if not Carla.host.patchbay_connect(portIdA, portIdB):
  541. print("Connection failed:", cString(Carla.host.get_last_error()))
  542. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  543. connectionId = value1
  544. if not Carla.host.patchbay_disconnect(connectionId):
  545. print("Disconnect failed:", cString(Carla.host.get_last_error()))