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.

973 lines
32KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # PatchBay Canvas engine using QGraphicsView/Scene
  4. # Copyright (C) 2010-2019 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. from PyQt5.QtCore import pyqtSlot, qCritical, qFatal, qWarning, QObject
  20. from PyQt5.QtCore import QPointF, QRectF, QTimer
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom)
  23. from . import (
  24. canvas,
  25. features,
  26. options,
  27. group_dict_t,
  28. port_dict_t,
  29. connection_dict_t,
  30. bool2str,
  31. icon2str,
  32. split2str,
  33. port_mode2str,
  34. port_type2str,
  35. CanvasIconType,
  36. CanvasRubberbandType,
  37. ACTION_PORTS_DISCONNECT,
  38. EYECANDY_FULL,
  39. ICON_APPLICATION,
  40. ICON_HARDWARE,
  41. ICON_LADISH_ROOM,
  42. PORT_MODE_INPUT,
  43. PORT_MODE_OUTPUT,
  44. SPLIT_YES,
  45. SPLIT_NO,
  46. SPLIT_UNDEF,
  47. MAX_PLUGIN_ID_ALLOWED,
  48. )
  49. from .canvasbox import CanvasBox
  50. from .canvasbezierline import CanvasBezierLine
  51. from .canvasline import CanvasLine
  52. from .theme import Theme, getDefaultTheme, getThemeName
  53. from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRemoveItemFX
  54. # FIXME
  55. from . import *
  56. from .scene import PatchScene
  57. from carla_shared import QSafeSettings
  58. # ------------------------------------------------------------------------------------------------------------
  59. class CanvasObject(QObject):
  60. def __init__(self, parent=None):
  61. QObject.__init__(self, parent)
  62. @pyqtSlot()
  63. def AnimationFinishedShow(self):
  64. animation = self.sender()
  65. if animation:
  66. animation.forceStop()
  67. canvas.animation_list.remove(animation)
  68. @pyqtSlot()
  69. def AnimationFinishedHide(self):
  70. animation = self.sender()
  71. if animation:
  72. animation.forceStop()
  73. canvas.animation_list.remove(animation)
  74. item = animation.item()
  75. if item:
  76. item.hide()
  77. @pyqtSlot()
  78. def AnimationFinishedDestroy(self):
  79. animation = self.sender()
  80. if animation:
  81. animation.forceStop()
  82. canvas.animation_list.remove(animation)
  83. item = animation.item()
  84. if item:
  85. CanvasRemoveItemFX(item)
  86. @pyqtSlot()
  87. def PortContextMenuConnect(self):
  88. try:
  89. sources, targets = self.sender().data()
  90. except:
  91. return
  92. for port_type in (PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER):
  93. source_ports = sources[port_type]
  94. target_ports = targets[port_type]
  95. source_ports_len = len(source_ports)
  96. target_ports_len = len(target_ports)
  97. if source_ports_len == 0 or target_ports_len == 0:
  98. continue
  99. for i in range(min(source_ports_len, target_ports_len)):
  100. data = "%i:%i:%i:%i" % (source_ports[i][0],
  101. source_ports[i][1],
  102. target_ports[i][0],
  103. target_ports[i][1])
  104. CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data)
  105. if source_ports_len == 1 and target_ports_len > 1:
  106. for i in range(1, target_ports_len):
  107. data = "%i:%i:%i:%i" % (source_ports[0][0],
  108. source_ports[0][1],
  109. target_ports[i][0],
  110. target_ports[i][1])
  111. CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data)
  112. @pyqtSlot()
  113. def PortContextMenuDisconnect(self):
  114. try:
  115. connectionId = int(self.sender().data())
  116. except:
  117. return
  118. CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "")
  119. # ------------------------------------------------------------------------------------------------------------
  120. def getStoredCanvasPosition(key, fallback_pos):
  121. try:
  122. return canvas.settings.value("CanvasPositions/" + key, fallback_pos, type=QPointF)
  123. except:
  124. return fallback_pos
  125. def getStoredCanvasSplit(group_name, fallback_split_mode):
  126. try:
  127. return canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, fallback_split_mode, type=int)
  128. except:
  129. return fallback_split_mode
  130. # ------------------------------------------------------------------------------------------------------------
  131. def init(appName, scene, callback, debug=False):
  132. if debug:
  133. print("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug)))
  134. if canvas.initiated:
  135. qCritical("PatchCanvas::init() - already initiated")
  136. return
  137. if not callback:
  138. qFatal("PatchCanvas::init() - fatal error: callback not set")
  139. return
  140. canvas.callback = callback
  141. canvas.debug = debug
  142. canvas.scene = scene
  143. canvas.last_z_value = 0
  144. canvas.last_connection_id = 0
  145. canvas.initial_pos = QPointF(0, 0)
  146. canvas.size_rect = QRectF()
  147. if not canvas.qobject:
  148. canvas.qobject = CanvasObject()
  149. if not canvas.settings:
  150. canvas.settings = QSafeSettings("falkTX", appName)
  151. if canvas.theme:
  152. del canvas.theme
  153. canvas.theme = None
  154. for i in range(Theme.THEME_MAX):
  155. this_theme_name = getThemeName(i)
  156. if this_theme_name == options.theme_name:
  157. canvas.theme = Theme(i)
  158. break
  159. if not canvas.theme:
  160. canvas.theme = Theme(getDefaultTheme())
  161. canvas.scene.updateTheme()
  162. canvas.initiated = True
  163. def clear():
  164. if canvas.debug:
  165. print("PatchCanvas::clear()")
  166. group_list_ids = []
  167. port_list_ids = []
  168. connection_list_ids = []
  169. for group in canvas.group_list:
  170. group_list_ids.append(group.group_id)
  171. for port in canvas.port_list:
  172. port_list_ids.append((port.group_id, port.port_id))
  173. for connection in canvas.connection_list:
  174. connection_list_ids.append(connection.connection_id)
  175. for idx in connection_list_ids:
  176. disconnectPorts(idx)
  177. for group_id, port_id in port_list_ids:
  178. removePort(group_id, port_id)
  179. for idx in group_list_ids:
  180. removeGroup(idx)
  181. canvas.last_z_value = 0
  182. canvas.last_connection_id = 0
  183. canvas.group_list = []
  184. canvas.port_list = []
  185. canvas.connection_list = []
  186. canvas.group_plugin_map = {}
  187. canvas.scene.clearSelection()
  188. animatedItems = []
  189. for animation in canvas.animation_list:
  190. animatedItems.append(animation.item())
  191. for item in canvas.scene.items():
  192. if item.type() in (CanvasIconType, CanvasRubberbandType) or item in animatedItems:
  193. continue
  194. canvas.scene.removeItem(item)
  195. del item
  196. canvas.initiated = False
  197. QTimer.singleShot(0, canvas.scene.update)
  198. # ------------------------------------------------------------------------------------------------------------
  199. def setInitialPos(x, y):
  200. if canvas.debug:
  201. print("PatchCanvas::setInitialPos(%i, %i)" % (x, y))
  202. canvas.initial_pos.setX(x)
  203. canvas.initial_pos.setY(y)
  204. def setCanvasSize(x, y, width, height):
  205. if canvas.debug:
  206. print("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height))
  207. canvas.size_rect.setX(x)
  208. canvas.size_rect.setY(y)
  209. canvas.size_rect.setWidth(width)
  210. canvas.size_rect.setHeight(height)
  211. canvas.scene.updateLimits()
  212. canvas.scene.fixScaleFactor()
  213. def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION):
  214. if canvas.debug:
  215. print("PatchCanvas::addGroup(%i, %s, %s, %s)" % (
  216. group_id, group_name.encode(), split2str(split), icon2str(icon)))
  217. for group in canvas.group_list:
  218. if group.group_id == group_id:
  219. qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (
  220. group_id, group_name.encode(), split2str(split), icon2str(icon)))
  221. return
  222. if split == SPLIT_UNDEF:
  223. isHardware = bool(icon == ICON_HARDWARE)
  224. if features.handle_group_pos:
  225. split = getStoredCanvasSplit(group_name, SPLIT_YES if isHardware else split)
  226. elif isHardware:
  227. split = SPLIT_YES
  228. group_box = CanvasBox(group_id, group_name, icon)
  229. group_dict = group_dict_t()
  230. group_dict.group_id = group_id
  231. group_dict.group_name = group_name
  232. group_dict.split = bool(split == SPLIT_YES)
  233. group_dict.icon = icon
  234. group_dict.plugin_id = -1
  235. group_dict.plugin_ui = False
  236. group_dict.plugin_inline = False
  237. group_dict.widgets = [group_box, None]
  238. if split == SPLIT_YES:
  239. group_box.setSplit(True, PORT_MODE_OUTPUT)
  240. if features.handle_group_pos:
  241. group_box.setPos(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False)))
  242. else:
  243. group_box.setPos(CanvasGetNewGroupPos(False))
  244. group_sbox = CanvasBox(group_id, group_name, icon)
  245. group_sbox.setSplit(True, PORT_MODE_INPUT)
  246. group_dict.widgets[1] = group_sbox
  247. if features.handle_group_pos:
  248. group_sbox.setPos(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True)))
  249. else:
  250. group_sbox.setPos(CanvasGetNewGroupPos(True))
  251. canvas.last_z_value += 1
  252. group_sbox.setZValue(canvas.last_z_value)
  253. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  254. CanvasItemFX(group_sbox, True, False)
  255. group_sbox.checkItemPos()
  256. else:
  257. group_box.setSplit(False)
  258. if features.handle_group_pos:
  259. group_box.setPos(getStoredCanvasPosition(group_name, CanvasGetNewGroupPos(False)))
  260. else:
  261. # Special ladish fake-split groups
  262. horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM)
  263. group_box.setPos(CanvasGetNewGroupPos(horizontal))
  264. group_box.checkItemPos()
  265. canvas.last_z_value += 1
  266. group_box.setZValue(canvas.last_z_value)
  267. canvas.group_list.append(group_dict)
  268. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  269. CanvasItemFX(group_box, True, False)
  270. return
  271. QTimer.singleShot(0, canvas.scene.update)
  272. def removeGroup(group_id):
  273. if canvas.debug:
  274. print("PatchCanvas::removeGroup(%i)" % group_id)
  275. for group in canvas.group_list:
  276. if group.group_id == group_id:
  277. item = group.widgets[0]
  278. group_name = group.group_name
  279. if group.split:
  280. s_item = group.widgets[1]
  281. if features.handle_group_pos:
  282. canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
  283. canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
  284. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
  285. if options.eyecandy == EYECANDY_FULL:
  286. CanvasItemFX(s_item, False, True)
  287. else:
  288. s_item.removeIconFromScene()
  289. canvas.scene.removeItem(s_item)
  290. del s_item
  291. else:
  292. if features.handle_group_pos:
  293. canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
  294. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
  295. if options.eyecandy == EYECANDY_FULL:
  296. CanvasItemFX(item, False, True)
  297. else:
  298. item.removeIconFromScene()
  299. canvas.scene.removeItem(item)
  300. del item
  301. canvas.group_list.remove(group)
  302. canvas.group_plugin_map.pop(group.plugin_id, None)
  303. QTimer.singleShot(0, canvas.scene.update)
  304. return
  305. qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
  306. def renameGroup(group_id, new_group_name):
  307. if canvas.debug:
  308. print("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
  309. for group in canvas.group_list:
  310. if group.group_id == group_id:
  311. group.group_name = new_group_name
  312. group.widgets[0].setGroupName(new_group_name)
  313. if group.split and group.widgets[1]:
  314. group.widgets[1].setGroupName(new_group_name)
  315. QTimer.singleShot(0, canvas.scene.update)
  316. return
  317. qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
  318. def splitGroup(group_id):
  319. if canvas.debug:
  320. print("PatchCanvas::splitGroup(%i)" % group_id)
  321. item = None
  322. group_name = ""
  323. group_icon = ICON_APPLICATION
  324. plugin_id = -1
  325. plugin_ui = False
  326. plugin_inline = False
  327. ports_data = []
  328. conns_data = []
  329. # Step 1 - Store all Item data
  330. for group in canvas.group_list:
  331. if group.group_id == group_id:
  332. if group.split:
  333. qCritical("PatchCanvas::splitGroup(%i) - group is already split" % group_id)
  334. return
  335. item = group.widgets[0]
  336. group_name = group.group_name
  337. group_icon = group.icon
  338. plugin_id = group.plugin_id
  339. plugin_ui = group.plugin_ui
  340. plugin_inline = group.plugin_inline
  341. break
  342. if not item:
  343. qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
  344. return
  345. port_list_ids = list(item.getPortList())
  346. for port in canvas.port_list:
  347. if port.port_id in port_list_ids:
  348. port_dict = port_dict_t()
  349. port_dict.group_id = port.group_id
  350. port_dict.port_id = port.port_id
  351. port_dict.port_name = port.port_name
  352. port_dict.port_mode = port.port_mode
  353. port_dict.port_type = port.port_type
  354. port_dict.is_alternate = port.is_alternate
  355. port_dict.widget = None
  356. ports_data.append(port_dict)
  357. for connection in canvas.connection_list:
  358. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  359. connection_dict = connection_dict_t()
  360. connection_dict.connection_id = connection.connection_id
  361. connection_dict.group_in_id = connection.group_in_id
  362. connection_dict.port_in_id = connection.port_in_id
  363. connection_dict.group_out_id = connection.group_out_id
  364. connection_dict.port_out_id = connection.port_out_id
  365. connection_dict.widget = None
  366. conns_data.append(connection_dict)
  367. # Step 2 - Remove Item and Children
  368. for conn in conns_data:
  369. disconnectPorts(conn.connection_id)
  370. for port_id in port_list_ids:
  371. removePort(group_id, port_id)
  372. removeGroup(group_id)
  373. # Step 3 - Re-create Item, now split
  374. addGroup(group_id, group_name, SPLIT_YES, group_icon)
  375. if plugin_id >= 0:
  376. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  377. for port in ports_data:
  378. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  379. for conn in conns_data:
  380. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
  381. QTimer.singleShot(0, canvas.scene.update)
  382. def joinGroup(group_id):
  383. if canvas.debug:
  384. print("PatchCanvas::joinGroup(%i)" % group_id)
  385. item = None
  386. s_item = None
  387. group_name = ""
  388. group_icon = ICON_APPLICATION
  389. plugin_id = -1
  390. plugin_ui = False
  391. plugin_inline = False
  392. ports_data = []
  393. conns_data = []
  394. # Step 1 - Store all Item data
  395. for group in canvas.group_list:
  396. if group.group_id == group_id:
  397. if not group.split:
  398. qCritical("PatchCanvas::joinGroup(%i) - group is not split" % group_id)
  399. return
  400. item = group.widgets[0]
  401. s_item = group.widgets[1]
  402. group_name = group.group_name
  403. group_icon = group.icon
  404. plugin_id = group.plugin_id
  405. plugin_ui = group.plugin_ui
  406. plugin_inline = group.plugin_inline
  407. break
  408. # FIXME
  409. if not (item and s_item):
  410. qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
  411. return
  412. port_list_ids = list(item.getPortList())
  413. port_list_idss = s_item.getPortList()
  414. for port_id in port_list_idss:
  415. if port_id not in port_list_ids:
  416. port_list_ids.append(port_id)
  417. for port in canvas.port_list:
  418. if port.port_id in port_list_ids:
  419. port_dict = port_dict_t()
  420. port_dict.group_id = port.group_id
  421. port_dict.port_id = port.port_id
  422. port_dict.port_name = port.port_name
  423. port_dict.port_mode = port.port_mode
  424. port_dict.port_type = port.port_type
  425. port_dict.is_alternate = port.is_alternate
  426. port_dict.widget = None
  427. ports_data.append(port_dict)
  428. for connection in canvas.connection_list:
  429. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  430. connection_dict = connection_dict_t()
  431. connection_dict.connection_id = connection.connection_id
  432. connection_dict.group_in_id = connection.group_in_id
  433. connection_dict.port_in_id = connection.port_in_id
  434. connection_dict.group_out_id = connection.group_out_id
  435. connection_dict.port_out_id = connection.port_out_id
  436. connection_dict.widget = None
  437. conns_data.append(connection_dict)
  438. # Step 2 - Remove Item and Children
  439. for conn in conns_data:
  440. disconnectPorts(conn.connection_id)
  441. for port_id in port_list_ids:
  442. removePort(group_id, port_id)
  443. removeGroup(group_id)
  444. # Step 3 - Re-create Item, now together
  445. addGroup(group_id, group_name, SPLIT_NO, group_icon)
  446. if plugin_id >= 0:
  447. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  448. for port in ports_data:
  449. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  450. for conn in conns_data:
  451. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id)
  452. QTimer.singleShot(0, canvas.scene.update)
  453. # ------------------------------------------------------------------------------------------------------------
  454. def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
  455. if canvas.debug:
  456. print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
  457. for group in canvas.group_list:
  458. if group.group_id == group_id:
  459. return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos()
  460. qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
  461. return QPointF(0, 0)
  462. def saveGroupPositions():
  463. if canvas.debug:
  464. print("PatchCanvas::getGroupPositions()")
  465. ret = []
  466. for group in canvas.group_list:
  467. if group.split:
  468. pos1 = group.widgets[0].pos()
  469. pos2 = group.widgets[1].pos()
  470. else:
  471. pos1 = group.widgets[0].pos()
  472. pos2 = QPointF(0, 0)
  473. ret.append({
  474. "name" : group.group_name,
  475. "pos1x": pos1.x(),
  476. "pos1y": pos1.y(),
  477. "pos2x": pos2.x(),
  478. "pos2y": pos2.y(),
  479. "split": group.split,
  480. })
  481. return ret
  482. def restoreGroupPositions(dataList):
  483. if canvas.debug:
  484. print("PatchCanvas::restoreGroupPositions(...)")
  485. mapping = {}
  486. for group in canvas.group_list:
  487. mapping[group.group_name] = group
  488. for data in dataList:
  489. name = data['name']
  490. group = mapping.get(name, None)
  491. if group is None:
  492. continue
  493. group.widgets[0].setPos(data['pos1x'], data['pos1y'])
  494. if group.split and group.widgets[1]:
  495. group.widgets[1].setPos(data['pos2x'], data['pos2y'])
  496. def setGroupPos(group_id, group_pos_x, group_pos_y):
  497. setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
  498. def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
  499. if canvas.debug:
  500. print("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (
  501. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  502. for group in canvas.group_list:
  503. if group.group_id == group_id:
  504. group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
  505. if group.split and group.widgets[1]:
  506. group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
  507. QTimer.singleShot(0, canvas.scene.update)
  508. return
  509. qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (
  510. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  511. # ------------------------------------------------------------------------------------------------------------
  512. def setGroupIcon(group_id, icon):
  513. if canvas.debug:
  514. print("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
  515. for group in canvas.group_list:
  516. if group.group_id == group_id:
  517. group.icon = icon
  518. group.widgets[0].setIcon(icon)
  519. if group.split and group.widgets[1]:
  520. group.widgets[1].setIcon(icon)
  521. QTimer.singleShot(0, canvas.scene.update)
  522. return
  523. qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
  524. def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay):
  525. if canvas.debug:
  526. print("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s)" % (
  527. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  528. for group in canvas.group_list:
  529. if group.group_id == group_id:
  530. group.plugin_id = plugin_id
  531. group.plugin_ui = hasUI
  532. group.plugin_inline = hasInlineDisplay
  533. group.widgets[0].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  534. if group.split and group.widgets[1]:
  535. group.widgets[1].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  536. canvas.group_plugin_map[plugin_id] = group
  537. return
  538. qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s) - unable to find group to set as plugin" % (
  539. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  540. # ------------------------------------------------------------------------------------------------------------
  541. def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):
  542. if canvas.debug:
  543. print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % (
  544. group_id, port_id, port_name.encode(),
  545. port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate)))
  546. for port in canvas.port_list:
  547. if port.group_id == group_id and port.port_id == port_id:
  548. qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (
  549. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  550. return
  551. box_widget = None
  552. port_widget = None
  553. for group in canvas.group_list:
  554. if group.group_id == group_id:
  555. if group.split and group.widgets[0].getSplittedMode() != port_mode and group.widgets[1]:
  556. n = 1
  557. else:
  558. n = 0
  559. box_widget = group.widgets[n]
  560. port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate)
  561. break
  562. if not (box_widget and port_widget):
  563. qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (
  564. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  565. return
  566. port_dict = port_dict_t()
  567. port_dict.group_id = group_id
  568. port_dict.port_id = port_id
  569. port_dict.port_name = port_name
  570. port_dict.port_mode = port_mode
  571. port_dict.port_type = port_type
  572. port_dict.is_alternate = is_alternate
  573. port_dict.widget = port_widget
  574. canvas.port_list.append(port_dict)
  575. box_widget.updatePositions()
  576. if options.eyecandy == EYECANDY_FULL:
  577. CanvasItemFX(port_widget, True, False)
  578. return
  579. QTimer.singleShot(0, canvas.scene.update)
  580. def removePort(group_id, port_id):
  581. if canvas.debug:
  582. print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id))
  583. for port in canvas.port_list:
  584. if port.group_id == group_id and port.port_id == port_id:
  585. item = port.widget
  586. item.parentItem().removePortFromGroup(port_id)
  587. canvas.scene.removeItem(item)
  588. canvas.port_list.remove(port)
  589. del item
  590. QTimer.singleShot(0, canvas.scene.update)
  591. return
  592. qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id))
  593. def renamePort(group_id, port_id, new_port_name):
  594. if canvas.debug:
  595. print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode()))
  596. for port in canvas.port_list:
  597. if port.group_id == group_id and port.port_id == port_id:
  598. port.port_name = new_port_name
  599. port.widget.setPortName(new_port_name)
  600. port.widget.parentItem().updatePositions()
  601. QTimer.singleShot(0, canvas.scene.update)
  602. return
  603. qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % (
  604. group_id, port_id, new_port_name.encode()))
  605. def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id):
  606. if canvas.debug:
  607. print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % (
  608. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  609. port_out = None
  610. port_in = None
  611. port_out_parent = None
  612. port_in_parent = None
  613. for port in canvas.port_list:
  614. if port.group_id == group_out_id and port.port_id == port_out_id:
  615. port_out = port.widget
  616. port_out_parent = port_out.parentItem()
  617. elif port.group_id == group_in_id and port.port_id == port_in_id:
  618. port_in = port.widget
  619. port_in_parent = port_in.parentItem()
  620. # FIXME
  621. if not (port_out and port_in):
  622. qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % (
  623. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  624. return
  625. connection_dict = connection_dict_t()
  626. connection_dict.connection_id = connection_id
  627. connection_dict.group_in_id = group_in_id
  628. connection_dict.port_in_id = port_in_id
  629. connection_dict.group_out_id = group_out_id
  630. connection_dict.port_out_id = port_out_id
  631. if options.use_bezier_lines:
  632. connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
  633. else:
  634. connection_dict.widget = CanvasLine(port_out, port_in, None)
  635. canvas.scene.addItem(connection_dict.widget)
  636. port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
  637. port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
  638. canvas.last_z_value += 1
  639. port_out_parent.setZValue(canvas.last_z_value)
  640. port_in_parent.setZValue(canvas.last_z_value)
  641. canvas.last_z_value += 1
  642. connection_dict.widget.setZValue(canvas.last_z_value)
  643. canvas.connection_list.append(connection_dict)
  644. if options.eyecandy == EYECANDY_FULL:
  645. item = connection_dict.widget
  646. CanvasItemFX(item, True, False)
  647. return
  648. QTimer.singleShot(0, canvas.scene.update)
  649. def disconnectPorts(connection_id):
  650. if canvas.debug:
  651. print("PatchCanvas::disconnectPorts(%i)" % connection_id)
  652. line = None
  653. item1 = None
  654. item2 = None
  655. group1id = port1id = 0
  656. group2id = port2id = 0
  657. for connection in canvas.connection_list:
  658. if connection.connection_id == connection_id:
  659. group1id = connection.group_out_id
  660. group2id = connection.group_in_id
  661. port1id = connection.port_out_id
  662. port2id = connection.port_in_id
  663. line = connection.widget
  664. canvas.connection_list.remove(connection)
  665. break
  666. if not line:
  667. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
  668. return
  669. for port in canvas.port_list:
  670. if port.group_id == group1id and port.port_id == port1id:
  671. item1 = port.widget
  672. break
  673. if not item1:
  674. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
  675. return
  676. for port in canvas.port_list:
  677. if port.group_id == group2id and port.port_id == port2id:
  678. item2 = port.widget
  679. break
  680. if not item2:
  681. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
  682. return
  683. item1.parentItem().removeLineFromGroup(connection_id)
  684. item2.parentItem().removeLineFromGroup(connection_id)
  685. if options.eyecandy == EYECANDY_FULL:
  686. CanvasItemFX(line, False, True)
  687. return
  688. canvas.scene.removeItem(line)
  689. del line
  690. QTimer.singleShot(0, canvas.scene.update)
  691. # ------------------------------------------------------------------------------------------------------------
  692. def arrange():
  693. if canvas.debug:
  694. print("PatchCanvas::arrange()")
  695. # ------------------------------------------------------------------------------------------------------------
  696. def updateZValues():
  697. if canvas.debug:
  698. print("PatchCanvas::updateZValues()")
  699. for group in canvas.group_list:
  700. group.widgets[0].resetLinesZValue()
  701. if group.split and group.widgets[1]:
  702. group.widgets[1].resetLinesZValue()
  703. # ------------------------------------------------------------------------------------------------------------
  704. def redrawPluginGroup(plugin_id):
  705. group = canvas.group_plugin_map.get(plugin_id, None)
  706. if group is None:
  707. #qCritical("PatchCanvas::redrawPluginGroup(%i) - unable to find group" % plugin_id)
  708. return
  709. group.widgets[0].redrawInlineDisplay()
  710. if group.split and group.widgets[1]:
  711. group.widgets[1].redrawInlineDisplay()
  712. def handlePluginRemoved(plugin_id):
  713. if canvas.debug:
  714. print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id)
  715. group = canvas.group_plugin_map.pop(plugin_id, None)
  716. if group is not None:
  717. group.plugin_id = -1
  718. group.plugin_ui = False
  719. group.plugin_inline = False
  720. group.widgets[0].removeAsPlugin()
  721. if group.split and group.widgets[1]:
  722. group.widgets[1].removeAsPlugin()
  723. for group in canvas.group_list:
  724. if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  725. continue
  726. group.plugin_id -= 1
  727. group.widgets[0].m_plugin_id -= 1
  728. if group.split and group.widgets[1]:
  729. group.widgets[1].m_plugin_id -= 1
  730. canvas.group_plugin_map[plugin_id] = group
  731. def handleAllPluginsRemoved():
  732. if canvas.debug:
  733. print("PatchCanvas::handleAllPluginsRemoved()")
  734. canvas.group_plugin_map = {}
  735. for group in canvas.group_list:
  736. if group.plugin_id < 0:
  737. continue
  738. if group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  739. continue
  740. group.plugin_id = -1
  741. group.plugin_ui = False
  742. group.plugin_inline = False
  743. group.widgets[0].removeAsPlugin()
  744. if group.split and group.widgets[1]:
  745. group.widgets[1].removeAsPlugin()
  746. # ------------------------------------------------------------------------------------------------------------