cadence/src/patchcanvas.py

2652 lines
92 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# PatchBay Canvas engine using QGraphicsView/Scene
# Copyright (C) 2010-2018 Filipe Coelho <falktx@falktx.com>
#
# 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)