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.

1079 lines
36KB

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