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.

1133 lines
38KB

  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. # Callbacks can arrive out of order, so we might have pending events.
  321. if group_id in canvas.pending_group_joins:
  322. joinGroup(group_id)
  323. canvas.pending_group_joins.remove(group_id)
  324. if group_id in canvas.pending_position_changes:
  325. position_change = canvas.pending_position_changes[group_id]
  326. setGroupPosFull(*position_change)
  327. del canvas.pending_position_changes[group_id]
  328. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  329. CanvasItemFX(group_box, True, False)
  330. else:
  331. QTimer.singleShot(0, canvas.scene.update)
  332. return group_dict
  333. def removeGroup(group_id):
  334. if canvas.debug:
  335. print("PatchCanvas::removeGroup(%i)" % group_id)
  336. for group in canvas.group_list:
  337. if group.group_id == group_id:
  338. item = group.widgets[0]
  339. group_name = group.group_name
  340. if group.split:
  341. s_item = group.widgets[1]
  342. if features.handle_group_pos:
  343. canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
  344. canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
  345. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
  346. if options.eyecandy == EYECANDY_FULL:
  347. CanvasItemFX(s_item, False, True)
  348. else:
  349. s_item.removeIconFromScene()
  350. canvas.scene.removeItem(s_item)
  351. del s_item
  352. else:
  353. if features.handle_group_pos:
  354. canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
  355. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
  356. if options.eyecandy == EYECANDY_FULL:
  357. CanvasItemFX(item, False, True)
  358. else:
  359. item.removeIconFromScene()
  360. canvas.scene.removeItem(item)
  361. del item
  362. canvas.group_list.remove(group)
  363. canvas.group_plugin_map.pop(group.plugin_id, None)
  364. QTimer.singleShot(0, canvas.scene.update)
  365. return
  366. qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
  367. def renameGroup(group_id, new_group_name):
  368. if canvas.debug:
  369. print("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
  370. for group in canvas.group_list:
  371. if group.group_id == group_id:
  372. group.group_name = new_group_name
  373. group.widgets[0].setGroupName(new_group_name)
  374. if group.split and group.widgets[1]:
  375. group.widgets[1].setGroupName(new_group_name)
  376. QTimer.singleShot(0, canvas.scene.update)
  377. return
  378. qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
  379. def splitGroup(group_id):
  380. if canvas.debug:
  381. print("PatchCanvas::splitGroup(%i)" % group_id)
  382. item = None
  383. group_name = ""
  384. group_icon = ICON_APPLICATION
  385. group_pos = None
  386. plugin_id = -1
  387. plugin_ui = False
  388. plugin_inline = False
  389. ports_data = []
  390. conns_data = []
  391. # Step 1 - Store all Item data
  392. for group in canvas.group_list:
  393. if group.group_id == group_id:
  394. if group.split:
  395. if canvas.debug:
  396. print("PatchCanvas::splitGroup(%i) - group is already split" % group_id)
  397. return
  398. item = group.widgets[0]
  399. group_name = group.group_name
  400. group_icon = group.icon
  401. group_pos = item.pos()
  402. plugin_id = group.plugin_id
  403. plugin_ui = group.plugin_ui
  404. plugin_inline = group.plugin_inline
  405. break
  406. if not item:
  407. qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
  408. return
  409. port_list_ids = list(item.getPortList())
  410. for port in canvas.port_list:
  411. if port.group_id == group_id and port.port_id in port_list_ids:
  412. port_dict = port_dict_t()
  413. port_dict.group_id = port.group_id
  414. port_dict.port_id = port.port_id
  415. port_dict.port_name = port.port_name
  416. port_dict.port_mode = port.port_mode
  417. port_dict.port_type = port.port_type
  418. port_dict.is_alternate = port.is_alternate
  419. port_dict.widget = None
  420. ports_data.append(port_dict)
  421. for connection in canvas.connection_list:
  422. if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \
  423. (connection.group_out_id == group_id and connection.port_out_id in port_list_ids):
  424. connection_dict = connection_dict_t()
  425. connection_dict.connection_id = connection.connection_id
  426. connection_dict.group_in_id = connection.group_in_id
  427. connection_dict.port_in_id = connection.port_in_id
  428. connection_dict.group_out_id = connection.group_out_id
  429. connection_dict.port_out_id = connection.port_out_id
  430. connection_dict.widget = None
  431. conns_data.append(connection_dict)
  432. # Step 2 - Remove Item and Children
  433. for conn in conns_data:
  434. disconnectPorts(conn.connection_id)
  435. for port_id in port_list_ids:
  436. removePort(group_id, port_id)
  437. removeGroup(group_id)
  438. # Step 3 - Re-create Item, now split
  439. group = addGroup(group_id, group_name, SPLIT_YES, group_icon)
  440. if plugin_id >= 0:
  441. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  442. for port in ports_data:
  443. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  444. for conn in conns_data:
  445. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True)
  446. if group is not None:
  447. pos1 = group.widgets[0].pos()
  448. pos2 = group.widgets[1].pos()
  449. group2_pos = QPointF(group_pos.x() + group.widgets[1].boundingRect().width() * 3/2, group_pos.y())
  450. group.widgets[0].blockSignals(True)
  451. group.widgets[0].setPos(group_pos)
  452. group.widgets[0].blockSignals(False)
  453. group.widgets[1].blockSignals(True)
  454. group.widgets[1].setPos(group2_pos)
  455. group.widgets[1].checkItemPos()
  456. group.widgets[1].blockSignals(False)
  457. valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), group2_pos.x(), group2_pos.y())
  458. CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr)
  459. QTimer.singleShot(0, canvas.scene.update)
  460. def joinGroup(group_id):
  461. if canvas.debug:
  462. print("PatchCanvas::joinGroup(%i)" % group_id)
  463. item = None
  464. s_item = None
  465. group_name = ""
  466. group_icon = ICON_APPLICATION
  467. group_pos = None
  468. plugin_id = -1
  469. plugin_ui = False
  470. plugin_inline = False
  471. ports_data = []
  472. conns_data = []
  473. # Step 1 - Store all Item data
  474. for group in canvas.group_list:
  475. if group.group_id == group_id:
  476. if not group.split:
  477. if canvas.debug:
  478. print("PatchCanvas::joinGroup(%i) - group is not split" % group_id)
  479. return
  480. item = group.widgets[0]
  481. s_item = group.widgets[1]
  482. group_name = group.group_name
  483. group_icon = group.icon
  484. group_pos = item.pos()
  485. plugin_id = group.plugin_id
  486. plugin_ui = group.plugin_ui
  487. plugin_inline = group.plugin_inline
  488. break
  489. # FIXME
  490. if not (item and s_item):
  491. qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
  492. canvas.pending_group_joins.add(group_id)
  493. return
  494. port_list_ids = list(item.getPortList())
  495. port_list_idss = s_item.getPortList()
  496. for port_id in port_list_idss:
  497. if port_id not in port_list_ids:
  498. port_list_ids.append(port_id)
  499. for port in canvas.port_list:
  500. if port.group_id == group_id and port.port_id in port_list_ids:
  501. port_dict = port_dict_t()
  502. port_dict.group_id = port.group_id
  503. port_dict.port_id = port.port_id
  504. port_dict.port_name = port.port_name
  505. port_dict.port_mode = port.port_mode
  506. port_dict.port_type = port.port_type
  507. port_dict.is_alternate = port.is_alternate
  508. port_dict.widget = None
  509. ports_data.append(port_dict)
  510. for connection in canvas.connection_list:
  511. if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \
  512. (connection.group_out_id == group_id and connection.port_out_id in port_list_ids):
  513. connection_dict = connection_dict_t()
  514. connection_dict.connection_id = connection.connection_id
  515. connection_dict.group_in_id = connection.group_in_id
  516. connection_dict.port_in_id = connection.port_in_id
  517. connection_dict.group_out_id = connection.group_out_id
  518. connection_dict.port_out_id = connection.port_out_id
  519. connection_dict.widget = None
  520. conns_data.append(connection_dict)
  521. # Step 2 - Remove Item and Children
  522. for conn in conns_data:
  523. disconnectPorts(conn.connection_id)
  524. for port_id in port_list_ids:
  525. removePort(group_id, port_id)
  526. removeGroup(group_id)
  527. # Step 3 - Re-create Item, now together
  528. group = addGroup(group_id, group_name, SPLIT_NO, group_icon)
  529. if plugin_id >= 0:
  530. setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline)
  531. for port in ports_data:
  532. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate)
  533. for conn in conns_data:
  534. connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True)
  535. if group is not None:
  536. group.widgets[0].blockSignals(True)
  537. group.widgets[0].setPos(group_pos)
  538. group.widgets[0].checkItemPos()
  539. group.widgets[0].blockSignals(False)
  540. valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), 0, 0)
  541. CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr)
  542. QTimer.singleShot(0, canvas.scene.update)
  543. # ------------------------------------------------------------------------------------------------------------
  544. def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
  545. if canvas.debug:
  546. print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
  547. for group in canvas.group_list:
  548. if group.group_id == group_id:
  549. return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos()
  550. qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
  551. return QPointF(0, 0)
  552. def saveGroupPositions():
  553. if canvas.debug:
  554. print("PatchCanvas::getGroupPositions()")
  555. ret = []
  556. for group in canvas.group_list:
  557. if group.split:
  558. pos1 = group.widgets[0].pos()
  559. pos2 = group.widgets[1].pos()
  560. else:
  561. pos1 = group.widgets[0].pos()
  562. pos2 = QPointF(0, 0)
  563. ret.append({
  564. "name" : group.group_name,
  565. "pos1x": pos1.x(),
  566. "pos1y": pos1.y(),
  567. "pos2x": pos2.x(),
  568. "pos2y": pos2.y(),
  569. "split": group.split,
  570. })
  571. return ret
  572. def restoreGroupPositions(dataList):
  573. if canvas.debug:
  574. print("PatchCanvas::restoreGroupPositions(...)")
  575. mapping = {}
  576. for group in canvas.group_list:
  577. mapping[group.group_name] = group
  578. for data in dataList:
  579. name = data['name']
  580. group = mapping.get(name, None)
  581. if group is None:
  582. continue
  583. group.widgets[0].blockSignals(True)
  584. group.widgets[0].setPos(data['pos1x'], data['pos1y'])
  585. group.widgets[0].blockSignals(False)
  586. if group.split and group.widgets[1]:
  587. group.widgets[1].blockSignals(True)
  588. group.widgets[1].setPos(data['pos2x'], data['pos2y'])
  589. group.widgets[1].blockSignals(False)
  590. def setGroupPos(group_id, group_pos_x, group_pos_y):
  591. setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
  592. def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
  593. if canvas.debug:
  594. print("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (
  595. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  596. for group in canvas.group_list:
  597. if group.group_id == group_id:
  598. group.widgets[0].blockSignals(True)
  599. group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
  600. group.widgets[0].checkItemPos()
  601. group.widgets[0].blockSignals(False)
  602. if group.split and group.widgets[1]:
  603. group.widgets[1].blockSignals(True)
  604. group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
  605. group.widgets[1].checkItemPos()
  606. group.widgets[1].blockSignals(False)
  607. QTimer.singleShot(0, canvas.scene.update)
  608. return
  609. qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (
  610. group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  611. canvas.pending_position_changes[group_id] = (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i)
  612. # ------------------------------------------------------------------------------------------------------------
  613. def setGroupIcon(group_id, icon):
  614. if canvas.debug:
  615. print("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
  616. for group in canvas.group_list:
  617. if group.group_id == group_id:
  618. group.icon = icon
  619. group.widgets[0].setIcon(icon)
  620. if group.split and group.widgets[1]:
  621. group.widgets[1].setIcon(icon)
  622. QTimer.singleShot(0, canvas.scene.update)
  623. return
  624. qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
  625. def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay):
  626. if canvas.debug:
  627. print("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s)" % (
  628. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  629. for group in canvas.group_list:
  630. if group.group_id == group_id:
  631. group.plugin_id = plugin_id
  632. group.plugin_ui = hasUI
  633. group.plugin_inline = hasInlineDisplay
  634. group.widgets[0].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  635. if group.split and group.widgets[1]:
  636. group.widgets[1].setAsPlugin(plugin_id, hasUI, hasInlineDisplay)
  637. canvas.group_plugin_map[plugin_id] = group
  638. return
  639. qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s) - unable to find group to set as plugin" % (
  640. group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay)))
  641. # ------------------------------------------------------------------------------------------------------------
  642. def focusGroupUsingPluginId(plugin_id):
  643. if canvas.debug:
  644. print("PatchCanvas::focusGroupUsingPluginId(%i)" % (plugin_id,))
  645. if plugin_id < 0 or plugin_id >= MAX_PLUGIN_ID_ALLOWED:
  646. return False
  647. for group in canvas.group_list:
  648. if group.plugin_id == plugin_id:
  649. item = group.widgets[0]
  650. canvas.scene.clearSelection()
  651. canvas.scene.getView().centerOn(item)
  652. item.setSelected(True)
  653. return True
  654. def focusGroupUsingGroupName(group_name):
  655. if canvas.debug:
  656. print("PatchCanvas::focusGroupUsingGroupName(%s)" % (group_name,))
  657. for group in canvas.group_list:
  658. if group.group_name == group_name:
  659. item = group.widgets[0]
  660. canvas.scene.clearSelection()
  661. canvas.scene.getView().centerOn(item)
  662. item.setSelected(True)
  663. return True
  664. # ------------------------------------------------------------------------------------------------------------
  665. def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False):
  666. if canvas.debug:
  667. print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % (
  668. group_id, port_id, port_name.encode(),
  669. port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate)))
  670. for port in canvas.port_list:
  671. if port.group_id == group_id and port.port_id == port_id:
  672. qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (
  673. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  674. return
  675. box_widget = None
  676. port_widget = None
  677. for group in canvas.group_list:
  678. if group.group_id == group_id:
  679. if group.split and group.widgets[0].getSplitMode() != port_mode and group.widgets[1]:
  680. n = 1
  681. else:
  682. n = 0
  683. box_widget = group.widgets[n]
  684. port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate)
  685. break
  686. if not (box_widget and port_widget):
  687. qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (
  688. group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  689. return
  690. port_dict = port_dict_t()
  691. port_dict.group_id = group_id
  692. port_dict.port_id = port_id
  693. port_dict.port_name = port_name
  694. port_dict.port_mode = port_mode
  695. port_dict.port_type = port_type
  696. port_dict.is_alternate = is_alternate
  697. port_dict.widget = port_widget
  698. canvas.port_list.append(port_dict)
  699. box_widget.updatePositions()
  700. if options.eyecandy == EYECANDY_FULL:
  701. CanvasItemFX(port_widget, True, False)
  702. return
  703. QTimer.singleShot(0, canvas.scene.update)
  704. def removePort(group_id, port_id):
  705. if canvas.debug:
  706. print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id))
  707. for port in canvas.port_list:
  708. if port.group_id == group_id and port.port_id == port_id:
  709. item = port.widget
  710. try:
  711. pitem = item.parentItem()
  712. canvas.scene.removeItem(item)
  713. except RuntimeError:
  714. pass
  715. else:
  716. pitem.removePortFromGroup(port_id)
  717. canvas.port_list.remove(port)
  718. del item
  719. QTimer.singleShot(0, canvas.scene.update)
  720. return
  721. qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id))
  722. def renamePort(group_id, port_id, new_port_name):
  723. if canvas.debug:
  724. print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode()))
  725. for port in canvas.port_list:
  726. if port.group_id == group_id and port.port_id == port_id:
  727. port.port_name = new_port_name
  728. port.widget.setPortName(new_port_name)
  729. port.widget.parentItem().updatePositions()
  730. QTimer.singleShot(0, canvas.scene.update)
  731. return
  732. qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % (
  733. group_id, port_id, new_port_name.encode()))
  734. def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id, fromSplitOrJoin = False):
  735. if canvas.last_connection_id >= connection_id and not fromSplitOrJoin:
  736. print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - invalid connection id received (last: %i)" % (
  737. connection_id, group_out_id, port_out_id, group_in_id, port_in_id, canvas.last_connection_id))
  738. return
  739. canvas.last_connection_id = connection_id
  740. if canvas.debug:
  741. print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % (
  742. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  743. port_out = None
  744. port_in = None
  745. port_out_parent = None
  746. port_in_parent = None
  747. for port in canvas.port_list:
  748. if port.group_id == group_out_id and port.port_id == port_out_id:
  749. port_out = port.widget
  750. port_out_parent = port_out.parentItem()
  751. elif port.group_id == group_in_id and port.port_id == port_in_id:
  752. port_in = port.widget
  753. port_in_parent = port_in.parentItem()
  754. # FIXME
  755. if not (port_out and port_in):
  756. qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % (
  757. connection_id, group_out_id, port_out_id, group_in_id, port_in_id))
  758. return
  759. connection_dict = connection_dict_t()
  760. connection_dict.connection_id = connection_id
  761. connection_dict.group_in_id = group_in_id
  762. connection_dict.port_in_id = port_in_id
  763. connection_dict.group_out_id = group_out_id
  764. connection_dict.port_out_id = port_out_id
  765. if options.use_bezier_lines:
  766. connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
  767. else:
  768. connection_dict.widget = CanvasLine(port_out, port_in, None)
  769. canvas.scene.addItem(connection_dict.widget)
  770. port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
  771. port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
  772. canvas.last_z_value += 1
  773. port_out_parent.setZValue(canvas.last_z_value)
  774. port_in_parent.setZValue(canvas.last_z_value)
  775. canvas.last_z_value += 1
  776. connection_dict.widget.setZValue(canvas.last_z_value)
  777. canvas.connection_list.append(connection_dict)
  778. if options.eyecandy == EYECANDY_FULL:
  779. item = connection_dict.widget
  780. CanvasItemFX(item, True, False)
  781. return
  782. QTimer.singleShot(0, canvas.scene.update)
  783. def disconnectPorts(connection_id):
  784. if canvas.debug:
  785. print("PatchCanvas::disconnectPorts(%i)" % connection_id)
  786. line = None
  787. item1 = None
  788. item2 = None
  789. group1id = port1id = 0
  790. group2id = port2id = 0
  791. for connection in canvas.connection_list:
  792. if connection.connection_id == connection_id:
  793. group1id = connection.group_out_id
  794. group2id = connection.group_in_id
  795. port1id = connection.port_out_id
  796. port2id = connection.port_in_id
  797. line = connection.widget
  798. canvas.connection_list.remove(connection)
  799. break
  800. if not line:
  801. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
  802. return
  803. for port in canvas.port_list:
  804. if port.group_id == group1id and port.port_id == port1id:
  805. item1 = port.widget
  806. break
  807. if not item1:
  808. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
  809. return
  810. for port in canvas.port_list:
  811. if port.group_id == group2id and port.port_id == port2id:
  812. item2 = port.widget
  813. break
  814. if not item2:
  815. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
  816. return
  817. item1p = item1.parentItem()
  818. item2p = item2.parentItem()
  819. if item1p:
  820. item1p.removeLineFromGroup(connection_id)
  821. if item2p:
  822. item2p.removeLineFromGroup(connection_id)
  823. if options.eyecandy == EYECANDY_FULL:
  824. CanvasItemFX(line, False, True)
  825. return
  826. canvas.scene.removeItem(line)
  827. del line
  828. QTimer.singleShot(0, canvas.scene.update)
  829. # ------------------------------------------------------------------------------------------------------------
  830. def arrange():
  831. if canvas.debug:
  832. print("PatchCanvas::arrange()")
  833. # ------------------------------------------------------------------------------------------------------------
  834. def updateZValues():
  835. if canvas.debug:
  836. print("PatchCanvas::updateZValues()")
  837. for group in canvas.group_list:
  838. group.widgets[0].resetLinesZValue()
  839. if group.split and group.widgets[1]:
  840. group.widgets[1].resetLinesZValue()
  841. # ------------------------------------------------------------------------------------------------------------
  842. def redrawPluginGroup(plugin_id):
  843. group = canvas.group_plugin_map.get(plugin_id, None)
  844. if group is None:
  845. #qCritical("PatchCanvas::redrawPluginGroup(%i) - unable to find group" % plugin_id)
  846. return
  847. group.widgets[0].redrawInlineDisplay()
  848. if group.split and group.widgets[1]:
  849. group.widgets[1].redrawInlineDisplay()
  850. def handlePluginRemoved(plugin_id):
  851. if canvas.debug:
  852. print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id)
  853. canvas.scene.clearSelection()
  854. group = canvas.group_plugin_map.pop(plugin_id, None)
  855. if group is not None:
  856. group.plugin_id = -1
  857. group.plugin_ui = False
  858. group.plugin_inline = False
  859. group.widgets[0].removeAsPlugin()
  860. if group.split and group.widgets[1]:
  861. group.widgets[1].removeAsPlugin()
  862. for group in canvas.group_list:
  863. if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  864. continue
  865. group.plugin_id -= 1
  866. group.widgets[0].m_plugin_id -= 1
  867. if group.split and group.widgets[1]:
  868. group.widgets[1].m_plugin_id -= 1
  869. canvas.group_plugin_map[plugin_id] = group
  870. def handleAllPluginsRemoved():
  871. if canvas.debug:
  872. print("PatchCanvas::handleAllPluginsRemoved()")
  873. canvas.group_plugin_map = {}
  874. for group in canvas.group_list:
  875. if group.plugin_id < 0:
  876. continue
  877. if group.plugin_id > MAX_PLUGIN_ID_ALLOWED:
  878. continue
  879. group.plugin_id = -1
  880. group.plugin_ui = False
  881. group.plugin_inline = False
  882. group.widgets[0].removeAsPlugin()
  883. if group.split and group.widgets[1]:
  884. group.widgets[1].removeAsPlugin()
  885. # ------------------------------------------------------------------------------------------------------------