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.

1103 lines
37KB

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