#!/usr/bin/env python3 # -*- coding: utf-8 -*- # PatchBay Canvas engine using QGraphicsView/Scene # Copyright (C) 2010-2018 Filipe Coelho # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # For a full copy of the GNU General Public License see the GPL.txt file # Imports (Global) if True: from PyQt5.QtCore import pyqtSignal, pyqtSlot, qDebug, qCritical, qFatal, qWarning, Qt, QObject from PyQt5.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer from PyQt5.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath from PyQt5.QtGui import QCursor, QFont, QFontMetrics from PyQt5.QtWidgets import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem from PyQt5.QtWidgets import QGraphicsColorizeEffect, QGraphicsDropShadowEffect from PyQt5.QtWidgets import QInputDialog, QLineEdit, QMenu from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer else: from PyQt4.QtCore import pyqtSignal, pyqtSlot, qDebug, qCritical, qFatal, qWarning, Qt, QObject from PyQt4.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer from PyQt4.QtGui import QColor, QLinearGradient, QPen, QPolygonF, QPainter, QPainterPath from PyQt4.QtGui import QCursor, QFont, QFontMetrics from PyQt4.QtGui import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem from PyQt4.QtGui import QGraphicsColorizeEffect, QGraphicsDropShadowEffect from PyQt4.QtGui import QInputDialog, QLineEdit, QMenu from PyQt4.QtSvg import QGraphicsSvgItem, QSvgRenderer # Imports (Theme) from patchcanvas_theme import * # ------------------------------------------------------------------------------ # patchcanvas-api.h # Port Mode PORT_MODE_NULL = 0 PORT_MODE_INPUT = 1 PORT_MODE_OUTPUT = 2 # Port Type PORT_TYPE_NULL = 0 PORT_TYPE_AUDIO_JACK = 1 PORT_TYPE_MIDI_JACK = 2 PORT_TYPE_MIDI_A2J = 3 PORT_TYPE_MIDI_ALSA = 4 # Callback Action ACTION_GROUP_INFO = 0 # group_id, N, N ACTION_GROUP_RENAME = 1 # group_id, N, new_name ACTION_GROUP_SPLIT = 2 # group_id, N, N ACTION_GROUP_JOIN = 3 # group_id, N, N ACTION_PORT_INFO = 4 # port_id, N, N ACTION_PORT_RENAME = 5 # port_id, N, new_name ACTION_PORTS_CONNECT = 6 # out_id, in_id, N ACTION_PORTS_DISCONNECT = 7 # conn_id, N, N # Icon ICON_APPLICATION = 0 ICON_HARDWARE = 1 ICON_DISTRHO = 2 ICON_FILE = 3 ICON_PLUGIN = 4 ICON_LADISH_ROOM = 5 # Split Option SPLIT_UNDEF = 0 SPLIT_NO = 1 SPLIT_YES = 2 # Antialiasing Option ANTIALIASING_NONE = 0 ANTIALIASING_SMALL = 1 ANTIALIASING_FULL = 2 # Eye-Candy Option EYECANDY_NONE = 0 EYECANDY_SMALL = 1 EYECANDY_FULL = 2 # Canvas options class options_t(object): __slots__ = [ 'theme_name', 'auto_hide_groups', 'use_bezier_lines', 'antialiasing', 'eyecandy' ] # Canvas features class features_t(object): __slots__ = [ 'group_info', 'group_rename', 'port_info', 'port_rename', 'handle_group_pos' ] # ------------------------------------------------------------------------------ # patchcanvas.h # object types CanvasBoxType = QGraphicsItem.UserType + 1 CanvasIconType = QGraphicsItem.UserType + 2 CanvasPortType = QGraphicsItem.UserType + 3 CanvasLineType = QGraphicsItem.UserType + 4 CanvasBezierLineType = QGraphicsItem.UserType + 5 CanvasLineMovType = QGraphicsItem.UserType + 6 CanvasBezierLineMovType = QGraphicsItem.UserType + 7 # object lists class group_dict_t(object): __slots__ = [ 'group_id', 'group_name', 'split', 'icon', 'widgets' ] class port_dict_t(object): __slots__ = [ 'group_id', 'port_id', 'port_name', 'port_mode', 'port_type', 'widget' ] class connection_dict_t(object): __slots__ = [ 'connection_id', 'port_in_id', 'port_out_id', 'widget' ] class animation_dict_t(object): __slots__ = [ 'animation', 'item' ] # Main Canvas object class Canvas(object): __slots__ = [ 'scene', 'callback', 'debug', 'last_z_value', 'last_connection_id', 'initial_pos', 'size_rect', 'group_list', 'port_list', 'connection_list', 'animation_list', 'qobject', 'settings', 'theme', 'initiated' ] # ------------------------------------------------------------------------------ # patchcanvas.cpp class CanvasObject(QObject): def __init__(self, parent=None): QObject.__init__(self, parent) @pyqtSlot() def AnimationIdle(self): animation = self.sender() if animation: CanvasRemoveAnimation(animation) @pyqtSlot() def AnimationHide(self): animation = self.sender() if animation: item = animation.item() if item: item.hide() CanvasRemoveAnimation(animation) @pyqtSlot() def AnimationDestroy(self): animation = self.sender() if animation: item = animation.item() if item: CanvasRemoveItemFX(item) CanvasRemoveAnimation(animation) @pyqtSlot() def PortContextMenuDisconnect(self): try: connectionId = int(self.sender().data()) except: return CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "") # Global objects canvas = Canvas() canvas.qobject = None canvas.settings = None canvas.theme = None canvas.initiated = False canvas.group_list = [] canvas.port_list = [] canvas.connection_list = [] canvas.animation_list = [] options = options_t() options.theme_name = getDefaultThemeName() options.auto_hide_groups = False options.use_bezier_lines = True options.antialiasing = ANTIALIASING_SMALL options.eyecandy = EYECANDY_SMALL features = features_t() features.group_info = False features.group_rename = False features.port_info = False features.port_rename = False features.handle_group_pos = False # Internal functions def bool2str(check): return "True" if check else "False" def port_mode2str(port_mode): if port_mode == PORT_MODE_NULL: return "PORT_MODE_NULL" elif port_mode == PORT_MODE_INPUT: return "PORT_MODE_INPUT" elif port_mode == PORT_MODE_OUTPUT: return "PORT_MODE_OUTPUT" else: return "PORT_MODE_???" def port_type2str(port_type): if port_type == PORT_TYPE_NULL: return "PORT_TYPE_NULL" elif port_type == PORT_TYPE_AUDIO_JACK: return "PORT_TYPE_AUDIO_JACK" elif port_type == PORT_TYPE_MIDI_JACK: return "PORT_TYPE_MIDI_JACK" elif port_type == PORT_TYPE_MIDI_A2J: return "PORT_TYPE_MIDI_A2J" elif port_type == PORT_TYPE_MIDI_ALSA: return "PORT_TYPE_MIDI_ALSA" else: return "PORT_TYPE_???" def icon2str(icon): if icon == ICON_HARDWARE: return "ICON_HARDWARE" elif ICON_APPLICATION: return "ICON_APPLICATION" elif ICON_LADISH_ROOM: return "ICON_LADISH_ROOM" else: return "ICON_???" def split2str(split): if split == SPLIT_UNDEF: return "SPLIT_UNDEF" elif split == SPLIT_NO: return "SPLIT_NO" elif split == SPLIT_YES: return "SPLIT_YES" else: return "SPLIT_???" # PatchCanvas API def setOptions(new_options): if canvas.initiated: return options.theme_name = new_options.theme_name options.auto_hide_groups = new_options.auto_hide_groups options.use_bezier_lines = new_options.use_bezier_lines options.antialiasing = new_options.antialiasing options.eyecandy = new_options.eyecandy def setFeatures(new_features): if canvas.initiated: return features.group_info = new_features.group_info features.group_rename = new_features.group_rename features.port_info = new_features.port_info features.port_rename = new_features.port_rename features.handle_group_pos = new_features.handle_group_pos def init(appName, scene, callback, debug=False): if debug: qDebug("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug))) if canvas.initiated: qCritical("PatchCanvas::init() - already initiated") return if not callback: qFatal("PatchCanvas::init() - fatal error: callback not set") return canvas.scene = scene canvas.callback = callback canvas.debug = debug canvas.last_z_value = 0 canvas.last_connection_id = 0 canvas.initial_pos = QPointF(0, 0) canvas.size_rect = QRectF() if not canvas.qobject: canvas.qobject = CanvasObject() if not canvas.settings: canvas.settings = QSettings("falkTX", appName) if canvas.theme: del canvas.theme canvas.theme = None for i in range(Theme.THEME_MAX): this_theme_name = getThemeName(i) if this_theme_name == options.theme_name: canvas.theme = Theme(i) break if not canvas.theme: canvas.theme = Theme(getDefaultTheme()) canvas.scene.updateTheme() canvas.initiated = True def clear(): if canvas.debug: qDebug("PatchCanvas::clear()") group_list_ids = [] port_list_ids = [] connection_list_ids = [] for group in canvas.group_list: group_list_ids.append(group.group_id) for port in canvas.port_list: port_list_ids.append(port.port_id) for connection in canvas.connection_list: connection_list_ids.append(connection.connection_id) for idx in connection_list_ids: disconnectPorts(idx) for idx in port_list_ids: removePort(idx) for idx in group_list_ids: removeGroup(idx) canvas.last_z_value = 0 canvas.last_connection_id = 0 canvas.group_list = [] canvas.port_list = [] canvas.connection_list = [] canvas.scene.clear() canvas.initiated = False def setInitialPos(x, y): if canvas.debug: qDebug("PatchCanvas::setInitialPos(%i, %i)" % (x, y)) canvas.initial_pos.setX(x) canvas.initial_pos.setY(y) def setCanvasSize(x, y, width, height): if canvas.debug: qDebug("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height)) canvas.size_rect.setX(x) canvas.size_rect.setY(y) canvas.size_rect.setWidth(width) canvas.size_rect.setHeight(height) def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): if canvas.debug: qDebug("PatchCanvas::addGroup(%i, %s, %s, %s)" % (group_id, group_name.encode(), split2str(split), icon2str(icon))) for group in canvas.group_list: if group.group_id == group_id: qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (group_id, group_name.encode(), split2str(split), icon2str(icon))) return if split == SPLIT_UNDEF and features.handle_group_pos: split = canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, split, type=int) group_box = CanvasBox(group_id, group_name, icon) group_dict = group_dict_t() group_dict.group_id = group_id group_dict.group_name = group_name group_dict.split = bool(split == SPLIT_YES) group_dict.icon = icon group_dict.widgets = [group_box, None] if split == SPLIT_YES: group_box.setSplit(True, PORT_MODE_OUTPUT) if features.handle_group_pos: group_box.setPos(canvas.settings.value("CanvasPositions/%s_OUTPUT" % group_name, CanvasGetNewGroupPos(), type=QPointF)) else: group_box.setPos(CanvasGetNewGroupPos()) group_sbox = CanvasBox(group_id, group_name, icon) group_sbox.setSplit(True, PORT_MODE_INPUT) group_dict.widgets[1] = group_sbox if features.handle_group_pos: group_sbox.setPos(canvas.settings.value("CanvasPositions/%s_INPUT" % group_name, CanvasGetNewGroupPos(True), type=QPointF)) else: group_sbox.setPos(CanvasGetNewGroupPos(True)) canvas.last_z_value += 1 group_sbox.setZValue(canvas.last_z_value) if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups: CanvasItemFX(group_sbox, True) group_sbox.checkItemPos() else: group_box.setSplit(False) if features.handle_group_pos: group_box.setPos(canvas.settings.value("CanvasPositions/%s" % group_name, CanvasGetNewGroupPos(), type=QPointF)) else: # Special ladish fake-split groups horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM) group_box.setPos(CanvasGetNewGroupPos(horizontal)) group_box.checkItemPos() canvas.last_z_value += 1 group_box.setZValue(canvas.last_z_value) canvas.group_list.append(group_dict) if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups: CanvasItemFX(group_box, True) QTimer.singleShot(0, canvas.scene.update) def removeGroup(group_id): if canvas.debug: qDebug("PatchCanvas::removeGroup(%i)" % group_id) for group in canvas.group_list: if group.group_id == group_id: item = group.widgets[0] group_name = group.group_name if group.split: s_item = group.widgets[1] if features.handle_group_pos: canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos()) canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos()) canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES) if options.eyecandy == EYECANDY_FULL: CanvasItemFX(s_item, False, True) else: s_item.removeIconFromScene() canvas.scene.removeItem(s_item) del s_item else: if features.handle_group_pos: canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos()) canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO) if options.eyecandy == EYECANDY_FULL: CanvasItemFX(item, False, True) else: item.removeIconFromScene() canvas.scene.removeItem(item) del item canvas.group_list.remove(group) QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id) def renameGroup(group_id, new_group_name): if canvas.debug: qDebug("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode())) for group in canvas.group_list: if group.group_id == group_id: group.group_name = new_group_name group.widgets[0].setGroupName(new_group_name) if group.split and group.widgets[1]: group.widgets[1].setGroupName(new_group_name) QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode())) def splitGroup(group_id): if canvas.debug: qDebug("PatchCanvas::splitGroup(%i)" % group_id) item = None group_name = "" group_icon = ICON_APPLICATION ports_data = [] conns_data = [] # Step 1 - Store all Item data for group in canvas.group_list: if group.group_id == group_id: if group.split: qCritical("PatchCanvas::splitGroup(%i) - group is already splitted" % group_id) return item = group.widgets[0] group_name = group.group_name group_icon = group.icon break if not item: qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id) return port_list_ids = list(item.getPortList()) for port in canvas.port_list: if port.port_id in port_list_ids: port_dict = port_dict_t() port_dict.group_id = port.group_id port_dict.port_id = port.port_id port_dict.port_name = port.port_name port_dict.port_mode = port.port_mode port_dict.port_type = port.port_type port_dict.widget = None ports_data.append(port_dict) for connection in canvas.connection_list: if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids: connection_dict = connection_dict_t() connection_dict.connection_id = connection.connection_id connection_dict.port_in_id = connection.port_in_id connection_dict.port_out_id = connection.port_out_id connection_dict.widget = None conns_data.append(connection_dict) # Step 2 - Remove Item and Children for conn in conns_data: disconnectPorts(conn.connection_id) for port_id in port_list_ids: removePort(port_id) removeGroup(group_id) # Step 3 - Re-create Item, now splitted addGroup(group_id, group_name, SPLIT_YES, group_icon) for port in ports_data: addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type) for conn in conns_data: connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id) QTimer.singleShot(0, canvas.scene.update) def joinGroup(group_id): if canvas.debug: qDebug("PatchCanvas::joinGroup(%i)" % group_id) item = None s_item = None group_name = "" group_icon = ICON_APPLICATION ports_data = [] conns_data = [] # Step 1 - Store all Item data for group in canvas.group_list: if group.group_id == group_id: if not group.split: qCritical("PatchCanvas::joinGroup(%i) - group is not splitted" % group_id) return item = group.widgets[0] s_item = group.widgets[1] group_name = group.group_name group_icon = group.icon break # FIXME if not (item and s_item): qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id) return port_list_ids = list(item.getPortList()) port_list_idss = s_item.getPortList() for port_id in port_list_idss: if port_id not in port_list_ids: port_list_ids.append(port_id) for port in canvas.port_list: if port.port_id in port_list_ids: port_dict = port_dict_t() port_dict.group_id = port.group_id port_dict.port_id = port.port_id port_dict.port_name = port.port_name port_dict.port_mode = port.port_mode port_dict.port_type = port.port_type port_dict.widget = None ports_data.append(port_dict) for connection in canvas.connection_list: if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids: connection_dict = connection_dict_t() connection_dict.connection_id = connection.connection_id connection_dict.port_in_id = connection.port_in_id connection_dict.port_out_id = connection.port_out_id connection_dict.widget = None conns_data.append(connection_dict) # Step 2 - Remove Item and Children for conn in conns_data: disconnectPorts(conn.connection_id) for port_id in port_list_ids: removePort(port_id) removeGroup(group_id) # Step 3 - Re-create Item, now together addGroup(group_id, group_name, SPLIT_NO, group_icon) for port in ports_data: addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type) for conn in conns_data: connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id) QTimer.singleShot(0, canvas.scene.update) def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT): if canvas.debug: qDebug("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode))) for group in canvas.group_list: if group.group_id == group_id: if group.split: if port_mode == PORT_MODE_OUTPUT: return group.widgets[0].pos() elif port_mode == PORT_MODE_INPUT: return group.widgets[1].pos() else: return QPointF(0, 0) else: return group.widgets[0].pos() qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode))) return QPointF(0, 0) def setGroupPos(group_id, group_pos_x, group_pos_y): setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y) def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i): if canvas.debug: qDebug("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i)) for group in canvas.group_list: if group.group_id == group_id: group.widgets[0].setPos(group_pos_x_o, group_pos_y_o) if group.split and group.widgets[1]: group.widgets[1].setPos(group_pos_x_i, group_pos_y_i) QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i)) def setGroupIcon(group_id, icon): if canvas.debug: qDebug("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon))) for group in canvas.group_list: if group.group_id == group_id: group.icon = icon group.widgets[0].setIcon(icon) if group.split and group.widgets[1]: group.widgets[1].setIcon(icon) QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon))) def addPort(group_id, port_id, port_name, port_mode, port_type): if canvas.debug: qDebug("PatchCanvas::addPort(%i, %i, %s, %s, %s)" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type))) for port in canvas.port_list: if port.group_id == group_id and port.port_id == port_id: qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type))) return box_widget = None port_widget = None for group in canvas.group_list: if group.group_id == group_id: if group.split and group.widgets[0].getSplittedMode() != port_mode and group.widgets[1]: n = 1 else: n = 0 box_widget = group.widgets[n] port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name) break if not (box_widget and port_widget): qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - Unable to find parent group" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type))) return if options.eyecandy == EYECANDY_FULL: CanvasItemFX(port_widget, True) port_dict = port_dict_t() port_dict.group_id = group_id port_dict.port_id = port_id port_dict.port_name = port_name port_dict.port_mode = port_mode port_dict.port_type = port_type port_dict.widget = port_widget canvas.port_list.append(port_dict) box_widget.updatePositions() QTimer.singleShot(0, canvas.scene.update) def removePort(port_id): if canvas.debug: qDebug("PatchCanvas::removePort(%i)" % port_id) for port in canvas.port_list: if port.port_id == port_id: item = port.widget item.parentItem().removePortFromGroup(port_id) canvas.scene.removeItem(item) del item canvas.port_list.remove(port) QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::removePort(%i) - Unable to find port to remove" % port_id) def renamePort(port_id, new_port_name): if canvas.debug: qDebug("PatchCanvas::renamePort(%i, %s)" % (port_id, new_port_name.encode())) for port in canvas.port_list: if port.port_id == port_id: port.port_name = new_port_name port.widget.setPortName(new_port_name) port.widget.parentItem().updatePositions() QTimer.singleShot(0, canvas.scene.update) return qCritical("PatchCanvas::renamePort(%i, %s) - Unable to find port to rename" % (port_id, new_port_name.encode())) def connectPorts(connection_id, port_out_id, port_in_id): if canvas.debug: qDebug("PatchCanvas::connectPorts(%i, %i, %i)" % (connection_id, port_out_id, port_in_id)) port_out = None port_in = None port_out_parent = None port_in_parent = None for port in canvas.port_list: if port.port_id == port_out_id: port_out = port.widget port_out_parent = port_out.parentItem() elif port.port_id == port_in_id: port_in = port.widget port_in_parent = port_in.parentItem() # FIXME if not (port_out and port_in): qCritical("PatchCanvas::connectPorts(%i, %i, %i) - unable to find ports to connect" % (connection_id, port_out_id, port_in_id)) return connection_dict = connection_dict_t() connection_dict.connection_id = connection_id connection_dict.port_out_id = port_out_id connection_dict.port_in_id = port_in_id if options.use_bezier_lines: connection_dict.widget = CanvasBezierLine(port_out, port_in, None) else: connection_dict.widget = CanvasLine(port_out, port_in, None) canvas.scene.addItem(connection_dict.widget) port_out_parent.addLineFromGroup(connection_dict.widget, connection_id) port_in_parent.addLineFromGroup(connection_dict.widget, connection_id) canvas.last_z_value += 1 port_out_parent.setZValue(canvas.last_z_value) port_in_parent.setZValue(canvas.last_z_value) canvas.last_z_value += 1 connection_dict.widget.setZValue(canvas.last_z_value) canvas.connection_list.append(connection_dict) if options.eyecandy == EYECANDY_FULL: item = connection_dict.widget CanvasItemFX(item, True) QTimer.singleShot(0, canvas.scene.update) def disconnectPorts(connection_id): if canvas.debug: qDebug("PatchCanvas::disconnectPorts(%i)" % connection_id) port_1_id = port_2_id = 0 line = None item1 = None item2 = None for connection in canvas.connection_list: if connection.connection_id == connection_id: port_1_id = connection.port_out_id port_2_id = connection.port_in_id line = connection.widget canvas.connection_list.remove(connection) break if not line: qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id) return for port in canvas.port_list: if port.port_id == port_1_id: item1 = port.widget break if not item1: qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id) return for port in canvas.port_list: if port.port_id == port_2_id: item2 = port.widget break if not item2: qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id) return item1.parentItem().removeLineFromGroup(connection_id) item2.parentItem().removeLineFromGroup(connection_id) if options.eyecandy == EYECANDY_FULL: CanvasItemFX(line, False, True) else: line.deleteFromScene() QTimer.singleShot(0, canvas.scene.update) def arrange(): if canvas.debug: qDebug("PatchCanvas::arrange()") def updateZValues(): if canvas.debug: qDebug("PatchCanvas::updateZValues()") for group in canvas.group_list: group.widgets[0].resetLinesZValue() if group.split and group.widgets[1]: group.widgets[1].resetLinesZValue() # Extra Internal functions def CanvasGetGroupName(group_id): if canvas.debug: qDebug("PatchCanvas::CanvasGetGroupName(%i)" % group_id) for group in canvas.group_list: if group.group_id == group_id: return group.group_name qCritical("PatchCanvas::CanvasGetGroupName(%i) - unable to find group" % group_id) return "" def CanvasGetGroupPortCount(group_id): if canvas.debug: qDebug("PatchCanvas::CanvasGetGroupPortCount(%i)" % group_id) port_count = 0 for port in canvas.port_list: if port.group_id == group_id: port_count += 1 return port_count def CanvasGetNewGroupPos(horizontal=False): if canvas.debug: qDebug("PatchCanvas::CanvasGetNewGroupPos(%s)" % bool2str(horizontal)) new_pos = QPointF(canvas.initial_pos.x(), canvas.initial_pos.y()) items = canvas.scene.items() break_loop = False while not break_loop: break_for = False for i in range(len(items)): item = items[i] if item and item.type() == CanvasBoxType: if item.sceneBoundingRect().contains(new_pos): if horizontal: new_pos += QPointF(item.boundingRect().width() + 15, 0) else: new_pos += QPointF(0, item.boundingRect().height() + 15) break_for = True break if i >= len(items) - 1 and not break_for: break_loop = True return new_pos def CanvasGetFullPortName(port_id): if canvas.debug: qDebug("PatchCanvas::CanvasGetFullPortName(%i)" % port_id) for port in canvas.port_list: if port.port_id == port_id: group_id = port.group_id for group in canvas.group_list: if group.group_id == group_id: return group.group_name + ":" + port.port_name break qCritical("PatchCanvas::CanvasGetFullPortName(%i) - unable to find port" % port_id) return "" def CanvasGetPortConnectionList(port_id): if canvas.debug: qDebug("PatchCanvas::CanvasGetPortConnectionList(%i)" % port_id) port_con_list = [] for connection in canvas.connection_list: if connection.port_out_id == port_id or connection.port_in_id == port_id: port_con_list.append(connection.connection_id) return port_con_list def CanvasGetConnectedPort(connection_id, port_id): if canvas.debug: qDebug("PatchCanvas::CanvasGetConnectedPort(%i, %i)" % (connection_id, port_id)) for connection in canvas.connection_list: if connection.connection_id == connection_id: if connection.port_out_id == port_id: return connection.port_in_id else: return connection.port_out_id qCritical("PatchCanvas::CanvasGetConnectedPort(%i, %i) - unable to find connection" % (connection_id, port_id)) return 0 def CanvasRemoveAnimation(f_animation): if canvas.debug: qDebug("PatchCanvas::CanvasRemoveAnimation(%s)" % f_animation) for animation in canvas.animation_list: if animation.animation == f_animation: canvas.animation_list.remove(animation) del animation.animation break def CanvasCallback(action, value1, value2, value_str): if canvas.debug: qDebug("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode())) canvas.callback(action, value1, value2, value_str) def CanvasItemFX(item, show, destroy=False): if canvas.debug: qDebug("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy))) # Check if item already has an animationItemFX for animation in canvas.animation_list: if animation.item == item: animation.animation.stop() canvas.animation_list.remove(animation) del animation.animation break animation = CanvasFadeAnimation(item, show) animation.setDuration(750 if show else 500) animation_dict = animation_dict_t() animation_dict.animation = animation animation_dict.item = item canvas.animation_list.append(animation_dict) if show: animation.finished.connect(canvas.qobject.AnimationIdle) else: if destroy: animation.finished.connect(canvas.qobject.AnimationDestroy) else: animation.finished.connect(canvas.qobject.AnimationHide) animation.start() def CanvasRemoveItemFX(item): if canvas.debug: qDebug("PatchCanvas::CanvasRemoveItemFX(%s)" % item) if item.type() == CanvasBoxType: item.removeIconFromScene() canvas.scene.removeItem(item) elif item.type() == CanvasPortType: canvas.scene.removeItem(item) elif item.type() in (CanvasLineType, CanvasBezierLineType): pass #item.deleteFromScene() # Force deletion of item if needed if item.type() in (CanvasBoxType, CanvasPortType): del item # ------------------------------------------------------------------------------ # patchscene.cpp class PatchScene(QGraphicsScene): scaleChanged = pyqtSignal(float) sceneGroupMoved = pyqtSignal(int, int, QPointF) def __init__(self, parent, view): QGraphicsScene.__init__(self, parent) self.m_ctrl_down = False self.m_mouse_down_init = False self.m_mouse_rubberband = False self.addRubberBand() self.m_view = view if not self.m_view: qFatal("PatchCanvas::PatchScene() - invalid view") def addRubberBand(self): self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0)) self.m_rubberband.setZValue(-1) self.m_rubberband.hide() self.m_rubberband_selection = False self.m_rubberband_orig_point = QPointF(0, 0) def clear(self): QGraphicsScene.clear(self) # Re-add rubberband, that just got deleted self.addRubberBand() def fixScaleFactor(self): scale = self.m_view.transform().m11() if scale > 3.0: self.m_view.resetTransform() self.m_view.scale(3.0, 3.0) elif scale < 0.2: self.m_view.resetTransform() self.m_view.scale(0.2, 0.2) self.scaleChanged.emit(self.m_view.transform().m11()) def updateTheme(self): self.setBackgroundBrush(canvas.theme.canvas_bg) self.m_rubberband.setPen(canvas.theme.rubberband_pen) self.m_rubberband.setBrush(canvas.theme.rubberband_brush) def zoom_fit(self): min_x = min_y = max_x = max_y = None first_value = True items_list = self.items() if len(items_list) > 0: for item in items_list: if item and item.isVisible() and item.type() == CanvasBoxType: pos = item.scenePos() rect = item.boundingRect() if first_value: min_x = pos.x() elif pos.x() < min_x: min_x = pos.x() if first_value: min_y = pos.y() elif pos.y() < min_y: min_y = pos.y() if first_value: max_x = pos.x() + rect.width() elif pos.x() + rect.width() > max_x: max_x = pos.x() + rect.width() if first_value: max_y = pos.y() + rect.height() elif pos.y() + rect.height() > max_y: max_y = pos.y() + rect.height() first_value = False if not first_value: self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio) self.fixScaleFactor() def zoom_in(self): if self.m_view.transform().m11() < 3.0: self.m_view.scale(1.2, 1.2) self.scaleChanged.emit(self.m_view.transform().m11()) def zoom_out(self): if self.m_view.transform().m11() > 0.2: self.m_view.scale(0.8, 0.8) self.scaleChanged.emit(self.m_view.transform().m11()) def zoom_reset(self): self.m_view.resetTransform() self.scaleChanged.emit(1.0) def keyPressEvent(self, event): if not self.m_view: return event.ignore() if event.key() == Qt.Key_Control: self.m_ctrl_down = True elif event.key() == Qt.Key_Home: self.zoom_fit() return event.accept() elif self.m_ctrl_down: if event.key() == Qt.Key_Plus: self.zoom_in() return event.accept() elif event.key() == Qt.Key_Minus: self.zoom_out() return event.accept() elif event.key() == Qt.Key_1: self.zoom_reset() return event.accept() QGraphicsScene.keyPressEvent(self, event) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.m_ctrl_down = False QGraphicsScene.keyReleaseEvent(self, event) def mousePressEvent(self, event): self.m_mouse_down_init = bool(event.button() == Qt.LeftButton) self.m_mouse_rubberband = False QGraphicsScene.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.m_mouse_down_init: self.m_mouse_down_init = False self.m_mouse_rubberband = bool(len(self.selectedItems()) == 0) if self.m_mouse_rubberband: if not self.m_rubberband_selection: self.m_rubberband.show() self.m_rubberband_selection = True self.m_rubberband_orig_point = event.scenePos() pos = event.scenePos() if pos.x() > self.m_rubberband_orig_point.x(): x = self.m_rubberband_orig_point.x() else: x = pos.x() if pos.y() > self.m_rubberband_orig_point.y(): y = self.m_rubberband_orig_point.y() else: y = pos.y() self.m_rubberband.setRect(x, y, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y())) return event.accept() QGraphicsScene.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self.m_rubberband_selection: items_list = self.items() if len(items_list) > 0: for item in items_list: if item and item.isVisible() and item.type() == CanvasBoxType: item_rect = item.sceneBoundingRect() item_top_left = QPointF(item_rect.x(), item_rect.y()) item_bottom_right = QPointF(item_rect.x() + item_rect.width(), item_rect.y() + item_rect.height()) if self.m_rubberband.contains(item_top_left) and self.m_rubberband.contains(item_bottom_right): item.setSelected(True) self.m_rubberband.hide() self.m_rubberband.setRect(0, 0, 0, 0) self.m_rubberband_selection = False else: items_list = self.selectedItems() for item in items_list: if item and item.isVisible() and item.type() == CanvasBoxType: item.checkItemPos() self.sceneGroupMoved.emit(item.getGroupId(), item.getSplittedMode(), item.scenePos()) if len(items_list) > 1: canvas.scene.update() self.m_mouse_down_init = False self.m_mouse_rubberband = False QGraphicsScene.mouseReleaseEvent(self, event) def wheelEvent(self, event): if not self.m_view: return event.ignore() if self.m_ctrl_down: factor = 1.41 ** (event.delta() / 240.0) self.m_view.scale(factor, factor) self.fixScaleFactor() return event.accept() QGraphicsScene.wheelEvent(self, event) # ------------------------------------------------------------------------------ # canvasfadeanimation.cpp class CanvasFadeAnimation(QAbstractAnimation): def __init__(self, item, show, parent=None): QAbstractAnimation.__init__(self, parent) self.m_show = show self.m_duration = 0 self.m_item = item def item(self): return self.m_item def setDuration(self, time): if self.m_item.opacity() == 0 and not self.m_show: self._duration = 0 else: self.m_item.show() self.m_duration = time def duration(self): return self.m_duration def updateCurrentTime(self, time): if self.m_duration == 0: return if self.m_show: value = float(time) / self.m_duration else: value = 1.0 - (float(time) / self.m_duration) self.m_item.setOpacity(value) if self.m_item.type() == CanvasBoxType: self.m_item.setShadowOpacity(value) def updateDirection(self, direction): pass def updateState(self, oldState, newState): pass # ------------------------------------------------------------------------------ # canvasline.cpp class CanvasLine(QGraphicsLineItem): def __init__(self, item1, item2, parent): QGraphicsLineItem.__init__(self, parent) self.item1 = item1 self.item2 = item2 self.m_locked = False self.m_lineSelected = False self.setGraphicsEffect(None) self.updateLinePos() def deleteFromScene(self): canvas.scene.removeItem(self) del self def isLocked(self): return self.m_locked def setLocked(self, yesno): self.m_locked = yesno def isLineSelected(self): return self.m_lineSelected def setLineSelected(self, yesno): if self.m_locked: return if options.eyecandy == EYECANDY_FULL: if yesno: self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject())) else: self.setGraphicsEffect(None) self.m_lineSelected = yesno self.updateLineGradient() def updateLinePos(self): if self.item1.getPortMode() == PORT_MODE_OUTPUT: line = QLineF(self.item1.scenePos().x() + self.item1.getPortWidth() + 12, self.item1.scenePos().y() + float(canvas.theme.port_height)/2, self.item2.scenePos().x(), self.item2.scenePos().y() + float(canvas.theme.port_height)/2) self.setLine(line) self.m_lineSelected = False self.updateLineGradient() def type(self): return CanvasLineType def updateLineGradient(self): pos_top = self.boundingRect().top() pos_bot = self.boundingRect().bottom() if self.item2.scenePos().y() >= self.item1.scenePos().y(): pos1 = 0 pos2 = 1 else: pos1 = 1 pos2 = 0 port_type1 = self.item1.getPortType() port_type2 = self.item2.getPortType() port_gradient = QLinearGradient(0, pos_top, 0, pos_bot) if port_type1 == PORT_TYPE_AUDIO_JACK: port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack) elif port_type1 == PORT_TYPE_MIDI_JACK: port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack) elif port_type1 == PORT_TYPE_MIDI_A2J: port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j) elif port_type1 == PORT_TYPE_MIDI_ALSA: port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) if port_type2 == PORT_TYPE_AUDIO_JACK: port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack) elif port_type2 == PORT_TYPE_MIDI_JACK: port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack) elif port_type2 == PORT_TYPE_MIDI_A2J: port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j) elif port_type2 == PORT_TYPE_MIDI_ALSA: port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) self.setPen(QPen(port_gradient, 2)) def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) QGraphicsLineItem.paint(self, painter, option, widget) painter.restore() # ------------------------------------------------------------------------------ # canvasbezierline.cpp class CanvasBezierLine(QGraphicsPathItem): def __init__(self, item1, item2, parent): QGraphicsPathItem.__init__(self, parent) self.item1 = item1 self.item2 = item2 self.m_locked = False self.m_lineSelected = False self.setBrush(QColor(0, 0, 0, 0)) self.setGraphicsEffect(None) self.updateLinePos() def deleteFromScene(self): canvas.scene.removeItem(self) del self def isLocked(self): return self.m_locked def setLocked(self, yesno): self.m_locked = yesno def isLineSelected(self): return self.m_lineSelected def setLineSelected(self, yesno): if self.m_locked: return if options.eyecandy == EYECANDY_FULL: if yesno: self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject())) else: self.setGraphicsEffect(None) self.m_lineSelected = yesno self.updateLineGradient() def updateLinePos(self): if self.item1.getPortMode() == PORT_MODE_OUTPUT: item1_x = self.item1.scenePos().x() + self.item1.getPortWidth() + 12 item1_y = self.item1.scenePos().y() + float(canvas.theme.port_height)/2 item2_x = self.item2.scenePos().x() item2_y = self.item2.scenePos().y() + float(canvas.theme.port_height)/2 item1_mid_x = abs(item1_x - item2_x) / 2 item1_new_x = item1_x + item1_mid_x item2_mid_x = abs(item1_x - item2_x) / 2 item2_new_x = item2_x - item2_mid_x path = QPainterPath(QPointF(item1_x, item1_y)) path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y) self.setPath(path) self.m_lineSelected = False self.updateLineGradient() def type(self): return CanvasBezierLineType def updateLineGradient(self): pos_top = self.boundingRect().top() pos_bot = self.boundingRect().bottom() if self.item2.scenePos().y() >= self.item1.scenePos().y(): pos1 = 0 pos2 = 1 else: pos1 = 1 pos2 = 0 port_type1 = self.item1.getPortType() port_type2 = self.item2.getPortType() port_gradient = QLinearGradient(0, pos_top, 0, pos_bot) if port_type1 == PORT_TYPE_AUDIO_JACK: port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack) elif port_type1 == PORT_TYPE_MIDI_JACK: port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack) elif port_type1 == PORT_TYPE_MIDI_A2J: port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j) elif port_type1 == PORT_TYPE_MIDI_ALSA: port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) if port_type2 == PORT_TYPE_AUDIO_JACK: port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack) elif port_type2 == PORT_TYPE_MIDI_JACK: port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack) elif port_type2 == PORT_TYPE_MIDI_A2J: port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j) elif port_type2 == PORT_TYPE_MIDI_ALSA: port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) self.setPen(QPen(port_gradient, 2)) def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) QGraphicsPathItem.paint(self, painter, option, widget) painter.restore() # ------------------------------------------------------------------------------ # canvaslivemov.cpp class CanvasLineMov(QGraphicsLineItem): def __init__(self, port_mode, port_type, parent): QGraphicsLineItem.__init__(self, parent) self.m_port_mode = port_mode self.m_port_type = port_type # Port position doesn't change while moving around line self.p_lineX = self.scenePos().x() self.p_lineY = self.scenePos().y() self.p_width = self.parentItem().getPortWidth() if port_type == PORT_TYPE_AUDIO_JACK: pen = QPen(canvas.theme.line_audio_jack, 2) elif port_type == PORT_TYPE_MIDI_JACK: pen = QPen(canvas.theme.line_midi_jack, 2) elif port_type == PORT_TYPE_MIDI_A2J: pen = QPen(canvas.theme.line_midi_a2j, 2) elif port_type == PORT_TYPE_MIDI_ALSA: pen = QPen(canvas.theme.line_midi_alsa, 2) else: qWarning("PatchCanvas::CanvasLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent)) pen = QPen(Qt.black) self.setPen(pen) def deleteFromScene(self): canvas.scene.removeItem(self) del self def updateLinePos(self, scenePos): item_pos = [0, 0] if self.m_port_mode == PORT_MODE_INPUT: item_pos[0] = 0 item_pos[1] = float(canvas.theme.port_height)/2 elif self.m_port_mode == PORT_MODE_OUTPUT: item_pos[0] = self.p_width + 12 item_pos[1] = float(canvas.theme.port_height)/2 else: return line = QLineF(item_pos[0], item_pos[1], scenePos.x() - self.p_lineX, scenePos.y() - self.p_lineY) self.setLine(line) def type(self): return CanvasLineMovType def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) QGraphicsLineItem.paint(self, painter, option, widget) painter.restore() # ------------------------------------------------------------------------------ # canvasbezierlinemov.cpp class CanvasBezierLineMov(QGraphicsPathItem): def __init__(self, port_mode, port_type, parent): QGraphicsPathItem.__init__(self, parent) self.m_port_mode = port_mode self.m_port_type = port_type # Port position doesn't change while moving around line self.p_itemX = self.scenePos().x() self.p_itemY = self.scenePos().y() self.p_width = self.parentItem().getPortWidth() if port_type == PORT_TYPE_AUDIO_JACK: pen = QPen(canvas.theme.line_audio_jack, 2) elif port_type == PORT_TYPE_MIDI_JACK: pen = QPen(canvas.theme.line_midi_jack, 2) elif port_type == PORT_TYPE_MIDI_A2J: pen = QPen(canvas.theme.line_midi_a2j, 2) elif port_type == PORT_TYPE_MIDI_ALSA: pen = QPen(canvas.theme.line_midi_alsa, 2) else: qWarning("PatchCanvas::CanvasBezierLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent)) pen = QPen(Qt.black) self.setBrush(QColor(0, 0, 0, 0)) self.setPen(pen) def deleteFromScene(self): canvas.scene.removeItem(self) del self def updateLinePos(self, scenePos): if self.m_port_mode == PORT_MODE_INPUT: old_x = 0 old_y = float(canvas.theme.port_height)/2 mid_x = abs(scenePos.x() - self.p_itemX) / 2 new_x = old_x - mid_x elif self.m_port_mode == PORT_MODE_OUTPUT: old_x = self.p_width + 12 old_y = float(canvas.theme.port_height)/2 mid_x = abs(scenePos.x() - (self.p_itemX + old_x)) / 2 new_x = old_x + mid_x else: return final_x = scenePos.x() - self.p_itemX final_y = scenePos.y() - self.p_itemY path = QPainterPath(QPointF(old_x, old_y)) path.cubicTo(new_x, old_y, new_x, final_y, final_x, final_y) self.setPath(path) def type(self): return CanvasBezierLineMovType def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) QGraphicsPathItem.paint(self, painter, option, widget) painter.restore() # ------------------------------------------------------------------------------ # canvasport.cpp class CanvasPort(QGraphicsItem): def __init__(self, port_id, port_name, port_mode, port_type, parent): QGraphicsItem.__init__(self, parent) # Save Variables, useful for later self.m_port_id = port_id self.m_port_mode = port_mode self.m_port_type = port_type self.m_port_name = port_name # Base Variables self.m_port_width = 15 self.m_port_height = canvas.theme.port_height self.m_port_font = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, canvas.theme.port_font_state) self.m_line_mov = None self.m_hover_item = None self.m_last_selected_state = False self.m_mouse_down = False self.m_cursor_moving = False self.setFlags(QGraphicsItem.ItemIsSelectable) def getPortId(self): return self.m_port_id def getPortMode(self): return self.m_port_mode def getPortType(self): return self.m_port_type def getPortName(self): return self.m_port_name def getFullPortName(self): return self.parentItem().getGroupName() + ":" + self.m_port_name def getPortWidth(self): return self.m_port_width def getPortHeight(self): return self.m_port_height def setPortMode(self, port_mode): self.m_port_mode = port_mode self.update() def setPortType(self, port_type): self.m_port_type = port_type self.update() def setPortName(self, port_name): if QFontMetrics(self.m_port_font).width(port_name) < QFontMetrics(self.m_port_font).width(self.m_port_name): QTimer.singleShot(0, canvas.scene.update) self.m_port_name = port_name self.update() def setPortWidth(self, port_width): if port_width < self.m_port_width: QTimer.singleShot(0, canvas.scene.update) self.m_port_width = port_width self.update() def type(self): return CanvasPortType def mousePressEvent(self, event): self.m_hover_item = None self.m_mouse_down = bool(event.button() == Qt.LeftButton) self.m_cursor_moving = False QGraphicsItem.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.m_mouse_down: if not self.m_cursor_moving: self.setCursor(QCursor(Qt.CrossCursor)) self.m_cursor_moving = True for connection in canvas.connection_list: if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id: connection.widget.setLocked(True) if not self.m_line_mov: if options.use_bezier_lines: self.m_line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, self) else: self.m_line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, self) canvas.last_z_value += 1 self.m_line_mov.setZValue(canvas.last_z_value) canvas.last_z_value += 1 self.parentItem().setZValue(canvas.last_z_value) item = None items = canvas.scene.items(event.scenePos(), Qt.ContainsItemShape, Qt.AscendingOrder) for i in range(len(items)): if items[i].type() == CanvasPortType: if items[i] != self: if not item: item = items[i] elif items[i].parentItem().zValue() > item.parentItem().zValue(): item = items[i] if self.m_hover_item and self.m_hover_item != item: self.m_hover_item.setSelected(False) if item: a2j_connection = bool(item.getPortType() == PORT_TYPE_MIDI_JACK and self.m_port_type == PORT_TYPE_MIDI_A2J) or (item.getPortType() == PORT_TYPE_MIDI_A2J and self.m_port_type == PORT_TYPE_MIDI_JACK) if item.getPortMode() != self.m_port_mode and (item.getPortType() == self.m_port_type or a2j_connection): item.setSelected(True) self.m_hover_item = item else: self.m_hover_item = None else: self.m_hover_item = None self.m_line_mov.updateLinePos(event.scenePos()) return event.accept() QGraphicsItem.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self.m_mouse_down: if self.m_line_mov: self.m_line_mov.deleteFromScene() self.m_line_mov = None for connection in canvas.connection_list: if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id: connection.widget.setLocked(False) if self.m_hover_item: check = False for connection in canvas.connection_list: if ( (connection.port_out_id == self.m_port_id and connection.port_in_id == self.m_hover_item.getPortId()) or (connection.port_out_id == self.m_hover_item.getPortId() and connection.port_in_id == self.m_port_id) ): canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") check = True break if not check: if self.m_port_mode == PORT_MODE_OUTPUT: canvas.callback(ACTION_PORTS_CONNECT, self.m_port_id, self.m_hover_item.getPortId(), "") else: canvas.callback(ACTION_PORTS_CONNECT, self.m_hover_item.getPortId(), self.m_port_id, "") canvas.scene.clearSelection() if self.m_cursor_moving: self.setCursor(QCursor(Qt.ArrowCursor)) self.m_hover_item = None self.m_mouse_down = False self.m_cursor_moving = False QGraphicsItem.mouseReleaseEvent(self, event) def contextMenuEvent(self, event): canvas.scene.clearSelection() self.setSelected(True) menu = QMenu() discMenu = QMenu("Disconnect", menu) port_con_list = CanvasGetPortConnectionList(self.m_port_id) if len(port_con_list) > 0: for port_id in port_con_list: port_con_id = CanvasGetConnectedPort(port_id, self.m_port_id) act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id)) act_x_disc.setData(port_id) act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect) else: act_x_disc = discMenu.addAction("No connections") act_x_disc.setEnabled(False) menu.addMenu(discMenu) act_x_disc_all = menu.addAction("Disconnect &All") act_x_sep_1 = menu.addSeparator() act_x_info = menu.addAction("Get &Info") act_x_rename = menu.addAction("&Rename") if not features.port_info: act_x_info.setVisible(False) if not features.port_rename: act_x_rename.setVisible(False) if not (features.port_info and features.port_rename): act_x_sep_1.setVisible(False) act_selected = menu.exec_(event.screenPos()) if act_selected == act_x_disc_all: for port_id in port_con_list: canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "") elif act_selected == act_x_info: canvas.callback(ACTION_PORT_INFO, self.m_port_id, 0, "") elif act_selected == act_x_rename: new_name_try = QInputDialog.getText(None, "Rename Port", "New name:", QLineEdit.Normal, self.m_port_name) if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text canvas.callback(ACTION_PORT_RENAME, self.m_port_id, 0, new_name_try[0]) event.accept() def boundingRect(self): return QRectF(0, 0, self.m_port_width + 12, self.m_port_height) def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) poly_locx = [0, 0, 0, 0, 0] if self.m_port_mode == PORT_MODE_INPUT: text_pos = QPointF(3, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: poly_locx[0] = 0 poly_locx[1] = self.m_port_width + 5 poly_locx[2] = self.m_port_width + 12 poly_locx[3] = self.m_port_width + 5 poly_locx[4] = 0 elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = 0 poly_locx[1] = self.m_port_width + 5 poly_locx[2] = self.m_port_width + 5 poly_locx[3] = self.m_port_width + 5 poly_locx[4] = 0 else: qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return elif self.m_port_mode == PORT_MODE_OUTPUT: text_pos = QPointF(9, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: poly_locx[0] = self.m_port_width + 12 poly_locx[1] = 7 poly_locx[2] = 0 poly_locx[3] = 7 poly_locx[4] = self.m_port_width + 12 elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = self.m_port_width + 12 poly_locx[1] = 5 poly_locx[2] = 5 poly_locx[3] = 5 poly_locx[4] = self.m_port_width + 12 else: qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return else: qCritical("PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode)) return if self.m_port_type == PORT_TYPE_AUDIO_JACK: poly_color = canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg poly_pen = canvas.theme.port_audio_jack_pen_sel if self.isSelected() else canvas.theme.port_audio_jack_pen text_pen = canvas.theme.port_audio_jack_text_sel if self.isSelected() else canvas.theme.port_audio_jack_text conn_pen = canvas.theme.port_audio_jack_pen_sel elif self.m_port_type == PORT_TYPE_MIDI_JACK: poly_color = canvas.theme.port_midi_jack_bg_sel if self.isSelected() else canvas.theme.port_midi_jack_bg poly_pen = canvas.theme.port_midi_jack_pen_sel if self.isSelected() else canvas.theme.port_midi_jack_pen text_pen = canvas.theme.port_midi_jack_text_sel if self.isSelected() else canvas.theme.port_midi_jack_text conn_pen = canvas.theme.port_midi_jack_pen_sel elif self.m_port_type == PORT_TYPE_MIDI_A2J: poly_color = canvas.theme.port_midi_a2j_bg_sel if self.isSelected() else canvas.theme.port_midi_a2j_bg poly_pen = canvas.theme.port_midi_a2j_pen_sel if self.isSelected() else canvas.theme.port_midi_a2j_pen text_pen = canvas.theme.port_midi_a2j_text_sel if self.isSelected() else canvas.theme.port_midi_a2j_text conn_pen = canvas.theme.port_midi_a2j_pen_sel elif self.m_port_type == PORT_TYPE_MIDI_ALSA: poly_color = canvas.theme.port_midi_alsa_bg_sel if self.isSelected() else canvas.theme.port_midi_alsa_bg poly_pen = canvas.theme.port_midi_alsa_pen_sel if self.isSelected() else canvas.theme.port_midi_alsa_pen text_pen = canvas.theme.port_midi_alsa_text_sel if self.isSelected() else canvas.theme.port_midi_alsa_text conn_pen = canvas.theme.port_midi_alsa_pen_sel else: qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type)) return polygon = QPolygonF() polygon += QPointF(poly_locx[0], 0) polygon += QPointF(poly_locx[1], 0) polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2) polygon += QPointF(poly_locx[3], canvas.theme.port_height) polygon += QPointF(poly_locx[4], canvas.theme.port_height) if canvas.theme.port_bg_pixmap: portRect = polygon.boundingRect() portPos = portRect.topLeft() painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos) else: painter.setBrush(poly_color) painter.setPen(poly_pen) painter.drawPolygon(polygon) painter.setPen(text_pen) painter.setFont(self.m_port_font) painter.drawText(text_pos, self.m_port_name) if self.isSelected() != self.m_last_selected_state: for connection in canvas.connection_list: if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id: connection.widget.setLineSelected(self.isSelected()) if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap: painter.setPen(Qt.NoPen) painter.setBrush(conn_pen.brush()) if self.m_port_mode == PORT_MODE_INPUT: connRect = QRectF(portRect.topLeft(), QSizeF(2, portRect.height())) else: connRect = QRectF(QPointF(portRect.right()-2, portRect.top()), QSizeF(2, portRect.height())) painter.drawRect(connRect) self.m_last_selected_state = self.isSelected() painter.restore() # ------------------------------------------------------------------------------ # canvasbox.cpp class cb_line_t(object): __slots__ = [ 'line', 'connection_id' ] class CanvasBox(QGraphicsItem): def __init__(self, group_id, group_name, icon, parent=None): QGraphicsItem.__init__(self, parent) # Save Variables, useful for later self.m_group_id = group_id self.m_group_name = group_name # Base Variables self.p_width = 50 self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1 self.m_last_pos = QPointF() self.m_splitted = False self.m_splitted_mode = PORT_MODE_NULL self.m_cursor_moving = False self.m_forced_split = False self.m_mouse_down = False self.m_port_list_ids = [] self.m_connection_lines = [] # Set Font self.m_font_name = QFont(canvas.theme.box_font_name, canvas.theme.box_font_size, canvas.theme.box_font_state) self.m_font_port = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, canvas.theme.port_font_state) # Icon if canvas.theme.box_use_icon: self.icon_svg = CanvasIcon(icon, self.m_group_name, self) else: self.icon_svg = None # Shadow if options.eyecandy: self.shadow = CanvasBoxShadow(self.toGraphicsObject()) self.shadow.setFakeParent(self) self.setGraphicsEffect(self.shadow) else: self.shadow = None # Final touches self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable) # Wait for at least 1 port if options.auto_hide_groups: self.setVisible(False) self.updatePositions() canvas.scene.addItem(self) def getGroupId(self): return self.m_group_id def getGroupName(self): return self.m_group_name def isSplitted(self): return self.m_splitted def getSplittedMode(self): return self.m_splitted_mode def getPortCount(self): return len(self.m_port_list_ids) def getPortList(self): return self.m_port_list_ids def setIcon(self, icon): if self.icon_svg: self.icon_svg.setIcon(icon, self.m_group_name) def setSplit(self, split, mode=PORT_MODE_NULL): self.m_splitted = split self.m_splitted_mode = mode def setGroupName(self, group_name): self.m_group_name = group_name self.updatePositions() def setShadowOpacity(self, opacity): if self.shadow: self.shadow.setOpacity(opacity) def addPortFromGroup(self, port_id, port_mode, port_type, port_name): if len(self.m_port_list_ids) == 0: if options.auto_hide_groups: if options.eyecandy == EYECANDY_FULL: CanvasItemFX(self, True) self.setVisible(True) new_widget = CanvasPort(port_id, port_name, port_mode, port_type, self) port_dict = port_dict_t() port_dict.group_id = self.m_group_id port_dict.port_id = port_id port_dict.port_name = port_name port_dict.port_mode = port_mode port_dict.port_type = port_type port_dict.widget = new_widget self.m_port_list_ids.append(port_id) return new_widget def removePortFromGroup(self, port_id): if port_id in self.m_port_list_ids: self.m_port_list_ids.remove(port_id) else: qCritical("PatchCanvas::CanvasBox.removePort(%i) - unable to find port to remove" % port_id) return if len(self.m_port_list_ids) > 0: self.updatePositions() elif self.isVisible(): if options.auto_hide_groups: if options.eyecandy == EYECANDY_FULL: CanvasItemFX(self, False) else: self.setVisible(False) def addLineFromGroup(self, line, connection_id): new_cbline = cb_line_t() new_cbline.line = line new_cbline.connection_id = connection_id self.m_connection_lines.append(new_cbline) def removeLineFromGroup(self, connection_id): for connection in self.m_connection_lines: if connection.connection_id == connection_id: self.m_connection_lines.remove(connection) return qCritical("PatchCanvas::CanvasBox.removeLineFromGroup(%i) - unable to find line to remove" % connection_id) def checkItemPos(self): if not canvas.size_rect.isNull(): pos = self.scenePos() if not (canvas.size_rect.contains(pos) and canvas.size_rect.contains(pos + QPointF(self.p_width, self.p_height))): if pos.x() < canvas.size_rect.x(): self.setPos(canvas.size_rect.x(), pos.y()) elif pos.x() + self.p_width > canvas.size_rect.width(): self.setPos(canvas.size_rect.width() - self.p_width, pos.y()) pos = self.scenePos() if pos.y() < canvas.size_rect.y(): self.setPos(pos.x(), canvas.size_rect.y()) elif pos.y() + self.p_height > canvas.size_rect.height(): self.setPos(pos.x(), canvas.size_rect.height() - self.p_height) def removeIconFromScene(self): if self.icon_svg: canvas.scene.removeItem(self.icon_svg) def updatePositions(self): self.prepareGeometryChange() max_in_width = 0 max_in_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing max_out_width = 0 max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing port_spacing = canvas.theme.port_height + canvas.theme.port_spacing have_audio_jack_in = have_midi_jack_in = have_midi_a2j_in = have_midi_alsa_in = False have_audio_jack_out = have_midi_jack_out = have_midi_a2j_out = have_midi_alsa_out = False # reset box size self.p_width = 50 self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1 # Check Text Name size app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30 if app_name_size > self.p_width: self.p_width = app_name_size # Get Port List port_list = [] for port in canvas.port_list: if port.port_id in self.m_port_list_ids: port_list.append(port) # Get Max Box Width/Height for port in port_list: if port.port_mode == PORT_MODE_INPUT: max_in_height += port_spacing size = QFontMetrics(self.m_font_port).width(port.port_name) if size > max_in_width: max_in_width = size if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in: have_audio_jack_in = True max_in_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in: have_midi_jack_in = True max_in_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_A2J and not have_midi_a2j_in: have_midi_a2j_in = True max_in_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in: have_midi_alsa_in = True max_in_height += canvas.theme.port_spacingT elif port.port_mode == PORT_MODE_OUTPUT: max_out_height += port_spacing size = QFontMetrics(self.m_font_port).width(port.port_name) if size > max_out_width: max_out_width = size if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out: have_audio_jack_out = True max_out_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out: have_midi_jack_out = True max_out_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_A2J and not have_midi_a2j_out: have_midi_a2j_out = True max_out_height += canvas.theme.port_spacingT elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out: have_midi_alsa_out = True max_out_height += canvas.theme.port_spacingT if canvas.theme.port_spacingT == 0: max_in_height += 2 max_out_height += 2 final_width = 30 + max_in_width + max_out_width if final_width > self.p_width: self.p_width = final_width if max_in_height > self.p_height: self.p_height = max_in_height if max_out_height > self.p_height: self.p_height = max_out_height # Remove bottom space self.p_height -= canvas.theme.port_spacingT if canvas.theme.box_header_spacing > 0: if len(port_list) == 0: self.p_height -= canvas.theme.box_header_spacing else: self.p_height -= canvas.theme.box_header_spacing/2 last_in_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing last_in_type = PORT_TYPE_NULL last_out_type = PORT_TYPE_NULL # Re-position ports, AUDIO_JACK for port in port_list: if port.port_type == PORT_TYPE_AUDIO_JACK: if port.port_mode == PORT_MODE_INPUT: port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) port.widget.setPortWidth(max_in_width) last_in_pos += port_spacing last_in_type = port.port_type elif port.port_mode == PORT_MODE_OUTPUT: port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) port.widget.setPortWidth(max_out_width) last_out_pos += port_spacing last_out_type = port.port_type # Re-position ports, MIDI_JACK for port in port_list: if port.port_type == PORT_TYPE_MIDI_JACK: if port.port_mode == PORT_MODE_INPUT: if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: last_in_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) port.widget.setPortWidth(max_in_width) last_in_pos += port_spacing last_in_type = port.port_type elif port.port_mode == PORT_MODE_OUTPUT: if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: last_out_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) port.widget.setPortWidth(max_out_width) last_out_pos += port_spacing last_out_type = port.port_type # Re-position ports, MIDI_A2J for port in port_list: if port.port_type == PORT_TYPE_MIDI_A2J: if port.port_mode == PORT_MODE_INPUT: if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: last_in_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) port.widget.setPortWidth(max_in_width) last_in_pos += port_spacing last_in_type = port.port_type elif port.port_mode == PORT_MODE_OUTPUT: if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: last_out_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) port.widget.setPortWidth(max_out_width) last_out_pos += port_spacing last_out_type = port.port_type # Re-position ports, MIDI_ALSA for port in port_list: if port.port_type == PORT_TYPE_MIDI_ALSA: if port.port_mode == PORT_MODE_INPUT: if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type: last_in_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(1 + canvas.theme.port_offset, last_in_pos)) port.widget.setPortWidth(max_in_width) last_in_pos += port_spacing last_in_type = port.port_type elif port.port_mode == PORT_MODE_OUTPUT: if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type: last_out_pos += canvas.theme.port_spacingT port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 13, last_out_pos)) port.widget.setPortWidth(max_out_width) last_out_pos += port_spacing last_out_type = port.port_type self.repaintLines(True) self.update() def repaintLines(self, forced=False): if self.pos() != self.m_last_pos or forced: for connection in self.m_connection_lines: connection.line.updateLinePos() self.m_last_pos = self.pos() def resetLinesZValue(self): for connection in canvas.connection_list: if connection.port_out_id in self.m_port_list_ids and connection.port_in_id in self.m_port_list_ids: z_value = canvas.last_z_value else: z_value = canvas.last_z_value - 1 connection.widget.setZValue(z_value) def type(self): return CanvasBoxType def contextMenuEvent(self, event): menu = QMenu() discMenu = QMenu("Disconnect", menu) port_con_list = [] port_con_list_ids = [] for port_id in self.m_port_list_ids: tmp_port_con_list = CanvasGetPortConnectionList(port_id) for port_con_id in tmp_port_con_list: if port_con_id not in port_con_list: port_con_list.append(port_con_id) port_con_list_ids.append(port_id) if len(port_con_list) > 0: for i in range(len(port_con_list)): port_con_id = CanvasGetConnectedPort(port_con_list[i], port_con_list_ids[i]) act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id)) act_x_disc.setData(port_con_list[i]) act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect) else: act_x_disc = discMenu.addAction("No connections") act_x_disc.setEnabled(False) menu.addMenu(discMenu) act_x_disc_all = menu.addAction("Disconnect &All") act_x_sep1 = menu.addSeparator() act_x_info = menu.addAction("&Info") act_x_rename = menu.addAction("&Rename") act_x_sep2 = menu.addSeparator() act_x_split_join = menu.addAction("Join" if self.m_splitted else "Split") if not features.group_info: act_x_info.setVisible(False) if not features.group_rename: act_x_rename.setVisible(False) if not (features.group_info and features.group_rename): act_x_sep1.setVisible(False) haveIns = haveOuts = False for port in canvas.port_list: if port.port_id in self.m_port_list_ids: if port.port_mode == PORT_MODE_INPUT: haveIns = True elif port.port_mode == PORT_MODE_OUTPUT: haveOuts = True if not (self.m_splitted or bool(haveIns and haveOuts)): act_x_sep2.setVisible(False) act_x_split_join.setVisible(False) act_selected = menu.exec_(event.screenPos()) if act_selected == act_x_disc_all: for port_id in port_con_list: canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "") elif act_selected == act_x_info: canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "") elif act_selected == act_x_rename: new_name_try = QInputDialog.getText(None, "Rename Group", "New name:", QLineEdit.Normal, self.m_group_name) if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 0, new_name_try[0]) elif act_selected == act_x_split_join: if self.m_splitted: canvas.callback(ACTION_GROUP_JOIN, self.m_group_id, 0, "") else: canvas.callback(ACTION_GROUP_SPLIT, self.m_group_id, 0, "") event.accept() def mousePressEvent(self, event): canvas.last_z_value += 1 self.setZValue(canvas.last_z_value) self.resetLinesZValue() self.m_cursor_moving = False if event.button() == Qt.RightButton: canvas.scene.clearSelection() self.setSelected(True) self.m_mouse_down = False return event.accept() elif event.button() == Qt.LeftButton: if self.sceneBoundingRect().contains(event.scenePos()): self.m_mouse_down = True else: # Fix a weird Qt behaviour with right-click mouseMove self.m_mouse_down = False return event.ignore() else: self.m_mouse_down = False QGraphicsItem.mousePressEvent(self, event) def mouseMoveEvent(self, event): if self.m_mouse_down: if not self.m_cursor_moving: self.setCursor(QCursor(Qt.SizeAllCursor)) self.m_cursor_moving = True self.repaintLines() QGraphicsItem.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self.m_cursor_moving: self.setCursor(QCursor(Qt.ArrowCursor)) self.m_mouse_down = False self.m_cursor_moving = False QGraphicsItem.mouseReleaseEvent(self, event) def boundingRect(self): return QRectF(0, 0, self.p_width, self.p_height) def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, False) # Draw rectangle if self.isSelected(): painter.setPen(canvas.theme.box_pen_sel) else: painter.setPen(canvas.theme.box_pen) if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT: box_gradient = QLinearGradient(0, 0, 0, self.p_height) box_gradient.setColorAt(0, canvas.theme.box_bg_1) box_gradient.setColorAt(1, canvas.theme.box_bg_2) painter.setBrush(box_gradient) else: painter.setBrush(canvas.theme.box_bg_1) painter.drawRect(QRectF(0, 0, self.p_width, self.p_height)) # Draw pixmap header if canvas.theme.box_header_pixmap: painter.setPen(Qt.NoPen) painter.setBrush(canvas.theme.box_bg_2) painter.drawRect(QRectF(1, 1, self.p_width-2, canvas.theme.box_header_height)) headerPos = QPointF(1, 1) headerRect = QRectF(2, 2, int(self.p_width-4), canvas.theme.box_header_height-3) painter.drawTiledPixmap(headerRect, canvas.theme.box_header_pixmap, headerPos) # Draw text painter.setFont(self.m_font_name) if self.isSelected(): painter.setPen(canvas.theme.box_text_sel) else: painter.setPen(canvas.theme.box_text) if canvas.theme.box_use_icon: textPos = QPointF(25, canvas.theme.box_text_ypos) else: appNameSize = QFontMetrics(self.m_font_name).width(self.m_group_name) rem = self.p_width - appNameSize textPos = QPointF(rem/2, canvas.theme.box_text_ypos) painter.drawText(textPos, self.m_group_name) self.repaintLines() painter.restore() # ------------------------------------------------------------------------------ # canvasicon.cpp class CanvasIcon(QGraphicsSvgItem): def __init__(self, icon, name, parent): QGraphicsSvgItem.__init__(self, parent) self.m_renderer = None self.p_size = QRectF(0, 0, 0, 0) self.m_colorFX = QGraphicsColorizeEffect(self) self.m_colorFX.setColor(canvas.theme.box_text.color()) self.setGraphicsEffect(self.m_colorFX) self.setIcon(icon, name) def setIcon(self, icon, name): name = name.lower() icon_path = "" if icon == ICON_APPLICATION: self.p_size = QRectF(3, 2, 19, 18) if "audacious" in name: icon_path = ":/scalable/pb_audacious.svg" self.p_size = QRectF(5, 4, 16, 16) elif "clementine" in name: icon_path = ":/scalable/pb_clementine.svg" self.p_size = QRectF(5, 4, 16, 16) elif "distrho" in name: icon_path = ":/scalable/pb_distrho.svg" self.p_size = QRectF(5, 4, 14, 14) elif "jamin" in name: icon_path = ":/scalable/pb_jamin.svg" self.p_size = QRectF(5, 3, 16, 16) elif "mplayer" in name: icon_path = ":/scalable/pb_mplayer.svg" self.p_size = QRectF(5, 4, 16, 16) elif "vlc" in name: icon_path = ":/scalable/pb_vlc.svg" self.p_size = QRectF(5, 3, 16, 16) else: icon_path = ":/scalable/pb_generic.svg" self.p_size = QRectF(4, 3, 16, 16) elif icon == ICON_HARDWARE: icon_path = ":/scalable/pb_hardware.svg" self.p_size = QRectF(5, 2, 16, 16) elif icon == ICON_DISTRHO: icon_path = ":/scalable/pb_distrho.svg" self.p_size = QRectF(5, 4, 14, 14) elif icon == ICON_FILE: icon_path = ":/scalable/pb_file.svg" self.p_size = QRectF(5, 4, 12, 14) elif icon == ICON_PLUGIN: icon_path = ":/scalable/pb_plugin.svg" self.p_size = QRectF(5, 4, 14, 14) elif icon == ICON_LADISH_ROOM: # TODO - make a unique ladish-room icon icon_path = ":/scalable/pb_hardware.svg" self.p_size = QRectF(5, 2, 16, 16) else: self.p_size = QRectF(0, 0, 0, 0) qCritical("PatchCanvas::CanvasIcon.setIcon(%s, %s) - unsupported icon requested" % (icon2str(icon), name.encode())) return self.m_renderer = QSvgRenderer(icon_path, canvas.scene) self.setSharedRenderer(self.m_renderer) self.update() def type(self): return CanvasIconType def boundingRect(self): return self.p_size def paint(self, painter, option, widget): if self.m_renderer: painter.save() painter.setRenderHint(QPainter.Antialiasing, False) painter.setRenderHint(QPainter.TextAntialiasing, False) self.m_renderer.render(painter, self.p_size) painter.restore() else: QGraphicsSvgItem.paint(self, painter, option, widget) # ------------------------------------------------------------------------------ # canvasportglow.cpp class CanvasPortGlow(QGraphicsDropShadowEffect): def __init__(self, port_type, parent): QGraphicsDropShadowEffect.__init__(self, parent) self.setBlurRadius(12) self.setOffset(0, 0) if port_type == PORT_TYPE_AUDIO_JACK: self.setColor(canvas.theme.line_audio_jack_glow) elif port_type == PORT_TYPE_MIDI_JACK: self.setColor(canvas.theme.line_midi_jack_glow) elif port_type == PORT_TYPE_MIDI_A2J: self.setColor(canvas.theme.line_midi_a2j_glow) elif port_type == PORT_TYPE_MIDI_ALSA: self.setColor(canvas.theme.line_midi_alsa_glow) # ------------------------------------------------------------------------------ # canvasboxshadow.cpp class CanvasBoxShadow(QGraphicsDropShadowEffect): def __init__(self, parent): QGraphicsDropShadowEffect.__init__(self, parent) self.m_fakeParent = None self.setBlurRadius(20) self.setColor(canvas.theme.box_shadow) self.setOffset(0, 0) def setFakeParent(self, fakeParent): self.m_fakeParent = fakeParent def setOpacity(self, opacity): color = QColor(canvas.theme.box_shadow) color.setAlphaF(opacity) self.setColor(color) def draw(self, painter): if self.m_fakeParent: self.m_fakeParent.repaintLines() QGraphicsDropShadowEffect.draw(self, painter)