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.

1126 lines
38KB

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