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.

patchcanvas.py 32KB

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