Signed-off-by: falkTX <falktx@gmail.com>tags/v2.1-alpha1-winvst
| @@ -163,6 +163,41 @@ rest: libs | |||
| theme: libs | |||
| @$(MAKE) -C source/theme | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # nuitka | |||
| nuitka: bin/carla bin/carla-rack bin/carla-plugin | |||
| bin/carla: | |||
| python3 -m nuitka \ | |||
| -j 8 \ | |||
| --recurse-all \ | |||
| --python-flag -O --warn-unusual-code --warn-implicit-exceptions \ | |||
| --recurse-not-to=PyQt5 \ | |||
| --file-reference-choice=runtime \ | |||
| -o ./$@ \ | |||
| ./source/frontend/carla | |||
| bin/carla-rack: | |||
| python3 -m nuitka \ | |||
| -j 8 \ | |||
| --recurse-all \ | |||
| --python-flag -O --warn-unusual-code --warn-implicit-exceptions \ | |||
| --recurse-not-to=PyQt5 \ | |||
| --file-reference-choice=runtime \ | |||
| -o ./$@ \ | |||
| ./source/frontend/carla | |||
| bin/carla-plugin: | |||
| python3 -m nuitka \ | |||
| -j 8 \ | |||
| --recurse-all \ | |||
| --python-flag -O --warn-unusual-code --warn-implicit-exceptions \ | |||
| --recurse-not-to=PyQt5 \ | |||
| --file-reference-choice=runtime \ | |||
| -o ./$@ \ | |||
| ./source/native-plugins/resources/carla-plugin | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # Binaries (posix32) | |||
| @@ -38,7 +38,6 @@ else: | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| import patchcanvas | |||
| import ui_carla_host | |||
| from carla_app import * | |||
| @@ -47,6 +46,7 @@ from carla_settings import * | |||
| from carla_utils import * | |||
| from carla_widgets import * | |||
| from patchcanvas import patchcanvas | |||
| from widgets.digitalpeakmeter import DigitalPeakMeter | |||
| from widgets.pixmapkeyboard import PixmapKeyboardHArea | |||
| @@ -40,7 +40,7 @@ import ui_carla_settings | |||
| import ui_carla_settings_driver | |||
| from carla_shared import * | |||
| from patchcanvas_theme import * | |||
| from patchcanvas.theme import * | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # ... | |||
| @@ -0,0 +1,279 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import QPointF, QRectF | |||
| from PyQt5.QtWidgets import QGraphicsItem | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Theme) | |||
| from .theme import getDefaultThemeName | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Maximum Id for a plugin, treated as invalid/zero if above this value | |||
| MAX_PLUGIN_ID_ALLOWED = 0x7FF | |||
| # 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_ALSA = 3 | |||
| PORT_TYPE_PARAMETER = 4 | |||
| # Callback Action | |||
| ACTION_GROUP_INFO = 0 # group_id, N, N | |||
| ACTION_GROUP_RENAME = 1 # group_id, N, N | |||
| ACTION_GROUP_SPLIT = 2 # group_id, N, N | |||
| ACTION_GROUP_JOIN = 3 # group_id, N, N | |||
| ACTION_PORT_INFO = 4 # group_id, port_id, N | |||
| ACTION_PORT_RENAME = 5 # group_id, port_id, N | |||
| ACTION_PORTS_CONNECT = 6 # N, N, "outG:outP:inG:inP" | |||
| ACTION_PORTS_DISCONNECT = 7 # conn_id, N, N | |||
| ACTION_PLUGIN_CLONE = 8 # plugin_id, N, N | |||
| ACTION_PLUGIN_EDIT = 9 # plugin_id, N, N | |||
| ACTION_PLUGIN_RENAME = 10 # plugin_id, N, N | |||
| ACTION_PLUGIN_REPLACE = 11 # plugin_id, N, N | |||
| ACTION_PLUGIN_REMOVE = 12 # plugin_id, N, N | |||
| ACTION_PLUGIN_SHOW_UI = 13 # plugin_id, N, N | |||
| ACTION_BG_RIGHT_CLICK = 14 # N, 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 | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # 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 | |||
| CanvasRubberbandType = QGraphicsItem.UserType + 8 | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Canvas options | |||
| class options_t(object): | |||
| __slots__ = [ | |||
| 'theme_name', | |||
| 'auto_hide_groups', | |||
| 'auto_select_items', | |||
| 'use_bezier_lines', | |||
| 'antialiasing', | |||
| 'eyecandy' | |||
| ] | |||
| # Canvas features | |||
| class features_t(object): | |||
| __slots__ = [ | |||
| 'group_info', | |||
| 'group_rename', | |||
| 'port_info', | |||
| 'port_rename', | |||
| 'handle_group_pos' | |||
| ] | |||
| # Main Canvas object | |||
| class Canvas(object): | |||
| def __init__(self): | |||
| self.qobject = None | |||
| self.settings = None | |||
| self.theme = None | |||
| self.initiated = False | |||
| self.group_list = [] | |||
| self.port_list = [] | |||
| self.connection_list = [] | |||
| self.animation_list = [] | |||
| self.callback = self.callback | |||
| self.debug = False | |||
| self.scene = None | |||
| self.last_z_value = 0 | |||
| self.last_connection_id = 0 | |||
| self.initial_pos = QPointF(0, 0) | |||
| self.size_rect = QRectF() | |||
| def callback(self, action, value1, value2, value_str): | |||
| print("Canvas::callback({}, {}, {}, {})".fmt(action, value1, value2, value_str)) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # object lists | |||
| class group_dict_t(object): | |||
| __slots__ = [ | |||
| 'group_id', | |||
| 'group_name', | |||
| 'split', | |||
| 'icon', | |||
| 'plugin_id', | |||
| 'plugin_ui', | |||
| 'widgets' | |||
| ] | |||
| class port_dict_t(object): | |||
| __slots__ = [ | |||
| 'group_id', | |||
| 'port_id', | |||
| 'port_name', | |||
| 'port_mode', | |||
| 'port_type', | |||
| 'is_alternate', | |||
| 'widget' | |||
| ] | |||
| class connection_dict_t(object): | |||
| __slots__ = [ | |||
| 'connection_id', | |||
| 'group_in_id', | |||
| 'port_in_id', | |||
| 'group_out_id', | |||
| 'port_out_id', | |||
| 'widget' | |||
| ] | |||
| class animation_dict_t(object): | |||
| __slots__ = [ | |||
| 'animation', | |||
| 'item' | |||
| ] | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # 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_ALSA: | |||
| return "PORT_TYPE_MIDI_ALSA" | |||
| elif port_type == PORT_TYPE_PARAMETER: | |||
| return "PORT_TYPE_MIDI_PARAMETER" | |||
| else: | |||
| return "PORT_TYPE_???" | |||
| def icon2str(icon): | |||
| if icon == ICON_APPLICATION: | |||
| return "ICON_APPLICATION" | |||
| elif icon == ICON_HARDWARE: | |||
| return "ICON_HARDWARE" | |||
| elif icon == ICON_DISTRHO: | |||
| return "ICON_DISTRHO" | |||
| elif icon == ICON_FILE: | |||
| return "ICON_FILE" | |||
| elif icon == ICON_PLUGIN: | |||
| return "ICON_PLUGIN" | |||
| elif icon == 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_???" | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Global objects | |||
| canvas = Canvas() | |||
| options = options_t() | |||
| options.theme_name = getDefaultThemeName() | |||
| options.auto_hide_groups = False | |||
| options.auto_select_items = 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 | |||
| # 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.auto_select_items = new_options.auto_select_items | |||
| 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 | |||
| @@ -0,0 +1,163 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import Qt, QPointF | |||
| from PyQt5.QtGui import QColor, QLinearGradient, QPainter, QPainterPath, QPen | |||
| from PyQt5.QtWidgets import QGraphicsPathItem | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| options, | |||
| CanvasBezierLineType, | |||
| ACTION_PORTS_DISCONNECT, | |||
| EYECANDY_FULL, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| from .canvasportglow import CanvasPortGlow | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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 isLocked(self): | |||
| return self.m_locked | |||
| def setLocked(self, yesno): | |||
| self.m_locked = yesno | |||
| def isLineSelected(self): | |||
| return self.m_lineSelected | |||
| def updateLineSelected(self): | |||
| if self.m_locked: | |||
| return | |||
| yesno = self.item1.isSelected() or self.item2.isSelected() | |||
| if yesno != self.m_lineSelected and 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 triggerDisconnect(self): | |||
| for connection in canvas.connection_list: | |||
| if (connection.port_out_id == self.item1.getPortId() and connection.port_in_id == self.item2.getPortId()): | |||
| canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") | |||
| break | |||
| def updateLinePos(self): | |||
| if self.item1.getPortMode() == PORT_MODE_OUTPUT: | |||
| rect1 = self.item1.sceneBoundingRect() | |||
| rect2 = self.item2.sceneBoundingRect() | |||
| item1_x = rect1.right() | |||
| item2_x = rect2.left() | |||
| item1_y = rect1.top() + float(canvas.theme.port_height)/2 | |||
| item2_y = rect2.top() + float(canvas.theme.port_height)/2 | |||
| item1_new_x = item1_x + abs(item1_x - item2_x) / 2 | |||
| item2_new_x = item2_x - abs(item1_x - item2_x) / 2 | |||
| 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_ALSA: | |||
| port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) | |||
| elif port_type1 == PORT_TYPE_PARAMETER: | |||
| port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter) | |||
| 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_ALSA: | |||
| port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) | |||
| elif port_type2 == PORT_TYPE_PARAMETER: | |||
| port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter) | |||
| self.setPen(QPen(port_gradient, 2.00001, Qt.SolidLine, Qt.FlatCap)) | |||
| def paint(self, painter, option, widget): | |||
| painter.save() | |||
| painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) | |||
| pen = self.pen() | |||
| cosm_pen = QPen(pen) | |||
| cosm_pen.setCosmetic(True) | |||
| cosm_pen.setWidthF(1.00001) | |||
| QGraphicsPathItem.paint(self, painter, option, widget) | |||
| painter.setPen(cosm_pen) | |||
| painter.setBrush(Qt.NoBrush) | |||
| painter.setOpacity(0.2) | |||
| painter.drawPath(self.path()) | |||
| painter.restore() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,104 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import qWarning, Qt, QPointF | |||
| from PyQt5.QtGui import QPainter, QPainterPath, QPen | |||
| from PyQt5.QtWidgets import QGraphicsPathItem | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| options, | |||
| port_mode2str, | |||
| port_type2str, | |||
| CanvasBezierLineMovType, | |||
| PORT_MODE_INPUT, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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_ALSA: | |||
| pen = QPen(canvas.theme.line_midi_alsa, 2) | |||
| elif port_type == PORT_TYPE_PARAMETER: | |||
| pen = QPen(canvas.theme.line_parameter, 2) | |||
| else: | |||
| qWarning("PatchCanvas::CanvasBezierLineMov({}, {}, {}) - invalid port type".format( | |||
| port_mode2str(port_mode), port_type2str(port_type), parent)) | |||
| pen = QPen(Qt.black) | |||
| pen.setCapStyle(Qt.FlatCap) | |||
| pen.setWidthF(pen.widthF() + 0.00001) | |||
| self.setPen(pen) | |||
| 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() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,590 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import qCritical, Qt, QPointF, QRectF, QTimer | |||
| from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QLinearGradient, QPainter, QPen | |||
| from PyQt5.QtWidgets import QGraphicsItem, QMenu | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| features, | |||
| options, | |||
| port_dict_t, | |||
| CanvasBoxType, | |||
| ANTIALIASING_FULL, | |||
| ACTION_PLUGIN_EDIT, | |||
| ACTION_PLUGIN_SHOW_UI, | |||
| ACTION_PLUGIN_CLONE, | |||
| ACTION_PLUGIN_REMOVE, | |||
| ACTION_PLUGIN_RENAME, | |||
| ACTION_PLUGIN_REPLACE, | |||
| ACTION_GROUP_INFO, | |||
| ACTION_GROUP_JOIN, | |||
| ACTION_GROUP_SPLIT, | |||
| ACTION_GROUP_RENAME, | |||
| ACTION_PORTS_DISCONNECT, | |||
| EYECANDY_FULL, | |||
| PORT_MODE_NULL, | |||
| PORT_MODE_INPUT, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_NULL, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| MAX_PLUGIN_ID_ALLOWED, | |||
| ) | |||
| from .canvasboxshadow import CanvasBoxShadow | |||
| from .canvasicon import CanvasIcon | |||
| from .canvasport import CanvasPort | |||
| from .theme import Theme | |||
| from .utils import CanvasItemFX, CanvasGetFullPortName, CanvasGetPortConnectionList | |||
| #from .canvasportglow import CanvasPortGlow | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class cb_line_t(object): | |||
| def __init__(self, line, connection_id): | |||
| self.line = line | |||
| self.connection_id = 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 | |||
| # plugin Id, < 0 if invalid | |||
| self.m_plugin_id = -1 | |||
| self.m_plugin_ui = False | |||
| # 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() | |||
| self.m_font_name.setFamily(canvas.theme.box_font_name) | |||
| self.m_font_name.setPixelSize(canvas.theme.box_font_size) | |||
| self.m_font_name.setWeight(canvas.theme.box_font_state) | |||
| self.m_font_port = QFont() | |||
| self.m_font_port.setFamily(canvas.theme.port_font_name) | |||
| self.m_font_port.setPixelSize(canvas.theme.port_font_size) | |||
| self.m_font_port.setWeight(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.setFlag(QGraphicsItem.ItemIsFocusable, True) | |||
| if options.auto_select_items: | |||
| self.setAcceptHoverEvents(True) | |||
| self.updatePositions() | |||
| canvas.scene.addItem(self) | |||
| QTimer.singleShot(0, self.fixPos) | |||
| 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 setAsPlugin(self, plugin_id, hasUi): | |||
| self.m_plugin_id = plugin_id | |||
| self.m_plugin_ui = hasUi | |||
| def setIcon(self, icon): | |||
| if self.icon_svg is not None: | |||
| 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, is_alternate): | |||
| if len(self.m_port_list_ids) == 0: | |||
| if options.auto_hide_groups: | |||
| if options.eyecandy == EYECANDY_FULL: | |||
| CanvasItemFX(self, True, False) | |||
| self.setVisible(True) | |||
| new_widget = CanvasPort(self.m_group_id, port_id, port_name, port_mode, port_type, is_alternate, 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.is_alternate = is_alternate | |||
| 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, False) | |||
| else: | |||
| self.setVisible(False) | |||
| def addLineFromGroup(self, line, connection_id): | |||
| new_cbline = cb_line_t(line, 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 is None: | |||
| return | |||
| item = self.icon_svg | |||
| self.icon_svg = None | |||
| canvas.scene.removeItem(item) | |||
| del item | |||
| def updatePositions(self): | |||
| self.prepareGeometryChange() | |||
| # Check Text Name size | |||
| app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30 | |||
| self.p_width = max(50, app_name_size) | |||
| # Get Port List | |||
| port_list = [] | |||
| for port in canvas.port_list: | |||
| if port.group_id == self.m_group_id and port.port_id in self.m_port_list_ids: | |||
| port_list.append(port) | |||
| if len(port_list) == 0: | |||
| self.p_height = canvas.theme.box_header_height | |||
| else: | |||
| max_in_width = max_out_width = 0 | |||
| port_spacing = canvas.theme.port_height + canvas.theme.port_spacing | |||
| # Get Max Box Width, vertical ports re-positioning | |||
| port_types = [PORT_TYPE_AUDIO_JACK, PORT_TYPE_MIDI_JACK, PORT_TYPE_MIDI_ALSA, PORT_TYPE_PARAMETER] | |||
| last_in_type = last_out_type = PORT_TYPE_NULL | |||
| last_in_pos = last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing | |||
| for port_type in port_types: | |||
| for port in port_list: | |||
| if port.port_type != port_type: | |||
| continue | |||
| size = QFontMetrics(self.m_font_port).width(port.port_name) | |||
| if port.port_mode == PORT_MODE_INPUT: | |||
| max_in_width = max(max_in_width, size) | |||
| if port.port_type != last_in_type: | |||
| if last_in_type != PORT_TYPE_NULL: | |||
| last_in_pos += canvas.theme.port_spacingT | |||
| last_in_type = port.port_type | |||
| port.widget.setY(last_in_pos) | |||
| last_in_pos += port_spacing | |||
| elif port.port_mode == PORT_MODE_OUTPUT: | |||
| max_out_width = max(max_out_width, size) | |||
| if port.port_type != last_out_type: | |||
| if last_out_type != PORT_TYPE_NULL: | |||
| last_out_pos += canvas.theme.port_spacingT | |||
| last_out_type = port.port_type | |||
| port.widget.setY(last_out_pos) | |||
| last_out_pos += port_spacing | |||
| self.p_width = max(self.p_width, 30 + max_in_width + max_out_width) | |||
| # Horizontal ports re-positioning | |||
| inX = canvas.theme.port_offset | |||
| outX = self.p_width - max_out_width - canvas.theme.port_offset - 12 | |||
| for port_type in port_types: | |||
| for port in port_list: | |||
| if port.port_mode == PORT_MODE_INPUT: | |||
| port.widget.setX(inX) | |||
| port.widget.setPortWidth(max_in_width) | |||
| elif port.port_mode == PORT_MODE_OUTPUT: | |||
| port.widget.setX(outX) | |||
| port.widget.setPortWidth(max_out_width) | |||
| self.p_height = max(last_in_pos, last_out_pos) | |||
| self.p_height += max(canvas.theme.port_spacing, canvas.theme.port_spacingT) - canvas.theme.port_spacing | |||
| self.p_height += canvas.theme.box_pen.widthF() | |||
| 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): | |||
| event.accept() | |||
| menu = QMenu() | |||
| discMenu = QMenu("Disconnect", menu) | |||
| conn_list = [] | |||
| conn_list_ids = [] | |||
| for port_id in self.m_port_list_ids: | |||
| tmp_conn_list = CanvasGetPortConnectionList(self.m_group_id, port_id) | |||
| for tmp_conn_id, tmp_group_id, tmp_port_id in tmp_conn_list: | |||
| if tmp_conn_id not in conn_list_ids: | |||
| conn_list.append((tmp_conn_id, tmp_group_id, tmp_port_id)) | |||
| conn_list_ids.append(tmp_conn_id) | |||
| if len(conn_list) > 0: | |||
| for conn_id, group_id, port_id in conn_list: | |||
| act_x_disc = discMenu.addAction(CanvasGetFullPortName(group_id, port_id)) | |||
| act_x_disc.setData(conn_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_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) | |||
| if self.m_plugin_id >= 0 and self.m_plugin_id <= MAX_PLUGIN_ID_ALLOWED: | |||
| menu.addSeparator() | |||
| act_p_edit = menu.addAction("Edit") | |||
| act_p_ui = menu.addAction("Show Custom UI") | |||
| menu.addSeparator() | |||
| act_p_clone = menu.addAction("Clone") | |||
| act_p_rename = menu.addAction("Rename...") | |||
| act_p_replace = menu.addAction("Replace...") | |||
| act_p_remove = menu.addAction("Remove") | |||
| if not self.m_plugin_ui: | |||
| act_p_ui.setVisible(False) | |||
| else: | |||
| act_p_edit = act_p_ui = None | |||
| act_p_clone = act_p_rename = None | |||
| act_p_replace = act_p_remove = None | |||
| haveIns = haveOuts = False | |||
| for port in canvas.port_list: | |||
| if port.group_id == self.m_group_id and 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 is None: | |||
| pass | |||
| elif act_selected == act_x_disc_all: | |||
| for conn_id in conn_list_ids: | |||
| canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "") | |||
| elif act_selected == act_x_info: | |||
| canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "") | |||
| elif act_selected == act_x_rename: | |||
| canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 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, "") | |||
| elif act_selected == act_p_edit: | |||
| canvas.callback(ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "") | |||
| elif act_selected == act_p_ui: | |||
| canvas.callback(ACTION_PLUGIN_SHOW_UI, self.m_plugin_id, 0, "") | |||
| elif act_selected == act_p_clone: | |||
| canvas.callback(ACTION_PLUGIN_CLONE, self.m_plugin_id, 0, "") | |||
| elif act_selected == act_p_rename: | |||
| canvas.callback(ACTION_PLUGIN_RENAME, self.m_plugin_id, 0, "") | |||
| elif act_selected == act_p_replace: | |||
| canvas.callback(ACTION_PLUGIN_REPLACE, self.m_plugin_id, 0, "") | |||
| elif act_selected == act_p_remove: | |||
| canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "") | |||
| def keyPressEvent(self, event): | |||
| if self.m_plugin_id >= 0 and event.key() == Qt.Key_Delete: | |||
| event.accept() | |||
| canvas.callback(ACTION_PLUGIN_REMOVE, self.m_plugin_id, 0, "") | |||
| return | |||
| QGraphicsItem.keyPressEvent(self, event) | |||
| def hoverEnterEvent(self, event): | |||
| if options.auto_select_items: | |||
| if len(canvas.scene.selectedItems()) > 0: | |||
| canvas.scene.clearSelection() | |||
| self.setSelected(True) | |||
| QGraphicsItem.hoverEnterEvent(self, event) | |||
| def mouseDoubleClickEvent(self, event): | |||
| if self.m_plugin_id >= 0: | |||
| event.accept() | |||
| canvas.callback(ACTION_PLUGIN_SHOW_UI if self.m_plugin_ui else ACTION_PLUGIN_EDIT, self.m_plugin_id, 0, "") | |||
| return | |||
| QGraphicsItem.mouseDoubleClickEvent(self, event) | |||
| 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: | |||
| event.accept() | |||
| canvas.scene.clearSelection() | |||
| self.setSelected(True) | |||
| self.m_mouse_down = False | |||
| return | |||
| elif event.button() == Qt.LeftButton: | |||
| if self.sceneBoundingRect().contains(event.scenePos()): | |||
| self.m_mouse_down = True | |||
| else: | |||
| # FIXME: Check if still valid: Fix a weird Qt behaviour with right-click mouseMove | |||
| self.m_mouse_down = False | |||
| event.ignore() | |||
| return | |||
| 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.unsetCursor() | |||
| self.fixPos() | |||
| self.m_mouse_down = False | |||
| self.m_cursor_moving = False | |||
| QGraphicsItem.mouseReleaseEvent(self, event) | |||
| def moveEvent(self, event): | |||
| if not self.m_mouse_down: | |||
| self.fixPos() | |||
| def fixPos(self): | |||
| self.setX(round(self.x())) | |||
| self.setY(round(self.y())) | |||
| 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, bool(options.antialiasing == ANTIALIASING_FULL)) | |||
| rect = QRectF(0, 0, self.p_width, self.p_height) | |||
| # Draw rectangle | |||
| pen = QPen(canvas.theme.box_pen_sel if self.isSelected() else canvas.theme.box_pen) | |||
| pen.setWidthF(pen.widthF() + 0.00001) | |||
| painter.setPen(pen) | |||
| lineHinting = pen.widthF() / 2 | |||
| 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) | |||
| rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) | |||
| painter.drawRect(rect) | |||
| # Draw pixmap header | |||
| rect.setHeight(canvas.theme.box_header_height) | |||
| if canvas.theme.box_header_pixmap: | |||
| painter.setPen(Qt.NoPen) | |||
| painter.setBrush(canvas.theme.box_bg_2) | |||
| # outline | |||
| rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) | |||
| painter.drawRect(rect) | |||
| rect.adjust(1, 1, -1, 0) | |||
| painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft()) | |||
| # 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() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,55 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtGui import QColor | |||
| from PyQt5.QtWidgets import QGraphicsDropShadowEffect | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import canvas | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,82 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import QAbstractAnimation | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import canvas, CanvasBoxType | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class CanvasFadeAnimation(QAbstractAnimation): | |||
| def __init__(self, item, show): | |||
| QAbstractAnimation.__init__(self) | |||
| self.m_show = show | |||
| self.m_duration = 0 | |||
| self.m_item = item | |||
| def item(self): | |||
| return self.m_item | |||
| def forceStop(self): | |||
| self.blockSignals(True) | |||
| self.stop() | |||
| def setDuration(self, time): | |||
| if self.m_item.opacity() == 0 and not self.m_show: | |||
| self.m_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) | |||
| try: | |||
| self.m_item.setOpacity(value) | |||
| except RuntimeError: | |||
| print("CanvasFadeAnimation::updateCurrentTime() - failed to animate canvas item, already destroyed?") | |||
| self.forceStop() | |||
| canvas.animation_list.remove(self) | |||
| return | |||
| if self.m_item.type() == CanvasBoxType: | |||
| self.m_item.setShadowOpacity(value) | |||
| def updateDirection(self, direction): | |||
| pass | |||
| def updateState(self, oldState, newState): | |||
| pass | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,135 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import qCritical, QRectF | |||
| from PyQt5.QtGui import QPainter | |||
| from PyQt5.QtSvg import QGraphicsSvgItem, QSvgRenderer | |||
| from PyQt5.QtWidgets import QGraphicsColorizeEffect | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| icon2str, | |||
| CanvasIconType, | |||
| ICON_APPLICATION, | |||
| ICON_HARDWARE, | |||
| ICON_DISTRHO, | |||
| ICON_FILE, | |||
| ICON_PLUGIN, | |||
| ICON_LADISH_ROOM, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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, 16, 16) | |||
| 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, 16, 16) | |||
| elif icon == ICON_FILE: | |||
| icon_path = ":/scalable/pb_file.svg" | |||
| self.p_size = QRectF(5, 4, 16, 16) | |||
| elif icon == ICON_PLUGIN: | |||
| icon_path = ":/scalable/pb_plugin.svg" | |||
| self.p_size = QRectF(5, 4, 16, 16) | |||
| 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 not self.m_renderer: | |||
| QGraphicsSvgItem.paint(self, painter, option, widget) | |||
| return | |||
| painter.save() | |||
| painter.setRenderHint(QPainter.Antialiasing, False) | |||
| painter.setRenderHint(QPainter.TextAntialiasing, False) | |||
| self.m_renderer.render(painter, self.p_size) | |||
| painter.restore() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,156 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import Qt, QLineF | |||
| from PyQt5.QtGui import QLinearGradient, QPainter, QPen | |||
| from PyQt5.QtWidgets import QGraphicsLineItem | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| options, | |||
| CanvasLineType, | |||
| ACTION_PORTS_DISCONNECT, | |||
| EYECANDY_FULL, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| from .canvasportglow import CanvasPortGlow | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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 isLocked(self): | |||
| return self.m_locked | |||
| def setLocked(self, yesno): | |||
| self.m_locked = yesno | |||
| def isLineSelected(self): | |||
| return self.m_lineSelected | |||
| def updateLineSelected(self): | |||
| if self.m_locked: | |||
| return | |||
| yesno = self.item1.isSelected() or self.item2.isSelected() | |||
| if yesno != self.m_lineSelected and 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 triggerDisconnect(self): | |||
| for connection in canvas.connection_list: | |||
| if (connection.port_out_id == self.item1.getPortId() and connection.port_in_id == self.item2.getPortId()): | |||
| canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") | |||
| break | |||
| def updateLinePos(self): | |||
| if self.item1.getPortMode() == PORT_MODE_OUTPUT: | |||
| rect1 = self.item1.sceneBoundingRect() | |||
| rect2 = self.item2.sceneBoundingRect() | |||
| line = QLineF(rect1.right(), | |||
| rect1.top() + float(canvas.theme.port_height)/2, | |||
| rect2.left(), | |||
| rect2.top() + 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_ALSA: | |||
| port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) | |||
| elif port_type1 == PORT_TYPE_PARAMETER: | |||
| port_gradient.setColorAt(pos1, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter) | |||
| 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_ALSA: | |||
| port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa) | |||
| elif port_type2 == PORT_TYPE_PARAMETER: | |||
| port_gradient.setColorAt(pos2, canvas.theme.line_parameter_sel if self.m_lineSelected else canvas.theme.line_parameter) | |||
| self.setPen(QPen(port_gradient, 2.00001, Qt.SolidLine, Qt.RoundCap)) | |||
| def paint(self, painter, option, widget): | |||
| painter.save() | |||
| painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing)) | |||
| pen = self.pen() | |||
| cosm_pen = QPen(pen) | |||
| cosm_pen.setCosmetic(True) | |||
| cosm_pen.setWidthF(1.00001) | |||
| QGraphicsLineItem.paint(self, painter, option, widget) | |||
| painter.setPen(cosm_pen) | |||
| painter.setBrush(Qt.NoBrush) | |||
| painter.setOpacity(0.2) | |||
| painter.drawLine(self.line()) | |||
| painter.restore() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,98 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import qWarning, Qt, QLineF | |||
| from PyQt5.QtGui import QPainter, QPen | |||
| from PyQt5.QtWidgets import QGraphicsLineItem | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| options, | |||
| port_mode2str, | |||
| port_type2str, | |||
| CanvasLineMovType, | |||
| PORT_MODE_INPUT, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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_ALSA: | |||
| pen = QPen(canvas.theme.line_midi_alsa, 2) | |||
| elif port_type == PORT_TYPE_PARAMETER: | |||
| pen = QPen(canvas.theme.line_parameter, 2) | |||
| else: | |||
| qWarning("PatchCanvas::CanvasLineMov({}, {}, {}) - invalid port type".format( | |||
| port_mode2str(port_mode), port_type2str(port_type), parent)) | |||
| pen = QPen(Qt.black) | |||
| pen.setCapStyle(Qt.RoundCap) | |||
| pen.setWidthF(pen.widthF() + 0.00001) | |||
| self.setPen(pen) | |||
| 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() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,467 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from math import floor | |||
| from PyQt5.QtCore import qCritical, Qt, QLineF, QPointF, QRectF, QTimer | |||
| from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QPainter, QPainterPath, QPen, QPolygonF | |||
| from PyQt5.QtWidgets import QGraphicsItem, QMenu | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| features, | |||
| options, | |||
| port_mode2str, | |||
| port_type2str, | |||
| CanvasPortType, | |||
| ANTIALIASING_FULL, | |||
| ACTION_PORT_INFO, | |||
| ACTION_PORT_RENAME, | |||
| ACTION_PORTS_CONNECT, | |||
| ACTION_PORTS_DISCONNECT, | |||
| PORT_MODE_INPUT, | |||
| PORT_MODE_OUTPUT, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| from .canvasbezierlinemov import CanvasBezierLineMov | |||
| from .canvaslinemov import CanvasLineMov | |||
| from .theme import Theme | |||
| from .utils import CanvasGetFullPortName, CanvasGetPortConnectionList | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class CanvasPort(QGraphicsItem): | |||
| def __init__(self, group_id, port_id, port_name, port_mode, port_type, is_alternate, parent): | |||
| QGraphicsItem.__init__(self, parent) | |||
| # Save Variables, useful for later | |||
| self.m_group_id = group_id | |||
| self.m_port_id = port_id | |||
| self.m_port_mode = port_mode | |||
| self.m_port_type = port_type | |||
| self.m_port_name = port_name | |||
| self.m_is_alternate = is_alternate | |||
| # Base Variables | |||
| self.m_port_width = 15 | |||
| self.m_port_height = canvas.theme.port_height | |||
| self.m_port_font = QFont() | |||
| self.m_port_font.setFamily(canvas.theme.port_font_name) | |||
| self.m_port_font.setPixelSize(canvas.theme.port_font_size) | |||
| self.m_port_font.setWeight(canvas.theme.port_font_state) | |||
| self.m_line_mov = None | |||
| self.m_hover_item = None | |||
| self.m_mouse_down = False | |||
| self.m_cursor_moving = False | |||
| self.setFlags(QGraphicsItem.ItemIsSelectable) | |||
| if options.auto_select_items: | |||
| self.setAcceptHoverEvents(True) | |||
| def getGroupId(self): | |||
| return self.m_group_id | |||
| 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 hoverEnterEvent(self, event): | |||
| if options.auto_select_items: | |||
| self.setSelected(True) | |||
| QGraphicsItem.hoverEnterEvent(self, event) | |||
| def hoverLeaveEvent(self, event): | |||
| if options.auto_select_items: | |||
| self.setSelected(False) | |||
| QGraphicsItem.hoverLeaveEvent(self, event) | |||
| 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 not self.m_mouse_down: | |||
| QGraphicsItem.mouseMoveEvent(self, event) | |||
| return | |||
| event.accept() | |||
| if not self.m_cursor_moving: | |||
| self.setCursor(QCursor(Qt.CrossCursor)) | |||
| self.m_cursor_moving = True | |||
| for connection in canvas.connection_list: | |||
| if ( | |||
| (connection.group_out_id == self.m_group_id and | |||
| connection.port_out_id == self.m_port_id) | |||
| or | |||
| (connection.group_in_id == self.m_group_id and | |||
| 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)): | |||
| for _, itemx in enumerate(items): | |||
| if itemx.type() != CanvasPortType: | |||
| continue | |||
| if itemx == self: | |||
| continue | |||
| if item is None or itemx.parentItem().zValue() > item.parentItem().zValue(): | |||
| item = itemx | |||
| if self.m_hover_item and self.m_hover_item != item: | |||
| self.m_hover_item.setSelected(False) | |||
| if item is not None: | |||
| if item.getPortMode() != self.m_port_mode and item.getPortType() == self.m_port_type: | |||
| 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()) | |||
| def mouseReleaseEvent(self, event): | |||
| if self.m_mouse_down: | |||
| if self.m_line_mov is not None: | |||
| item = self.m_line_mov | |||
| self.m_line_mov = None | |||
| canvas.scene.removeItem(item) | |||
| del item | |||
| for connection in canvas.connection_list: | |||
| if ( | |||
| (connection.group_out_id == self.m_group_id and | |||
| connection.port_out_id == self.m_port_id) | |||
| or | |||
| (connection.group_in_id == self.m_group_id and | |||
| connection.port_in_id == self.m_port_id) | |||
| ): | |||
| connection.widget.setLocked(False) | |||
| if self.m_hover_item: | |||
| # TODO: a better way to check already existing connection | |||
| for connection in canvas.connection_list: | |||
| hover_group_id = self.m_hover_item.getGroupId() | |||
| hover_port_id = self.m_hover_item.getPortId() | |||
| # FIXME clean ths big if stuff | |||
| if ( | |||
| (connection.group_out_id == self.m_group_id and | |||
| connection.port_out_id == self.m_port_id and | |||
| connection.group_in_id == hover_group_id and | |||
| connection.port_in_id == hover_port_id) | |||
| or | |||
| (connection.group_out_id == hover_group_id and | |||
| connection.port_out_id == hover_port_id and | |||
| connection.group_in_id == self.m_group_id and | |||
| connection.port_in_id == self.m_port_id) | |||
| ): | |||
| canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "") | |||
| break | |||
| else: | |||
| if self.m_port_mode == PORT_MODE_OUTPUT: | |||
| conn = "%i:%i:%i:%i" % (self.m_group_id, self.m_port_id, | |||
| self.m_hover_item.getGroupId(), self.m_hover_item.getPortId()) | |||
| canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn) | |||
| else: | |||
| conn = "%i:%i:%i:%i" % (self.m_hover_item.getGroupId(), | |||
| self.m_hover_item.getPortId(), self.m_group_id, self.m_port_id) | |||
| canvas.callback(ACTION_PORTS_CONNECT, 0, 0, conn) | |||
| canvas.scene.clearSelection() | |||
| if self.m_cursor_moving: | |||
| self.unsetCursor() | |||
| self.m_hover_item = None | |||
| self.m_mouse_down = False | |||
| self.m_cursor_moving = False | |||
| QGraphicsItem.mouseReleaseEvent(self, event) | |||
| def contextMenuEvent(self, event): | |||
| event.accept() | |||
| canvas.scene.clearSelection() | |||
| self.setSelected(True) | |||
| menu = QMenu() | |||
| discMenu = QMenu("Disconnect", menu) | |||
| conn_list = CanvasGetPortConnectionList(self.m_group_id, self.m_port_id) | |||
| if len(conn_list) > 0: | |||
| for conn_id, group_id, port_id in conn_list: | |||
| act_x_disc = discMenu.addAction(CanvasGetFullPortName(group_id, port_id)) | |||
| act_x_disc.setData(conn_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: | |||
| self.triggerDisconnect(conn_list) | |||
| elif act_selected == act_x_info: | |||
| canvas.callback(ACTION_PORT_INFO, self.m_group_id, self.m_port_id, "") | |||
| elif act_selected == act_x_rename: | |||
| canvas.callback(ACTION_PORT_RENAME, self.m_group_id, self.m_port_id, "") | |||
| def setPortSelected(self, yesno): | |||
| for connection in canvas.connection_list: | |||
| if ( | |||
| (connection.group_out_id == self.m_group_id and | |||
| connection.port_out_id == self.m_port_id) | |||
| or | |||
| (connection.group_in_id == self.m_group_id and | |||
| connection.port_in_id == self.m_port_id) | |||
| ): | |||
| connection.widget.updateLineSelected() | |||
| def itemChange(self, change, value): | |||
| if change == QGraphicsItem.ItemSelectedHasChanged: | |||
| self.setPortSelected(value) | |||
| return QGraphicsItem.itemChange(self, change, value) | |||
| def triggerDisconnect(self, conn_list=None): | |||
| if not conn_list: | |||
| conn_list = CanvasGetPortConnectionList(self.m_group_id, self.m_port_id) | |||
| for conn_id, in conn_list: | |||
| canvas.callback(ACTION_PORTS_DISCONNECT, conn_id, 0, "") | |||
| 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)) | |||
| selected = self.isSelected() | |||
| theme = canvas.theme | |||
| if self.m_port_type == PORT_TYPE_AUDIO_JACK: | |||
| poly_color = theme.port_audio_jack_bg_sel if selected else theme.port_audio_jack_bg | |||
| poly_pen = theme.port_audio_jack_pen_sel if selected else theme.port_audio_jack_pen | |||
| text_pen = theme.port_audio_jack_text_sel if selected else theme.port_audio_jack_text | |||
| conn_pen = QPen(theme.port_audio_jack_pen_sel) | |||
| elif self.m_port_type == PORT_TYPE_MIDI_JACK: | |||
| poly_color = theme.port_midi_jack_bg_sel if selected else theme.port_midi_jack_bg | |||
| poly_pen = theme.port_midi_jack_pen_sel if selected else theme.port_midi_jack_pen | |||
| text_pen = theme.port_midi_jack_text_sel if selected else theme.port_midi_jack_text | |||
| conn_pen = QPen(theme.port_midi_jack_pen_sel) | |||
| elif self.m_port_type == PORT_TYPE_MIDI_ALSA: | |||
| poly_color = theme.port_midi_alsa_bg_sel if selected else theme.port_midi_alsa_bg | |||
| poly_pen = theme.port_midi_alsa_pen_sel if selected else theme.port_midi_alsa_pen | |||
| text_pen = theme.port_midi_alsa_text_sel if selected else theme.port_midi_alsa_text | |||
| conn_pen = QPen(theme.port_midi_alsa_pen_sel) | |||
| elif self.m_port_type == PORT_TYPE_PARAMETER: | |||
| poly_color = theme.port_parameter_bg_sel if selected else theme.port_parameter_bg | |||
| poly_pen = theme.port_parameter_pen_sel if selected else theme.port_parameter_pen | |||
| text_pen = theme.port_parameter_text_sel if selected else theme.port_parameter_text | |||
| conn_pen = QPen(theme.port_parameter_pen_sel) | |||
| else: | |||
| qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type)) | |||
| return | |||
| # To prevent quality worsening | |||
| poly_pen = QPen(poly_pen) | |||
| poly_pen.setWidthF(poly_pen.widthF() + 0.00001) | |||
| if self.m_is_alternate: | |||
| poly_color = poly_color.darker(180) | |||
| #poly_pen.setColor(poly_pen.color().darker(110)) | |||
| #text_pen.setColor(text_pen.color()) #.darker(150)) | |||
| #conn_pen.setColor(conn_pen.color()) #.darker(150)) | |||
| lineHinting = poly_pen.widthF() / 2 | |||
| poly_locx = [0, 0, 0, 0, 0] | |||
| poly_corner_xhinting = (float(canvas.theme.port_height)/2) % floor(float(canvas.theme.port_height)/2) | |||
| if poly_corner_xhinting == 0: | |||
| poly_corner_xhinting = 0.5 * (1 - 7 / (float(canvas.theme.port_height)/2)) | |||
| 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] = lineHinting | |||
| poly_locx[1] = self.m_port_width + 5 - lineHinting | |||
| poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting | |||
| poly_locx[3] = self.m_port_width + 5 - lineHinting | |||
| poly_locx[4] = lineHinting | |||
| elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: | |||
| poly_locx[0] = lineHinting | |||
| poly_locx[1] = self.m_port_width + 5 - lineHinting | |||
| poly_locx[2] = self.m_port_width + 5 - lineHinting | |||
| poly_locx[3] = self.m_port_width + 5 - lineHinting | |||
| poly_locx[4] = lineHinting | |||
| 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 - lineHinting | |||
| poly_locx[1] = 7 + lineHinting | |||
| poly_locx[2] = 0 + poly_corner_xhinting | |||
| poly_locx[3] = 7 + lineHinting | |||
| poly_locx[4] = self.m_port_width + 12 - lineHinting | |||
| elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: | |||
| poly_locx[0] = self.m_port_width + 12 - lineHinting | |||
| poly_locx[1] = 5 + lineHinting | |||
| poly_locx[2] = 5 + lineHinting | |||
| poly_locx[3] = 5 + lineHinting | |||
| poly_locx[4] = self.m_port_width + 12 - lineHinting | |||
| 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 | |||
| polygon = QPolygonF() | |||
| polygon += QPointF(poly_locx[0], lineHinting) | |||
| polygon += QPointF(poly_locx[1], lineHinting) | |||
| polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2) | |||
| polygon += QPointF(poly_locx[3], canvas.theme.port_height - lineHinting) | |||
| polygon += QPointF(poly_locx[4], canvas.theme.port_height - lineHinting) | |||
| polygon += QPointF(poly_locx[0], lineHinting) | |||
| if canvas.theme.port_bg_pixmap: | |||
| portRect = polygon.boundingRect().adjusted(-lineHinting+1, -lineHinting+1, lineHinting-1, lineHinting-1) | |||
| portPos = portRect.topLeft() | |||
| painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos) | |||
| else: | |||
| painter.setBrush(poly_color) #.lighter(200)) | |||
| 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 canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap: | |||
| conn_pen.setCosmetic(True) | |||
| conn_pen.setWidthF(0.4) | |||
| painter.setPen(conn_pen) | |||
| if self.m_port_mode == PORT_MODE_INPUT: | |||
| connLineX = portRect.left()+1 | |||
| else: | |||
| connLineX = portRect.right()-1 | |||
| conn_path = QPainterPath() | |||
| conn_path.addRect(QRectF(connLineX-1, portRect.top(), 2, portRect.height())) | |||
| painter.fillPath(conn_path, conn_pen.brush()) | |||
| painter.drawLine(QLineF(connLineX, portRect.top(), connLineX, portRect.bottom())) | |||
| painter.restore() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,53 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtWidgets import QGraphicsDropShadowEffect | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| PORT_TYPE_AUDIO_JACK, | |||
| PORT_TYPE_MIDI_ALSA, | |||
| PORT_TYPE_MIDI_JACK, | |||
| PORT_TYPE_PARAMETER, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| 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_ALSA: | |||
| self.setColor(canvas.theme.line_midi_alsa_glow) | |||
| elif port_type == PORT_TYPE_PARAMETER: | |||
| self.setColor(canvas.theme.line_parameter_glow) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,899 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import pyqtSlot, qCritical, qFatal, qWarning, QObject | |||
| from PyQt5.QtCore import QPointF, QRectF, QSettings, QTimer | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| features, | |||
| options, | |||
| group_dict_t, | |||
| port_dict_t, | |||
| connection_dict_t, | |||
| bool2str, | |||
| icon2str, | |||
| split2str, | |||
| port_mode2str, | |||
| port_type2str, | |||
| CanvasIconType, | |||
| CanvasRubberbandType, | |||
| ACTION_PORTS_DISCONNECT, | |||
| EYECANDY_FULL, | |||
| ICON_APPLICATION, | |||
| ICON_HARDWARE, | |||
| ICON_LADISH_ROOM, | |||
| PORT_MODE_INPUT, | |||
| PORT_MODE_OUTPUT, | |||
| SPLIT_YES, | |||
| SPLIT_NO, | |||
| SPLIT_UNDEF, | |||
| MAX_PLUGIN_ID_ALLOWED, | |||
| ) | |||
| from .canvasbox import CanvasBox | |||
| from .canvasbezierline import CanvasBezierLine | |||
| from .canvasline import CanvasLine | |||
| from .theme import Theme, getDefaultTheme, getThemeName | |||
| from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRemoveItemFX | |||
| # FIXME | |||
| from . import * | |||
| from .scene import PatchScene | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class CanvasObject(QObject): | |||
| def __init__(self, parent=None): | |||
| QObject.__init__(self, parent) | |||
| @pyqtSlot() | |||
| def AnimationFinishedShow(self): | |||
| animation = self.sender() | |||
| if animation: | |||
| animation.forceStop() | |||
| canvas.animation_list.remove(animation) | |||
| @pyqtSlot() | |||
| def AnimationFinishedHide(self): | |||
| animation = self.sender() | |||
| if animation: | |||
| animation.forceStop() | |||
| canvas.animation_list.remove(animation) | |||
| item = animation.item() | |||
| if item: | |||
| item.hide() | |||
| @pyqtSlot() | |||
| def AnimationFinishedDestroy(self): | |||
| animation = self.sender() | |||
| if animation: | |||
| animation.forceStop() | |||
| canvas.animation_list.remove(animation) | |||
| item = animation.item() | |||
| if item: | |||
| CanvasRemoveItemFX(item) | |||
| @pyqtSlot() | |||
| def PortContextMenuDisconnect(self): | |||
| try: | |||
| connectionId = int(self.sender().data()) | |||
| except: | |||
| return | |||
| CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "") | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def getStoredCanvasPosition(key, fallback_pos): | |||
| try: | |||
| return canvas.settings.value("CanvasPositions/" + key, fallback_pos, type=QPointF) | |||
| except: | |||
| return fallback_pos | |||
| def getStoredCanvasSplit(group_name, fallback_split_mode): | |||
| try: | |||
| return canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, fallback_split_mode, type=int) | |||
| except: | |||
| return fallback_split_mode | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def init(appName, scene, callback, debug=False): | |||
| if debug: | |||
| print("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.callback = callback | |||
| canvas.debug = debug | |||
| canvas.scene = scene | |||
| 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: | |||
| print("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.group_id, 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 group_id, port_id in port_list_ids: | |||
| removePort(group_id, port_id) | |||
| 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.clearSelection() | |||
| animatedItems = [] | |||
| for animation in canvas.animation_list: | |||
| animatedItems.append(animation.item()) | |||
| for item in canvas.scene.items(): | |||
| if item.type() in (CanvasIconType, CanvasRubberbandType) or item in animatedItems: | |||
| continue | |||
| canvas.scene.removeItem(item) | |||
| del item | |||
| canvas.initiated = False | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def setInitialPos(x, y): | |||
| if canvas.debug: | |||
| print("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: | |||
| print("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) | |||
| canvas.scene.updateLimits() | |||
| canvas.scene.fixScaleFactor() | |||
| def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION): | |||
| if canvas.debug: | |||
| print("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: | |||
| isHardware = bool(icon == ICON_HARDWARE) | |||
| if features.handle_group_pos: | |||
| split = getStoredCanvasSplit(group_name, SPLIT_YES if isHardware else split) | |||
| elif isHardware: | |||
| split = SPLIT_YES | |||
| 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.plugin_id = -1 | |||
| group_dict.plugin_ui = False | |||
| 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(getStoredCanvasPosition(group_name + "_OUTPUT", CanvasGetNewGroupPos(False))) | |||
| else: | |||
| group_box.setPos(CanvasGetNewGroupPos(False)) | |||
| 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(getStoredCanvasPosition(group_name + "_INPUT", CanvasGetNewGroupPos(True))) | |||
| 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, False) | |||
| group_sbox.checkItemPos() | |||
| else: | |||
| group_box.setSplit(False) | |||
| if features.handle_group_pos: | |||
| group_box.setPos(getStoredCanvasPosition(group_name, CanvasGetNewGroupPos(False))) | |||
| 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, False) | |||
| return | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| def removeGroup(group_id): | |||
| if canvas.debug: | |||
| print("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: | |||
| print("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: | |||
| print("PatchCanvas::splitGroup(%i)" % group_id) | |||
| item = None | |||
| group_name = "" | |||
| group_icon = ICON_APPLICATION | |||
| plugin_id = -1 | |||
| plugin_ui = False | |||
| 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 | |||
| plugin_id = group.plugin_id | |||
| plugin_ui = group.plugin_ui | |||
| 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.is_alternate = port.is_alternate | |||
| 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.group_in_id = connection.group_in_id | |||
| connection_dict.port_in_id = connection.port_in_id | |||
| connection_dict.group_out_id = connection.group_out_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(group_id, port_id) | |||
| removeGroup(group_id) | |||
| # Step 3 - Re-create Item, now splitted | |||
| addGroup(group_id, group_name, SPLIT_YES, group_icon) | |||
| if plugin_id >= 0: | |||
| setGroupAsPlugin(group_id, plugin_id, plugin_ui) | |||
| for port in ports_data: | |||
| addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate) | |||
| for conn in conns_data: | |||
| connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id) | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| def joinGroup(group_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::joinGroup(%i)" % group_id) | |||
| item = None | |||
| s_item = None | |||
| group_name = "" | |||
| group_icon = ICON_APPLICATION | |||
| plugin_id = -1 | |||
| plugin_ui = False | |||
| 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 | |||
| plugin_id = group.plugin_id | |||
| plugin_ui = group.plugin_ui | |||
| 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.is_alternate = port.is_alternate | |||
| 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.group_in_id = connection.group_in_id | |||
| connection_dict.port_in_id = connection.port_in_id | |||
| connection_dict.group_out_id = connection.group_out_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(group_id, port_id) | |||
| removeGroup(group_id) | |||
| # Step 3 - Re-create Item, now together | |||
| addGroup(group_id, group_name, SPLIT_NO, group_icon) | |||
| if plugin_id >= 0: | |||
| setGroupAsPlugin(group_id, plugin_id, plugin_ui) | |||
| for port in ports_data: | |||
| addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type, port.is_alternate) | |||
| for conn in conns_data: | |||
| connectPorts(conn.connection_id, conn.group_out_id, conn.port_out_id, conn.group_in_id, conn.port_in_id) | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT): | |||
| if canvas.debug: | |||
| print("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode))) | |||
| for group in canvas.group_list: | |||
| if group.group_id == group_id: | |||
| return group.widgets[1 if (group.split and port_mode == PORT_MODE_INPUT) else 0].pos() | |||
| qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode))) | |||
| return QPointF(0, 0) | |||
| def saveGroupPositions(): | |||
| if canvas.debug: | |||
| print("PatchCanvas::getGroupPositions()") | |||
| ret = [] | |||
| for group in canvas.group_list: | |||
| if group.split: | |||
| pos1 = group.widgets[0].pos() | |||
| pos2 = group.widgets[1].pos() | |||
| else: | |||
| pos1 = group.widgets[0].pos() | |||
| pos2 = QPointF(0, 0) | |||
| ret.append({ | |||
| "name" : group.group_name, | |||
| "pos1x": pos1.x(), | |||
| "pos1y": pos1.y(), | |||
| "pos2x": pos2.x(), | |||
| "pos2y": pos2.y(), | |||
| "split": group.split, | |||
| }) | |||
| return ret | |||
| def restoreGroupPositions(dataList): | |||
| if canvas.debug: | |||
| print("PatchCanvas::restoreGroupPositions(...)") | |||
| mapping = {} | |||
| for group in canvas.group_list: | |||
| mapping[group.group_name] = group | |||
| for data in dataList: | |||
| name = data['name'] | |||
| group = mapping.get(name, None) | |||
| if group is None: | |||
| continue | |||
| group.widgets[0].setPos(data['pos1x'], data['pos1y']) | |||
| if group.split and group.widgets[1]: | |||
| group.widgets[1].setPos(data['pos2x'], data['pos2y']) | |||
| 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: | |||
| print("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: | |||
| print("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 setGroupAsPlugin(group_id, plugin_id, hasUi): | |||
| if canvas.debug: | |||
| print("PatchCanvas::setGroupAsPlugin(%i, %i, %s)" % (group_id, plugin_id, bool2str(hasUi))) | |||
| for group in canvas.group_list: | |||
| if group.group_id == group_id: | |||
| group.plugin_id = plugin_id | |||
| group.plugin_ui = hasUi | |||
| group.widgets[0].setAsPlugin(plugin_id, hasUi) | |||
| if group.split and group.widgets[1]: | |||
| group.widgets[1].setAsPlugin(plugin_id, hasUi) | |||
| return | |||
| qCritical("PatchCanvas::setGroupAsPlugin(%i, %i, %s) - unable to find group to set as plugin" % ( | |||
| group_id, plugin_id, bool2str(hasUi))) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def addPort(group_id, port_id, port_name, port_mode, port_type, is_alternate=False): | |||
| if canvas.debug: | |||
| print("PatchCanvas::addPort(%i, %i, %s, %s, %s, %s)" % ( | |||
| group_id, port_id, port_name.encode(), | |||
| port_mode2str(port_mode), port_type2str(port_type), bool2str(is_alternate))) | |||
| 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, is_alternate) | |||
| 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 | |||
| 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.is_alternate = is_alternate | |||
| port_dict.widget = port_widget | |||
| canvas.port_list.append(port_dict) | |||
| box_widget.updatePositions() | |||
| if options.eyecandy == EYECANDY_FULL: | |||
| CanvasItemFX(port_widget, True, False) | |||
| return | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| def removePort(group_id, port_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::removePort(%i, %i)" % (group_id, port_id)) | |||
| for port in canvas.port_list: | |||
| if port.group_id == group_id and port.port_id == port_id: | |||
| item = port.widget | |||
| item.parentItem().removePortFromGroup(port_id) | |||
| canvas.scene.removeItem(item) | |||
| canvas.port_list.remove(port) | |||
| del item | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| return | |||
| qCritical("PatchCanvas::removePort(%i, %i) - Unable to find port to remove" % (group_id, port_id)) | |||
| def renamePort(group_id, port_id, new_port_name): | |||
| if canvas.debug: | |||
| print("PatchCanvas::renamePort(%i, %i, %s)" % (group_id, port_id, new_port_name.encode())) | |||
| for port in canvas.port_list: | |||
| if port.group_id == group_id and 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, %i, %s) - Unable to find port to rename" % ( | |||
| group_id, port_id, new_port_name.encode())) | |||
| def connectPorts(connection_id, group_out_id, port_out_id, group_in_id, port_in_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::connectPorts(%i, %i, %i, %i, %i)" % ( | |||
| connection_id, group_out_id, port_out_id, group_in_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.group_id == group_out_id and port.port_id == port_out_id: | |||
| port_out = port.widget | |||
| port_out_parent = port_out.parentItem() | |||
| elif port.group_id == group_in_id and 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, %i, %i) - unable to find ports to connect" % ( | |||
| connection_id, group_out_id, port_out_id, group_in_id, port_in_id)) | |||
| return | |||
| connection_dict = connection_dict_t() | |||
| connection_dict.connection_id = connection_id | |||
| connection_dict.group_in_id = group_in_id | |||
| connection_dict.port_in_id = port_in_id | |||
| connection_dict.group_out_id = group_out_id | |||
| connection_dict.port_out_id = port_out_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, False) | |||
| return | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| def disconnectPorts(connection_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::disconnectPorts(%i)" % connection_id) | |||
| line = None | |||
| item1 = None | |||
| item2 = None | |||
| group1id = port1id = 0 | |||
| group2id = port2id = 0 | |||
| for connection in canvas.connection_list: | |||
| if connection.connection_id == connection_id: | |||
| group1id = connection.group_out_id | |||
| group2id = connection.group_in_id | |||
| port1id = connection.port_out_id | |||
| port2id = 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.group_id == group1id and port.port_id == port1id: | |||
| 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.group_id == group2id and port.port_id == port2id: | |||
| 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) | |||
| return | |||
| canvas.scene.removeItem(line) | |||
| del line | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def arrange(): | |||
| if canvas.debug: | |||
| print("PatchCanvas::arrange()") | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def updateZValues(): | |||
| if canvas.debug: | |||
| print("PatchCanvas::updateZValues()") | |||
| for group in canvas.group_list: | |||
| group.widgets[0].resetLinesZValue() | |||
| if group.split and group.widgets[1]: | |||
| group.widgets[1].resetLinesZValue() | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def handlePluginRemoved(plugin_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::handlePluginRemoved(%i)" % plugin_id) | |||
| for group in canvas.group_list: | |||
| if group.plugin_id < plugin_id or group.plugin_id > MAX_PLUGIN_ID_ALLOWED: | |||
| continue | |||
| group.plugin_id -= 1 | |||
| group.widgets[0].m_plugin_id -= 1 | |||
| if group.split and group.widgets[1]: | |||
| group.widgets[1].m_plugin_id -= 1 | |||
| def handleAllPluginsRemoved(): | |||
| if canvas.debug: | |||
| print("PatchCanvas::handleAllPluginsRemoved()") | |||
| for group in canvas.group_list: | |||
| if group.plugin_id > MAX_PLUGIN_ID_ALLOWED: | |||
| continue | |||
| group.plugin_id = -1 | |||
| group.plugin_ui = False | |||
| group.widgets[0].m_plugin_id = -1 | |||
| group.widgets[0].m_plugin_ui = False | |||
| if group.split and group.widgets[1]: | |||
| group.widgets[1].m_plugin_id = -1 | |||
| group.widgets[1].m_plugin_ui = False | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,405 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from math import floor | |||
| from PyQt5.QtCore import pyqtSignal, pyqtSlot, qFatal, Qt, QPointF, QRectF | |||
| from PyQt5.QtGui import QCursor, QPixmap, QPolygonF | |||
| from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsScene | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import ( | |||
| canvas, | |||
| CanvasBoxType, | |||
| CanvasPortType, | |||
| CanvasLineType, | |||
| CanvasBezierLineType, | |||
| CanvasRubberbandType, | |||
| ACTION_BG_RIGHT_CLICK, | |||
| MAX_PLUGIN_ID_ALLOWED, | |||
| ) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class RubberbandRect(QGraphicsRectItem): | |||
| def __init__(self, scene): | |||
| QGraphicsRectItem.__init__(self, QRectF(0, 0, 0, 0)) | |||
| self.setZValue(-1) | |||
| self.hide() | |||
| scene.addItem(self) | |||
| def type(self): | |||
| return CanvasRubberbandType | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| class PatchScene(QGraphicsScene): | |||
| scaleChanged = pyqtSignal(float) | |||
| sceneGroupMoved = pyqtSignal(int, int, QPointF) | |||
| pluginSelected = pyqtSignal(list) | |||
| def __init__(self, parent, view): | |||
| QGraphicsScene.__init__(self, parent) | |||
| self.m_ctrl_down = False | |||
| self.m_scale_area = False | |||
| self.m_mouse_down_init = False | |||
| self.m_mouse_rubberband = False | |||
| self.m_mid_button_down = False | |||
| self.m_pointer_border = QRectF(0.0, 0.0, 1.0, 1.0) | |||
| self.m_scale_min = 0.1 | |||
| self.m_rubberband = RubberbandRect(self) | |||
| self.m_rubberband_selection = False | |||
| self.m_rubberband_orig_point = QPointF(0, 0) | |||
| self.m_view = view | |||
| if not self.m_view: | |||
| qFatal("PatchCanvas::PatchScene() - invalid view") | |||
| self.curCut = None | |||
| self.curZoomArea = None | |||
| self.selectionChanged.connect(self.slot_selectionChanged) | |||
| def fixScaleFactor(self, transform=None): | |||
| fix, set_view = False, False | |||
| if not transform: | |||
| set_view = True | |||
| view = self.m_view | |||
| transform = view.transform() | |||
| scale = transform.m11() | |||
| if scale > 3.0: | |||
| fix = True | |||
| transform.reset() | |||
| transform.scale(3.0, 3.0) | |||
| elif scale < self.m_scale_min: | |||
| fix = True | |||
| transform.reset() | |||
| transform.scale(self.m_scale_min, self.m_scale_min) | |||
| if set_view: | |||
| if fix: | |||
| view.setTransform(transform) | |||
| self.scaleChanged.emit(transform.m11()) | |||
| return fix | |||
| def updateLimits(self): | |||
| w0 = canvas.size_rect.width() | |||
| h0 = canvas.size_rect.height() | |||
| w1 = self.m_view.width() | |||
| h1 = self.m_view.height() | |||
| self.m_scale_min = w1/w0 if w0/h0 > w1/h1 else h1/h0 | |||
| 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) | |||
| cur_color = "black" if canvas.theme.canvas_bg.blackF() < 0.5 else "white" | |||
| self.curCut = QCursor(QPixmap(":/cursors/cut-"+cur_color+".png"), 1, 1) | |||
| self.curZoomArea = QCursor(QPixmap(":/cursors/zoom-area-"+cur_color+".png"), 8, 7) | |||
| 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() | |||
| x = pos.x() | |||
| y = pos.y() | |||
| if first_value: | |||
| first_value = False | |||
| min_x, min_y = x, y | |||
| max_x = x + rect.width() | |||
| max_y = y + rect.height() | |||
| else: | |||
| min_x = min(min_x, x) | |||
| min_y = min(min_y, y) | |||
| max_x = max(max_x, x + rect.width()) | |||
| max_y = max(max_y, y + rect.height()) | |||
| 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): | |||
| view = self.m_view | |||
| transform = view.transform() | |||
| if transform.m11() < 3.0: | |||
| transform.scale(1.2, 1.2) | |||
| view.setTransform(transform) | |||
| self.scaleChanged.emit(transform.m11()) | |||
| def zoom_out(self): | |||
| view = self.m_view | |||
| transform = view.transform() | |||
| if transform.m11() > self.m_scale_min: | |||
| transform.scale(0.833333333333333, 0.833333333333333) | |||
| view.setTransform(transform) | |||
| self.scaleChanged.emit(transform.m11()) | |||
| def zoom_reset(self): | |||
| self.m_view.resetTransform() | |||
| self.scaleChanged.emit(1.0) | |||
| @pyqtSlot() | |||
| def slot_selectionChanged(self): | |||
| items_list = self.selectedItems() | |||
| if len(items_list) == 0: | |||
| self.pluginSelected.emit([]) | |||
| return | |||
| plugin_list = [] | |||
| for item in items_list: | |||
| if item and item.isVisible(): | |||
| group_item = None | |||
| if item.type() == CanvasBoxType: | |||
| group_item = item | |||
| elif item.type() == CanvasPortType: | |||
| group_item = item.parentItem() | |||
| #elif item.type() in (CanvasLineType, CanvasBezierLineType, CanvasLineMovType, CanvasBezierLineMovType): | |||
| #plugin_list = [] | |||
| #break | |||
| if group_item is not None and group_item.m_plugin_id >= 0: | |||
| plugin_id = group_item.m_plugin_id | |||
| if plugin_id > MAX_PLUGIN_ID_ALLOWED: | |||
| plugin_id = 0 | |||
| plugin_list.append(plugin_id) | |||
| self.pluginSelected.emit(plugin_list) | |||
| def triggerRubberbandScale(self): | |||
| self.m_scale_area = True | |||
| if self.curZoomArea: | |||
| self.m_view.viewport().setCursor(self.curZoomArea) | |||
| def keyPressEvent(self, event): | |||
| if not self.m_view: | |||
| event.ignore() | |||
| return | |||
| if event.key() == Qt.Key_Control: | |||
| self.m_ctrl_down = True | |||
| if self.m_mid_button_down: | |||
| self.startConnectionCut() | |||
| elif event.key() == Qt.Key_Home: | |||
| event.accept() | |||
| self.zoom_fit() | |||
| return | |||
| elif self.m_ctrl_down: | |||
| if event.key() == Qt.Key_Plus: | |||
| event.accept() | |||
| self.zoom_in() | |||
| return | |||
| if event.key() == Qt.Key_Minus: | |||
| event.accept() | |||
| self.zoom_out() | |||
| return | |||
| if event.key() == Qt.Key_1: | |||
| event.accept() | |||
| self.zoom_reset() | |||
| return | |||
| QGraphicsScene.keyPressEvent(self, event) | |||
| def keyReleaseEvent(self, event): | |||
| if event.key() == Qt.Key_Control: | |||
| self.m_ctrl_down = False | |||
| # Connection cut mode off | |||
| if self.m_mid_button_down: | |||
| self.m_view.viewport().unsetCursor() | |||
| QGraphicsScene.keyReleaseEvent(self, event) | |||
| def startConnectionCut(self): | |||
| if self.curCut: | |||
| self.m_view.viewport().setCursor(self.curCut) | |||
| def mousePressEvent(self, event): | |||
| self.m_mouse_down_init = ( | |||
| (event.button() == Qt.LeftButton) or ((event.button() == Qt.RightButton) and self.m_ctrl_down) | |||
| ) | |||
| self.m_mouse_rubberband = False | |||
| if event.button() == Qt.MidButton and self.m_ctrl_down: | |||
| self.m_mid_button_down = True | |||
| self.startConnectionCut() | |||
| pos = event.scenePos() | |||
| self.m_pointer_border.moveTo(floor(pos.x()), floor(pos.y())) | |||
| items = self.items(self.m_pointer_border) | |||
| for item in items: | |||
| if item and item.type() in [CanvasLineType, CanvasBezierLineType, CanvasPortType]: | |||
| item.triggerDisconnect() | |||
| QGraphicsScene.mousePressEvent(self, event) | |||
| def mouseMoveEvent(self, event): | |||
| if self.m_mouse_down_init: | |||
| self.m_mouse_down_init = False | |||
| topmost = self.itemAt(event.scenePos(), self.m_view.transform()) | |||
| self.m_mouse_rubberband = not (topmost and topmost.type() in [CanvasBoxType, CanvasPortType]) | |||
| if self.m_mouse_rubberband: | |||
| event.accept() | |||
| pos = event.scenePos() | |||
| pos_x = pos.x() | |||
| pos_y = pos.y() | |||
| if not self.m_rubberband_selection: | |||
| self.m_rubberband.show() | |||
| self.m_rubberband_selection = True | |||
| self.m_rubberband_orig_point = pos | |||
| rubberband_orig_point = self.m_rubberband_orig_point | |||
| x = min(pos_x, rubberband_orig_point.x()) | |||
| y = min(pos_y, rubberband_orig_point.y()) | |||
| lineHinting = canvas.theme.rubberband_pen.widthF() / 2 | |||
| self.m_rubberband.setRect(x+lineHinting, | |||
| y+lineHinting, | |||
| abs(pos_x - rubberband_orig_point.x()), | |||
| abs(pos_y - rubberband_orig_point.y())) | |||
| return | |||
| if self.m_mid_button_down and self.m_ctrl_down: | |||
| trail = QPolygonF([event.scenePos(), event.lastScenePos(), event.scenePos()]) | |||
| items = self.items(trail) | |||
| for item in items: | |||
| if item and item.type() in [CanvasLineType, CanvasBezierLineType]: | |||
| item.triggerDisconnect() | |||
| QGraphicsScene.mouseMoveEvent(self, event) | |||
| def mouseReleaseEvent(self, event): | |||
| if self.m_scale_area and not self.m_rubberband_selection: | |||
| self.m_scale_area = False | |||
| self.m_view.viewport().unsetCursor() | |||
| if self.m_rubberband_selection: | |||
| if self.m_scale_area: | |||
| self.m_scale_area = False | |||
| self.m_view.viewport().unsetCursor() | |||
| rect = self.m_rubberband.rect() | |||
| self.m_view.fitInView(rect.x(), rect.y(), rect.width(), rect.height(), Qt.KeepAspectRatio) | |||
| self.fixScaleFactor() | |||
| else: | |||
| items_list = self.items() | |||
| 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 | |||
| if event.button() == Qt.MidButton: | |||
| event.accept() | |||
| self.m_mid_button_down = False | |||
| # Connection cut mode off | |||
| if self.m_ctrl_down: | |||
| self.m_view.viewport().unsetCursor() | |||
| return | |||
| QGraphicsScene.mouseReleaseEvent(self, event) | |||
| def zoom_wheel(self, delta): | |||
| transform = self.m_view.transform() | |||
| scale = transform.m11() | |||
| if (delta > 0 and scale < 3.0) or (delta < 0 and scale > self.m_scale_min): | |||
| factor = 1.41 ** (delta / 240.0) | |||
| transform.scale(factor, factor) | |||
| self.fixScaleFactor(transform) | |||
| self.m_view.setTransform(transform) | |||
| self.scaleChanged.emit(transform.m11()) | |||
| def wheelEvent(self, event): | |||
| if not self.m_view: | |||
| event.ignore() | |||
| return | |||
| if self.m_ctrl_down: | |||
| event.accept() | |||
| self.zoom_wheel(event.delta()) | |||
| return | |||
| QGraphicsScene.wheelEvent(self, event) | |||
| def contextMenuEvent(self, event): | |||
| if self.m_ctrl_down: | |||
| event.accept() | |||
| self.triggerRubberbandScale() | |||
| return | |||
| if len(self.selectedItems()) == 0: | |||
| event.accept() | |||
| canvas.callback(ACTION_BG_RIGHT_CLICK, 0, 0, "") | |||
| return | |||
| QGraphicsScene.contextMenuEvent(self, event) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -2,7 +2,7 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas Themes | |||
| # Copyright (C) 2010-2014 Filipe Coelho <falktx@falktx.com> | |||
| # Copyright (C) 2010-2019 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 | |||
| @@ -16,41 +16,29 @@ | |||
| # | |||
| # For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Config) | |||
| from carla_config import * | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| #from sys import platform | |||
| if config_UseQt5: | |||
| from PyQt5.QtCore import Qt | |||
| from PyQt5.QtGui import QColor, QFont, QPen, QPixmap | |||
| else: | |||
| from PyQt4.QtCore import Qt | |||
| from PyQt4.QtGui import QColor, QFont, QPen, QPixmap | |||
| from PyQt5.QtCore import Qt | |||
| from PyQt5.QtGui import QColor, QFont, QPen, QPixmap | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # patchcanvas-theme.cpp | |||
| class Theme(object): | |||
| # enum PortType | |||
| THEME_PORT_SQUARE = 0 | |||
| THEME_PORT_SQUARE = 0 | |||
| THEME_PORT_POLYGON = 1 | |||
| # enum List | |||
| THEME_MODERN_DARK = 0 | |||
| THEME_MODERN_DARK = 0 | |||
| THEME_MODERN_DARK_TINY = 1 | |||
| THEME_MODERN_LIGHT = 2 | |||
| THEME_CLASSIC_DARK = 3 | |||
| THEME_OOSTUDIO = 4 | |||
| THEME_MAX = 5 | |||
| THEME_MODERN_LIGHT = 2 | |||
| THEME_CLASSIC_DARK = 3 | |||
| THEME_OOSTUDIO = 4 | |||
| THEME_MAX = 5 | |||
| # enum BackgroundType | |||
| THEME_BG_SOLID = 0 | |||
| THEME_BG_SOLID = 0 | |||
| THEME_BG_GRADIENT = 1 | |||
| def __init__(self, idx): | |||
| @@ -68,18 +56,18 @@ class Theme(object): | |||
| self.box_bg_1 = QColor(32, 34, 35) | |||
| self.box_bg_2 = QColor(43, 47, 48) | |||
| self.box_shadow = QColor(89, 89, 89, 180) | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 24 | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 24 | |||
| self.box_header_spacing = 0 | |||
| self.box_text = QPen(QColor(240, 240, 240), 0) | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_ypos = 16 | |||
| self.box_font_name = "Deja Vu Sans" | |||
| self.box_font_size = 11 | |||
| self.box_font_state = QFont.Bold | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_use_icon = True | |||
| # Ports | |||
| @@ -118,9 +106,9 @@ class Theme(object): | |||
| self.port_parameter_text = self.port_text | |||
| self.port_parameter_text_sel = self.port_text | |||
| self.port_height = 16 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 2 | |||
| self.port_height = 16 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 2 | |||
| self.port_spacingT = 2 | |||
| # Lines | |||
| @@ -150,18 +138,18 @@ class Theme(object): | |||
| self.box_bg_1 = QColor(32, 34, 35) | |||
| self.box_bg_2 = QColor(43, 47, 48) | |||
| self.box_shadow = QColor(89, 89, 89, 180) | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 14 | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 14 | |||
| self.box_header_spacing = 0 | |||
| self.box_text = QPen(QColor(240, 240, 240), 0) | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_ypos = 10 | |||
| self.box_font_name = "Deja Vu Sans" | |||
| self.box_font_size = 10 | |||
| self.box_font_state = QFont.Bold | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_use_icon = False | |||
| # Ports | |||
| @@ -200,9 +188,9 @@ class Theme(object): | |||
| self.port_parameter_text = self.port_text | |||
| self.port_parameter_text_sel = self.port_text | |||
| self.port_height = 12 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 1 | |||
| self.port_height = 12 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 1 | |||
| self.port_spacingT = 1 | |||
| # Lines | |||
| @@ -232,18 +220,18 @@ class Theme(object): | |||
| self.box_bg_1 = QColor(250, 250, 250) | |||
| self.box_bg_2 = QColor(200, 200, 200) | |||
| self.box_shadow = QColor(1, 1, 1, 100) | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 24 | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 24 | |||
| self.box_header_spacing = 0 | |||
| self.box_text = QPen(QColor(1, 1, 1), 0) | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_ypos = 16 | |||
| self.box_font_name = "Ubuntu" | |||
| self.box_font_size = 11 | |||
| self.box_font_state = QFont.Bold | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_use_icon = True | |||
| # Ports | |||
| @@ -282,9 +270,9 @@ class Theme(object): | |||
| self.port_parameter_text = self.port_text | |||
| self.port_parameter_text_sel = self.port_text | |||
| self.port_height = 16 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 2 | |||
| self.port_height = 16 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 2 | |||
| self.port_spacingT = 2 | |||
| # Lines | |||
| @@ -314,18 +302,18 @@ class Theme(object): | |||
| self.box_bg_1 = QColor(30, 34, 36) | |||
| self.box_bg_2 = QColor(30, 34, 36) | |||
| self.box_shadow = QColor(89, 89, 89, 180) | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 19 | |||
| self.box_header_pixmap = None | |||
| self.box_header_height = 19 | |||
| self.box_header_spacing = 0 | |||
| self.box_text = QPen(QColor(255, 255, 255), 0) | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_sel = self.box_text | |||
| self.box_text_ypos = 12 | |||
| self.box_font_name = "Sans" | |||
| self.box_font_size = 12 | |||
| self.box_font_state = QFont.Normal | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_bg_type = self.THEME_BG_GRADIENT | |||
| self.box_use_icon = False | |||
| # Ports | |||
| @@ -364,9 +352,9 @@ class Theme(object): | |||
| self.port_parameter_text = self.port_text | |||
| self.port_parameter_text_sel = self.port_text | |||
| self.port_height = 14 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 1 | |||
| self.port_height = 14 | |||
| self.port_offset = 0 | |||
| self.port_spacing = 1 | |||
| self.port_spacingT = 0 | |||
| # Lines | |||
| @@ -396,18 +384,18 @@ class Theme(object): | |||
| self.box_bg_1 = QColor(46, 46, 46) | |||
| self.box_bg_2 = QColor(23, 23, 23) | |||
| self.box_shadow = QColor(89, 89, 89, 180) | |||
| self.box_header_pixmap = QPixmap(":/bitmaps/canvas/frame_node_header.png") | |||
| self.box_header_height = 22 | |||
| self.box_header_pixmap = QPixmap(":/bitmaps/canvas/frame_node_header.png") | |||
| self.box_header_height = 22 | |||
| self.box_header_spacing = 6 | |||
| self.box_text = QPen(QColor(144, 144, 144), 0) | |||
| self.box_text_sel = QPen(QColor(189, 122, 214), 0) | |||
| self.box_text_sel = QPen(QColor(189, 122, 214), 0) | |||
| self.box_text_ypos = 16 | |||
| self.box_font_name = "Deja Vu Sans" | |||
| self.box_font_size = 11 | |||
| self.box_font_state = QFont.Bold | |||
| self.box_bg_type = self.THEME_BG_SOLID | |||
| self.box_bg_type = self.THEME_BG_SOLID | |||
| self.box_use_icon = False | |||
| # Ports | |||
| @@ -450,9 +438,9 @@ class Theme(object): | |||
| self.port_parameter_text_sel = self.port_parameter_pen_sel | |||
| # missing, ports 2 | |||
| self.port_height = 21 | |||
| self.port_offset = 1 | |||
| self.port_spacing = 3 | |||
| self.port_height = 21 | |||
| self.port_offset = 1 | |||
| self.port_spacing = 3 | |||
| self.port_spacingT = 0 | |||
| # Lines | |||
| @@ -472,22 +460,25 @@ class Theme(object): | |||
| self.rubberband_pen = QPen(QColor(1, 230, 238), 2, Qt.SolidLine) | |||
| self.rubberband_brush = QColor(90, 90, 90, 100) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def getDefaultTheme(): | |||
| return Theme.THEME_MODERN_DARK | |||
| def getThemeName(idx): | |||
| if idx == Theme.THEME_MODERN_DARK: | |||
| return "Modern Dark" | |||
| elif idx == Theme.THEME_MODERN_DARK_TINY: | |||
| if idx == Theme.THEME_MODERN_DARK_TINY: | |||
| return "Modern Dark (Tiny)" | |||
| elif idx == Theme.THEME_MODERN_LIGHT: | |||
| if idx == Theme.THEME_MODERN_LIGHT: | |||
| return "Modern Light" | |||
| elif idx == Theme.THEME_CLASSIC_DARK: | |||
| if idx == Theme.THEME_CLASSIC_DARK: | |||
| return "Classic Dark" | |||
| elif idx == Theme.THEME_OOSTUDIO: | |||
| if idx == Theme.THEME_OOSTUDIO: | |||
| return "OpenOctave Studio" | |||
| else: | |||
| return "" | |||
| return "" | |||
| def getDefaultThemeName(): | |||
| return "Modern Dark" | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -0,0 +1,131 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # PatchBay Canvas engine using QGraphicsView/Scene | |||
| # Copyright (C) 2010-2019 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 doc/GPL.txt file. | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Global) | |||
| from PyQt5.QtCore import qCritical, QPointF, QTimer | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| # Imports (Custom) | |||
| from . import bool2str, canvas, CanvasBoxType | |||
| from .canvasfadeanimation import CanvasFadeAnimation | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| def CanvasGetNewGroupPos(horizontal): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasGetNewGroupPos(%s)" % bool2str(horizontal)) | |||
| new_pos = QPointF(canvas.initial_pos) | |||
| items = canvas.scene.items() | |||
| break_loop = False | |||
| while not break_loop: | |||
| break_for = False | |||
| for i, item in enumerate(items): | |||
| 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(group_id, port_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasGetFullPortName(%i, %i)" % (group_id, port_id)) | |||
| for port in canvas.port_list: | |||
| if port.group_id == group_id and 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, %i) - unable to find port" % (group_id, port_id)) | |||
| return "" | |||
| def CanvasGetPortConnectionList(group_id, port_id): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasGetPortConnectionList(%i, %i)" % (group_id, port_id)) | |||
| conn_list = [] | |||
| for connection in canvas.connection_list: | |||
| if connection.group_out_id == group_id and connection.port_out_id == port_id: | |||
| conn_list.append((connection.connection_id, connection.group_in_id, connection.port_in_id)) | |||
| elif connection.group_in_id == group_id and connection.port_in_id == port_id: | |||
| conn_list.append((connection.connection_id, connection.group_out_id, connection.port_out_id)) | |||
| return conn_list | |||
| def CanvasCallback(action, value1, value2, value_str): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode())) | |||
| canvas.callback(action, value1, value2, value_str) | |||
| def CanvasItemFX(item, show, destroy): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy))) | |||
| # Check if the item already has an animation | |||
| for animation in canvas.animation_list: | |||
| if animation.item() == item: | |||
| animation.forceStop() | |||
| canvas.animation_list.remove(animation) | |||
| del animation | |||
| break | |||
| animation = CanvasFadeAnimation(item, show) | |||
| animation.setDuration(750 if show else 500) | |||
| if show: | |||
| animation.finished.connect(canvas.qobject.AnimationFinishedShow) | |||
| else: | |||
| if destroy: | |||
| animation.finished.connect(canvas.qobject.AnimationFinishedDestroy) | |||
| else: | |||
| animation.finished.connect(canvas.qobject.AnimationFinishedHide) | |||
| canvas.animation_list.append(animation) | |||
| animation.start() | |||
| def CanvasRemoveItemFX(item): | |||
| if canvas.debug: | |||
| print("PatchCanvas::CanvasRemoveItemFX(%s)" % item) | |||
| if item.type() == CanvasBoxType: | |||
| item.removeIconFromScene() | |||
| canvas.scene.removeItem(item) | |||
| del item | |||
| QTimer.singleShot(0, canvas.scene.update) | |||
| # ------------------------------------------------------------------------------------------------------------ | |||
| @@ -1 +0,0 @@ | |||