1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# PatchBay Canvas engine using QGraphicsView/Scene 5# Copyright (C) 2010-2019 Filipe Coelho <falktx@falktx.com> 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License as 9# published by the Free Software Foundation; either version 2 of 10# the License, or any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# For a full copy of the GNU General Public License see the doc/GPL.txt file. 18 19# ------------------------------------------------------------------------------------------------------------ 20# Imports (Global) 21 22from PyQt5.QtCore import pyqtSlot, qCritical, qFatal, qWarning, QObject 23from PyQt5.QtCore import QPointF, QRectF, QTimer 24from PyQt5.QtWidgets import QGraphicsObject 25 26# ------------------------------------------------------------------------------------------------------------ 27# Imports (Custom) 28 29from . import ( 30 canvas, 31 features, 32 options, 33 group_dict_t, 34 port_dict_t, 35 connection_dict_t, 36 bool2str, 37 icon2str, 38 split2str, 39 port_mode2str, 40 port_type2str, 41 CanvasIconType, 42 CanvasRubberbandType, 43 ACTION_PORTS_DISCONNECT, 44 EYECANDY_FULL, 45 ICON_APPLICATION, 46 ICON_HARDWARE, 47 ICON_LADISH_ROOM, 48 PORT_MODE_INPUT, 49 PORT_MODE_OUTPUT, 50 SPLIT_YES, 51 SPLIT_NO, 52 SPLIT_UNDEF, 53 MAX_PLUGIN_ID_ALLOWED, 54) 55 56from .canvasbox import CanvasBox 57from .canvasbezierline import CanvasBezierLine 58from .canvasline import CanvasLine 59from .theme import Theme, getDefaultTheme, getThemeName 60from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRemoveItemFX 61 62# FIXME 63from . import * 64from .scene import PatchScene 65 66from carla_shared import QSafeSettings 67 68# ------------------------------------------------------------------------------------------------------------ 69 70class CanvasObject(QObject): 71 def __init__(self, parent=None): 72 QObject.__init__(self, parent) 73 74 @pyqtSlot() 75 def AnimationFinishedShow(self): 76 animation = self.sender() 77 if animation: 78 animation.forceStop() 79 canvas.animation_list.remove(animation) 80 81 @pyqtSlot() 82 def AnimationFinishedHide(self): 83 animation = self.sender() 84 if animation: 85 animation.forceStop() 86 canvas.animation_list.remove(animation) 87 item = animation.item() 88 if item: 89 if isinstance(item, QGraphicsObject): 90 item.blockSignals(True) 91 item.hide() 92 item.blockSignals(False) 93 else: 94 item.hide() 95 96 @pyqtSlot() 97 def AnimationFinishedDestroy(self): 98 animation = self.sender() 99 if animation: 100 animation.forceStop() 101 canvas.animation_list.remove(animation) 102 item = animation.item() 103 if item: 104 CanvasRemoveItemFX(item) 105 106 @pyqtSlot() 107 def PortContextMenuConnect(self): 108 try: 109 sources, targets = self.sender().data() 110 except: 111 return 112 113 for port_type in (PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER): 114 source_ports = sources[port_type] 115 target_ports = targets[port_type] 116 117 source_ports_len = len(source_ports) 118 target_ports_len = len(target_ports) 119 120 if source_ports_len == 0 or target_ports_len == 0: 121 continue 122 123 for i in range(min(source_ports_len, target_ports_len)): 124 data = "%i:%i:%i:%i" % (source_ports[i][0], 125 source_ports[i][1], 126 target_ports[i][0], 127 target_ports[i][1]) 128 CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data) 129 130 if source_ports_len == 1 and target_ports_len > 1: 131 for i in range(1, target_ports_len): 132 data = "%i:%i:%i:%i" % (source_ports[0][0], 133 source_ports[0][1], 134 target_ports[i][0], 135 target_ports[i][1]) 136 CanvasCallback(ACTION_PORTS_CONNECT, 0, 0, data) 137 138 @pyqtSlot() 139 def PortContextMenuDisconnect(self): 140 try: 141 connectionId = int(self.sender().data()) 142 except: 143 return 144 145 CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "") 146 147 @pyqtSlot(int, bool, int, int) 148 def boxPositionChanged(self, groupId, split, x, y): 149 x2 = y2 = 0 150 151 if split: 152 for group in canvas.group_list: 153 if group.group_id == groupId: 154 if group.split: 155 pos = group.widgets[1].pos() 156 x2 = pos.x() 157 y2 = pos.y() 158 break 159 160 valueStr = "%i:%i:%i:%i" % (x, y, x2, y2) 161 CanvasCallback(ACTION_GROUP_POSITION, groupId, 0, valueStr) 162 163 @pyqtSlot(int, bool, int, int) 164 def sboxPositionChanged(self, groupId, split, x2, y2): 165 x = y = 0 166 167 for group in canvas.group_list: 168 if group.group_id == groupId: 169 pos = group.widgets[0].pos() 170 x = pos.x() 171 y = pos.y() 172 break 173 174 valueStr = "%i:%i:%i:%i" % (x, y, x2, y2) 175 CanvasCallback(ACTION_GROUP_POSITION, groupId, 0, valueStr) 176 177# ------------------------------------------------------------------------------------------------------------ 178 179def getStoredCanvasPosition(key, fallback_pos): 180 try: 181 return canvas.settings.value("CanvasPositions/" + key, fallback_pos, type=QPointF) 182 except: 183 return fallback_pos 184 185def getStoredCanvasSplit(group_name, fallback_split_mode): 186 try: 187 return canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, fallback_split_mode, type=int) 188 except: 189 return fallback_split_mode 190 191# ------------------------------------------------------------------------------------------------------------ 192 193def init(appName, scene, callback, debug=False): 194 if debug: 195 print("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug))) 196 197 if canvas.initiated: 198 qCritical("PatchCanvas::init() - already initiated") 199 return 200 201 if not callback: 202 qFatal("PatchCanvas::init() - fatal error: callback not set") 203 return 204 205 canvas.callback = callback 206 canvas.debug = debug 207 canvas.scene = scene 208 209 canvas.last_z_value = 0 210 canvas.last_connection_id = 0 211 canvas.initial_pos = QPointF(0, 0) 212 canvas.size_rect = QRectF() 213 214 if not canvas.qobject: 215 canvas.qobject = CanvasObject() 216 if not canvas.settings: 217 canvas.settings = QSafeSettings("falkTX", appName) 218 219 if canvas.theme: 220 del canvas.theme 221 canvas.theme = None 222 223 for i in range(Theme.THEME_MAX): 224 this_theme_name = getThemeName(i) 225 if this_theme_name == options.theme_name: 226 canvas.theme = Theme(i) 227 break 228 229 if not canvas.theme: 230 canvas.theme = Theme(getDefaultTheme()) 231 232 canvas.scene.updateTheme() 233 234 canvas.initiated = True 235 236def clear(): 237 if canvas.debug: 238 print("PatchCanvas::clear()") 239 240 group_pos = {} 241 group_list_ids = [] 242 port_list_ids = [] 243 connection_list_ids = [] 244 245 for group in canvas.group_list: 246 group_pos[group.group_name] = ( 247 group.split, 248 group.widgets[0].pos(), 249 group.widgets[1].pos() if group.split else None, 250 ) 251 group_list_ids.append(group.group_id) 252 253 for port in canvas.port_list: 254 port_list_ids.append((port.group_id, port.port_id)) 255 256 for connection in canvas.connection_list: 257 connection_list_ids.append(connection.connection_id) 258 259 for idx in connection_list_ids: 260 disconnectPorts(idx) 261 262 for group_id, port_id in port_list_ids: 263 removePort(group_id, port_id) 264 265 for idx in group_list_ids: 266 removeGroup(idx) 267 268 canvas.last_z_value = 0 269 canvas.last_connection_id = 0 270 271 canvas.group_list = [] 272 canvas.port_list = [] 273 canvas.connection_list = [] 274 canvas.group_plugin_map = {} 275 canvas.old_group_pos = group_pos 276 277 canvas.scene.clearSelection() 278 279 animatedItems = [] 280 for animation in canvas.animation_list: 281 animatedItems.append(animation.item()) 282 283 for item in canvas.scene.items(): 284 if item.type() in (CanvasIconType, CanvasRubberbandType) or item in animatedItems: 285 continue 286 canvas.scene.removeItem(item) 287 del item 288 289 canvas.initiated = False 290 291 QTimer.singleShot(0, canvas.scene.update) 292 293# ------------------------------------------------------------------------------------------------------------ 294 295def setInitialPos(x, y): 296 if canvas.debug: 297 print("PatchCanvas::setInitialPos(%i, %i)" % (x, y)) 298 299 canvas.initial_pos.setX(x) 300 canvas.initial_pos.setY(y) 301 302def setCanvasSize(x, y, width, height): 303 if canvas.debug: 304 print("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height)) 305 306 canvas.size_rect.setX(x) 307 canvas.size_rect.setY(y) 308 canvas.size_rect.setWidth(width) 309 canvas.size_rect.setHeight(height) 310 canvas.scene.updateLimits() 311 canvas.scene.fixScaleFactor() 312 313def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): 314 if canvas.debug: 315 print("PatchCanvas::addGroup(%i, %s, %s, %s)" % ( 316 group_id, group_name.encode(), split2str(split), icon2str(icon))) 317 318 for group in canvas.group_list: 319 if group.group_id == group_id: 320 qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % ( 321 group_id, group_name.encode(), split2str(split), icon2str(icon))) 322 return None 323 324 old_matching_group = canvas.old_group_pos.pop(group_name, None) 325 326 if split == SPLIT_UNDEF: 327 isHardware = bool(icon == ICON_HARDWARE) 328 329 if features.handle_group_pos: 330 split = getStoredCanvasSplit(group_name, SPLIT_YES if isHardware else split) 331 elif isHardware: 332 split = SPLIT_YES 333 elif old_matching_group is not None and old_matching_group[0]: 334 split = SPLIT_YES 335 336 group_box = CanvasBox(group_id, group_name, icon) 337 group_box.positionChanged.connect(canvas.qobject.boxPositionChanged) 338 group_box.blockSignals(True) 339 340 group_dict = group_dict_t() 341 group_dict.group_id = group_id 342 group_dict.group_name = group_name 343 group_dict.split = bool(split == SPLIT_YES) 344 group_dict.icon = icon 345 group_dict.plugin_id = -1 346 group_dict.plugin_ui = False 347 group_dict.plugin_inline = False 348 group_dict.widgets = [group_box, None] 349 350 if split == SPLIT_YES: 351 group_box.setSplit(True, PORT_MODE_OUTPUT) 352 353 if features.handle_group_pos: 354 group_box.setPos(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False))) 355 elif old_matching_group is not None: 356 group_box.setPos(old_matching_group[1]) 357 else: 358 group_box.setPos(CanvasGetNewGroupPos(False)) 359 360 group_sbox = CanvasBox(group_id, group_name, icon) 361 group_sbox.positionChanged.connect(canvas.qobject.sboxPositionChanged) 362 group_sbox.blockSignals(True) 363 group_sbox.setSplit(True, PORT_MODE_INPUT) 364 365 group_dict.widgets[1] = group_sbox 366 367 if features.handle_group_pos: 368 group_sbox.setPos(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True))) 369 elif old_matching_group is not None and old_matching_group[0]: 370 group_sbox.setPos(old_matching_group[2]) 371 else: 372 group_sbox.setPos(group_box.x() + group_box.boundingRect().width() + 300, group_box.y()) 373 374 canvas.last_z_value += 1 375 group_sbox.setZValue(canvas.last_z_value) 376 377 if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups: 378 CanvasItemFX(group_sbox, True, False) 379 380 group_sbox.checkItemPos() 381 group_sbox.blockSignals(False) 382 383 else: 384 group_box.setSplit(False) 385 386 if features.handle_group_pos: 387 group_box.setPos(getStoredCanvasPosition(group_name, CanvasGetNewGroupPos(False))) 388 elif old_matching_group is not None: 389 group_box.setPos(old_matching_group[1]) 390 else: 391 # Special ladish fake-split groups 392 horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM) 393 group_box.setPos(CanvasGetNewGroupPos(horizontal)) 394 395 canvas.last_z_value += 1 396 group_box.setZValue(canvas.last_z_value) 397 398 group_box.checkItemPos() 399 group_box.blockSignals(False) 400 401 canvas.group_list.append(group_dict) 402 403 if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups: 404 CanvasItemFX(group_box, True, False) 405 else: 406 QTimer.singleShot(0, canvas.scene.update) 407 408 return group_dict 409 410def removeGroup(group_id): 411 if canvas.debug: 412 print("PatchCanvas::removeGroup(%i)" % group_id) 413 414 for group in canvas.group_list: 415 if group.group_id == group_id: 416 item = group.widgets[0] 417 group_name = group.group_name 418 419 if group.split: 420 s_item = group.widgets[1] 421 422 if features.handle_group_pos: 423 canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos()) 424 canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos()) 425 canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES) 426 427 if options.eyecandy == EYECANDY_FULL: 428 CanvasItemFX(s_item, False, True) 429 else: 430 s_item.removeIconFromScene() 431 canvas.scene.removeItem(s_item) 432 del s_item 433 434 else: 435 if features.handle_group_pos: 436 canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos()) 437 canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO) 438 439 if options.eyecandy == EYECANDY_FULL: 440 CanvasItemFX(item, False, True) 441 else: 442 item.removeIconFromScene() 443 canvas.scene.removeItem(item) 444 del item 445 446 canvas.group_list.remove(group) 447 canvas.group_plugin_map.pop(group.plugin_id, None) 448 449 QTimer.singleShot(0, canvas.scene.update) 450 return 451 452 qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id) 453 454def renameGroup(group_id, new_group_name): 455 if canvas.debug: 456 print("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode())) 457 458 for group in canvas.group_list: 459 if group.group_id == group_id: 460 group.group_name = new_group_name 461 group.widgets[0].setGroupName(new_group_name) 462 463 if group.split and group.widgets[1]: 464 group.widgets[1].setGroupName(new_group_name) 465 466 QTimer.singleShot(0, canvas.scene.update) 467 return 468 469 qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode())) 470 471def splitGroup(group_id): 472 if canvas.debug: 473 print("PatchCanvas::splitGroup(%i)" % group_id) 474 475 item = None 476 group_name = "" 477 group_icon = ICON_APPLICATION 478 group_pos = None 479 plugin_id = -1 480 plugin_ui = False 481 plugin_inline = False 482 ports_data = [] 483 conns_data = [] 484 485 # Step 1 - Store all Item data 486 for group in canvas.group_list: 487 if group.group_id == group_id: 488 if group.split: 489 if canvas.debug: 490 print("PatchCanvas::splitGroup(%i) - group is already split" % group_id) 491 return 492 493 item = group.widgets[0] 494 group_name = group.group_name 495 group_icon = group.icon 496 group_pos = item.pos() 497 plugin_id = group.plugin_id 498 plugin_ui = group.plugin_ui 499 plugin_inline = group.plugin_inline 500 break 501 502 if not item: 503 qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id) 504 return 505 506 port_list_ids = list(item.getPortList()) 507 508 for port in canvas.port_list: 509 if port.group_id == group_id and port.port_id in port_list_ids: 510 port_dict = port_dict_t() 511 port_dict.group_id = port.group_id 512 port_dict.port_id = port.port_id 513 port_dict.port_name = port.port_name 514 port_dict.port_mode = port.port_mode 515 port_dict.port_type = port.port_type 516 port_dict.is_alternate = port.is_alternate 517 port_dict.widget = None 518 ports_data.append(port_dict) 519 520 for connection in canvas.connection_list: 521 if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \ 522 (connection.group_out_id == group_id and connection.port_out_id in port_list_ids): 523 connection_dict = connection_dict_t() 524 connection_dict.connection_id = connection.connection_id 525 connection_dict.group_in_id = connection.group_in_id 526 connection_dict.port_in_id = connection.port_in_id 527 connection_dict.group_out_id = connection.group_out_id 528 connection_dict.port_out_id = connection.port_out_id 529 connection_dict.widget = None 530 conns_data.append(connection_dict) 531 532 # Step 2 - Remove Item and Children 533 for conn in conns_data: 534 disconnectPorts(conn.connection_id) 535 536 for port_id in port_list_ids: 537 removePort(group_id, port_id) 538 539 removeGroup(group_id) 540 541 # Step 3 - Re-create Item, now split 542 group = addGroup(group_id, group_name, SPLIT_YES, group_icon) 543 544 if plugin_id >= 0: 545 setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline) 546 547 for port in ports_data: 548 addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate) 549 550 for conn in conns_data: 551 connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True) 552 553 if group is not None: 554 pos1 = group.widgets[0].pos() 555 pos2 = group.widgets[1].pos() 556 group2_pos = QPointF(group_pos.x() + group.widgets[1].boundingRect().width() * 3/2, group_pos.y()) 557 group.widgets[0].blockSignals(True) 558 group.widgets[0].setPos(group_pos) 559 group.widgets[0].blockSignals(False) 560 group.widgets[1].blockSignals(True) 561 group.widgets[1].setPos(group2_pos) 562 group.widgets[1].checkItemPos() 563 group.widgets[1].blockSignals(False) 564 valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), group2_pos.x(), group2_pos.y()) 565 CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr) 566 567 QTimer.singleShot(0, canvas.scene.update) 568 569def joinGroup(group_id): 570 if canvas.debug: 571 print("PatchCanvas::joinGroup(%i)" % group_id) 572 573 item = None 574 s_item = None 575 group_name = "" 576 group_icon = ICON_APPLICATION 577 group_pos = None 578 plugin_id = -1 579 plugin_ui = False 580 plugin_inline = False 581 ports_data = [] 582 conns_data = [] 583 584 # Step 1 - Store all Item data 585 for group in canvas.group_list: 586 if group.group_id == group_id: 587 if not group.split: 588 if canvas.debug: 589 print("PatchCanvas::joinGroup(%i) - group is not split" % group_id) 590 return 591 592 item = group.widgets[0] 593 s_item = group.widgets[1] 594 group_name = group.group_name 595 group_icon = group.icon 596 group_pos = item.pos() 597 plugin_id = group.plugin_id 598 plugin_ui = group.plugin_ui 599 plugin_inline = group.plugin_inline 600 break 601 602 # FIXME 603 if not (item and s_item): 604 qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id) 605 return 606 607 port_list_ids = list(item.getPortList()) 608 port_list_idss = s_item.getPortList() 609 610 for port_id in port_list_idss: 611 if port_id not in port_list_ids: 612 port_list_ids.append(port_id) 613 614 for port in canvas.port_list: 615 if port.group_id == group_id and port.port_id in port_list_ids: 616 port_dict = port_dict_t() 617 port_dict.group_id = port.group_id 618 port_dict.port_id = port.port_id 619 port_dict.port_name = port.port_name 620 port_dict.port_mode = port.port_mode 621 port_dict.port_type = port.port_type 622 port_dict.is_alternate = port.is_alternate 623 port_dict.widget = None 624 ports_data.append(port_dict) 625 626 for connection in canvas.connection_list: 627 if (connection.group_in_id == group_id and connection.port_in_id in port_list_ids) or \ 628 (connection.group_out_id == group_id and connection.port_out_id in port_list_ids): 629 connection_dict = connection_dict_t() 630 connection_dict.connection_id = connection.connection_id 631 connection_dict.group_in_id = connection.group_in_id 632 connection_dict.port_in_id = connection.port_in_id 633 connection_dict.group_out_id = connection.group_out_id 634 connection_dict.port_out_id = connection.port_out_id 635 connection_dict.widget = None 636 conns_data.append(connection_dict) 637 638 # Step 2 - Remove Item and Children 639 for conn in conns_data: 640 disconnectPorts(conn.connection_id) 641 642 for port_id in port_list_ids: 643 removePort(group_id, port_id) 644 645 removeGroup(group_id) 646 647 # Step 3 - Re-create Item, now together 648 group = addGroup(group_id, group_name, SPLIT_NO, group_icon) 649 650 if plugin_id >= 0: 651 setGroupAsPlugin(group_id, plugin_id, plugin_ui, plugin_inline) 652 653 for port in ports_data: 654 addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate) 655 656 for conn in conns_data: 657 connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id, True) 658 659 if group is not None: 660 group.widgets[0].blockSignals(True) 661 group.widgets[0].setPos(group_pos) 662 group.widgets[0].checkItemPos() 663 group.widgets[0].blockSignals(False) 664 valueStr = "%i:%i:%i:%i" % (group_pos.x(), group_pos.y(), 0, 0) 665 CanvasCallback(ACTION_GROUP_POSITION, group_id, 0, valueStr) 666 667 QTimer.singleShot(0, canvas.scene.update) 668 669# ------------------------------------------------------------------------------------------------------------ 670 671def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT): 672 if canvas.debug: 673 print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode))) 674 675 for group in canvas.group_list: 676 if group.group_id == group_id: 677 return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos() 678 679 qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode))) 680 return QPointF(0, 0) 681 682def saveGroupPositions(): 683 if canvas.debug: 684 print("PatchCanvas::getGroupPositions()") 685 686 ret = [] 687 688 for group in canvas.group_list: 689 if group.split: 690 pos1 = group.widgets[0].pos() 691 pos2 = group.widgets[1].pos() 692 else: 693 pos1 = group.widgets[0].pos() 694 pos2 = QPointF(0, 0) 695 696 ret.append({ 697 "name" : group.group_name, 698 "pos1x": pos1.x(), 699 "pos1y": pos1.y(), 700 "pos2x": pos2.x(), 701 "pos2y": pos2.y(), 702 "split": group.split, 703 }) 704 705 return ret 706 707def restoreGroupPositions(dataList): 708 if canvas.debug: 709 print("PatchCanvas::restoreGroupPositions(...)") 710 711 mapping = {} 712 713 for group in canvas.group_list: 714 mapping[group.group_name] = group 715 716 for data in dataList: 717 name = data['name'] 718 group = mapping.get(name, None) 719 720 if group is None: 721 continue 722 723 group.widgets[0].blockSignals(True) 724 group.widgets[0].setPos(data['pos1x'], data['pos1y']) 725 group.widgets[0].blockSignals(False) 726 727 if group.split and group.widgets[1]: 728 group.widgets[1].blockSignals(True) 729 group.widgets[1].setPos(data['pos2x'], data['pos2y']) 730 group.widgets[1].blockSignals(False) 731 732def setGroupPos(group_id, group_pos_x, group_pos_y): 733 setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y) 734 735def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i): 736 if canvas.debug: 737 print("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % ( 738 group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i)) 739 740 for group in canvas.group_list: 741 if group.group_id == group_id: 742 group.widgets[0].blockSignals(True) 743 group.widgets[0].setPos(group_pos_x_o, group_pos_y_o) 744 group.widgets[0].checkItemPos() 745 group.widgets[0].blockSignals(False) 746 747 if group.split and group.widgets[1]: 748 group.widgets[1].blockSignals(True) 749 group.widgets[1].setPos(group_pos_x_i, group_pos_y_i) 750 group.widgets[1].checkItemPos() 751 group.widgets[1].blockSignals(False) 752 753 QTimer.singleShot(0, canvas.scene.update) 754 return 755 756 qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % ( 757 group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i)) 758 759# ------------------------------------------------------------------------------------------------------------ 760 761def setGroupIcon(group_id, icon): 762 if canvas.debug: 763 print("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon))) 764 765 for group in canvas.group_list: 766 if group.group_id == group_id: 767 group.icon = icon 768 group.widgets[0].setIcon(icon) 769 770 if group.split and group.widgets[1]: 771 group.widgets[1].setIcon(icon) 772 773 QTimer.singleShot(0, canvas.scene.update) 774 return 775 776 qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon))) 777 778def setGroupAsPlugin(group_id, plugin_id, hasUI, hasInlineDisplay): 779 if canvas.debug: 780 print("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s)" % ( 781 group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay))) 782 783 for group in canvas.group_list: 784 if group.group_id == group_id: 785 group.plugin_id = plugin_id 786 group.plugin_ui = hasUI 787 group.plugin_inline = hasInlineDisplay 788 group.widgets[0].setAsPlugin(plugin_id, hasUI, hasInlineDisplay) 789 790 if group.split and group.widgets[1]: 791 group.widgets[1].setAsPlugin(plugin_id, hasUI, hasInlineDisplay) 792 793 canvas.group_plugin_map[plugin_id] = group 794 return 795 796 qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s, %s) - unable to find group to set as plugin" % ( 797 group_id, plugin_id, bool2str(hasUI), bool2str(hasInlineDisplay))) 798 799# ------------------------------------------------------------------------------------------------------------ 800 801def focusGroupUsingPluginId(plugin_id): 802 if canvas.debug: 803 print("PatchCanvas::focusGroupUsingPluginId(%i)" % (plugin_id,)) 804 805 if plugin_id < 0 or plugin_id >= MAX_PLUGIN_ID_ALLOWED: 806 return False 807 808 for group in canvas.group_list: 809 if group.plugin_id == plugin_id: 810 item = group.widgets[0] 811 canvas.scene.clearSelection() 812 canvas.scene.getView().centerOn(item) 813 item.setSelected(True) 814 return True 815 816def focusGroupUsingGroupName(group_name): 817 if canvas.debug: 818 print("PatchCanvas::focusGroupUsingGroupName(%s)" % (group_name,)) 819 820 for group in canvas.group_list: 821 if group.group_name == group_name: 822 item = group.widgets[0] 823 canvas.scene.clearSelection() 824 canvas.scene.getView().centerOn(item) 825 item.setSelected(True) 826 return True 827 828# ------------------------------------------------------------------------------------------------------------ 829 830def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False): 831 if canvas.debug: 832 print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % ( 833 group_id, port_id, port_name.encode(), 834 port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate))) 835 836 for port in canvas.port_list: 837 if port.group_id == group_id and port.port_id == port_id: 838 qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % ( 839 group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type))) 840 return 841 842 box_widget = None 843 port_widget = None 844 845 for group in canvas.group_list: 846 if group.group_id == group_id: 847 if group.split and group.widgets[0].getSplitMode() != port_mode and group.widgets[1]: 848 n = 1 849 else: 850 n = 0 851 box_widget = group.widgets[n] 852 port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name, is_alternate) 853 break 854 855 if not (box_widget and port_widget): 856 qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % ( 857 group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type))) 858 return 859 860 port_dict = port_dict_t() 861 port_dict.group_id = group_id 862 port_dict.port_id = port_id 863 port_dict.port_name = port_name 864 port_dict.port_mode = port_mode 865 port_dict.port_type = port_type 866 port_dict.is_alternate = is_alternate 867 port_dict.widget = port_widget 868 canvas.port_list.append(port_dict) 869 870 box_widget.updatePositions() 871 872 if options.eyecandy == EYECANDY_FULL: 873 CanvasItemFX(port_widget, True, False) 874 return 875 876 QTimer.singleShot(0, canvas.scene.update) 877 878def removePort(group_id, port_id): 879 if canvas.debug: 880 print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id)) 881 882 for port in canvas.port_list: 883 if port.group_id == group_id and port.port_id == port_id: 884 item = port.widget 885 try: 886 pitem = item.parentItem() 887 canvas.scene.removeItem(item) 888 except RuntimeError: 889 pass 890 else: 891 pitem.removePortFromGroup(port_id) 892 canvas.port_list.remove(port) 893 del item 894 895 QTimer.singleShot(0, canvas.scene.update) 896 return 897 898 qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id)) 899 900def renamePort(group_id, port_id, new_port_name): 901 if canvas.debug: 902 print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode())) 903 904 for port in canvas.port_list: 905 if port.group_id == group_id and port.port_id == port_id: 906 port.port_name = new_port_name 907 port.widget.setPortName(new_port_name) 908 port.widget.parentItem().updatePositions() 909 910 QTimer.singleShot(0, canvas.scene.update) 911 return 912 913 qCritical("PatchCanvas::renamePort(%i, %i, %s) - Unable to find port to rename" % ( 914 group_id, port_id, new_port_name.encode())) 915 916def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id, fromSplitOrJoin = False): 917 if canvas.last_connection_id >= connection_id and not fromSplitOrJoin: 918 print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - invalid connection id received (last: %i)" % ( 919 connection_id, group_out_id, port_out_id, group_in_id, port_in_id, canvas.last_connection_id)) 920 return 921 922 canvas.last_connection_id = connection_id 923 924 if canvas.debug: 925 print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % ( 926 connection_id, group_out_id, port_out_id, group_in_id, port_in_id)) 927 928 port_out = None 929 port_in = None 930 port_out_parent = None 931 port_in_parent = None 932 933 for port in canvas.port_list: 934 if port.group_id == group_out_id and port.port_id == port_out_id: 935 port_out = port.widget 936 port_out_parent = port_out.parentItem() 937 elif port.group_id == group_in_id and port.port_id == port_in_id: 938 port_in = port.widget 939 port_in_parent = port_in.parentItem() 940 941 # FIXME 942 if not (port_out and port_in): 943 qCritical("PatchCanvas::connectPorts(%i, %i, %i, %i, %i) - unable to find ports to connect" % ( 944 connection_id, group_out_id, port_out_id, group_in_id, port_in_id)) 945 return 946 947 connection_dict = connection_dict_t() 948 connection_dict.connection_id = connection_id 949 connection_dict.group_in_id = group_in_id 950 connection_dict.port_in_id = port_in_id 951 connection_dict.group_out_id = group_out_id 952 connection_dict.port_out_id = port_out_id 953 954 if options.use_bezier_lines: 955 connection_dict.widget = CanvasBezierLine(port_out, port_in, None) 956 else: 957 connection_dict.widget = CanvasLine(port_out, port_in, None) 958 959 canvas.scene.addItem(connection_dict.widget) 960 961 port_out_parent.addLineFromGroup(connection_dict.widget, connection_id) 962 port_in_parent.addLineFromGroup(connection_dict.widget, connection_id) 963 964 canvas.last_z_value += 1 965 port_out_parent.setZValue(canvas.last_z_value) 966 port_in_parent.setZValue(canvas.last_z_value) 967 968 canvas.last_z_value += 1 969 connection_dict.widget.setZValue(canvas.last_z_value) 970 971 canvas.connection_list.append(connection_dict) 972 973 if options.eyecandy == EYECANDY_FULL: 974 item = connection_dict.widget 975 CanvasItemFX(item, True, False) 976 return 977 978 QTimer.singleShot(0, canvas.scene.update) 979 980def disconnectPorts(connection_id): 981 if canvas.debug: 982 print("PatchCanvas::disconnectPorts(%i)" % connection_id) 983 984 line = None 985 item1 = None 986 item2 = None 987 group1id = port1id = 0 988 group2id = port2id = 0 989 990 for connection in canvas.connection_list: 991 if connection.connection_id == connection_id: 992 group1id = connection.group_out_id 993 group2id = connection.group_in_id 994 port1id = connection.port_out_id 995 port2id = connection.port_in_id 996 line = connection.widget 997 canvas.connection_list.remove(connection) 998 break 999 1000 if not line: 1001 qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id) 1002 return 1003 1004 for port in canvas.port_list: 1005 if port.group_id == group1id and port.port_id == port1id: 1006 item1 = port.widget 1007 break 1008 1009 if not item1: 1010 qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id) 1011 return 1012 1013 for port in canvas.port_list: 1014 if port.group_id == group2id and port.port_id == port2id: 1015 item2 = port.widget 1016 break 1017 1018 if not item2: 1019 qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id) 1020 return 1021 1022 item1p = item1.parentItem() 1023 item2p = item2.parentItem() 1024 if item1p: 1025 item1p.removeLineFromGroup(connection_id) 1026 if item2p: 1027 item2p.removeLineFromGroup(connection_id) 1028 1029 if options.eyecandy == EYECANDY_FULL: 1030 CanvasItemFX(line, False, True) 1031 return 1032 1033 canvas.scene.removeItem(line) 1034 del line 1035 1036 QTimer.singleShot(0, canvas.scene.update) 1037 1038# ------------------------------------------------------------------------------------------------------------ 1039 1040def arrange(): 1041 if canvas.debug: 1042 print("PatchCanvas::arrange()") 1043 1044# ------------------------------------------------------------------------------------------------------------ 1045 1046def updateZValues(): 1047 if canvas.debug: 1048 print("PatchCanvas::updateZValues()") 1049 1050 for group in canvas.group_list: 1051 group.widgets[0].resetLinesZValue() 1052 1053 if group.split and group.widgets[1]: 1054 group.widgets[1].resetLinesZValue() 1055 1056# ------------------------------------------------------------------------------------------------------------ 1057 1058def redrawPluginGroup(plugin_id): 1059 group = canvas.group_plugin_map.get(plugin_id, None) 1060 1061 if group is None: 1062 #qCritical("PatchCanvas::redrawPluginGroup(%i) - unable to find group" % plugin_id) 1063 return 1064 1065 group.widgets[0].redrawInlineDisplay() 1066 1067 if group.split and group.widgets[1]: 1068 group.widgets[1].redrawInlineDisplay() 1069 1070def handlePluginRemoved(plugin_id): 1071 if canvas.debug: 1072 print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id) 1073 1074 canvas.scene.clearSelection() 1075 1076 group = canvas.group_plugin_map.pop(plugin_id, None) 1077 1078 if group is not None: 1079 group.plugin_id = -1 1080 group.plugin_ui = False 1081 group.plugin_inline = False 1082 group.widgets[0].removeAsPlugin() 1083 1084 if group.split and group.widgets[1]: 1085 group.widgets[1].removeAsPlugin() 1086 1087 for group in canvas.group_list: 1088 if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED: 1089 continue 1090 1091 group.plugin_id -= 1 1092 group.widgets[0].m_plugin_id -= 1 1093 1094 if group.split and group.widgets[1]: 1095 group.widgets[1].m_plugin_id -= 1 1096 1097 canvas.group_plugin_map[plugin_id] = group 1098 1099def handleAllPluginsRemoved(): 1100 if canvas.debug: 1101 print("PatchCanvas::handleAllPluginsRemoved()") 1102 1103 canvas.group_plugin_map = {} 1104 1105 for group in canvas.group_list: 1106 if group.plugin_id < 0: 1107 continue 1108 if group.plugin_id > MAX_PLUGIN_ID_ALLOWED: 1109 continue 1110 1111 group.plugin_id = -1 1112 group.plugin_ui = False 1113 group.plugin_inline = False 1114 group.widgets[0].removeAsPlugin() 1115 1116 if group.split and group.widgets[1]: 1117 group.widgets[1].removeAsPlugin() 1118 1119# ------------------------------------------------------------------------------------------------------------ 1120