|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
-
- # PatchBay Canvas engine using QGraphicsView/Scene
- # Copyright (C) 2010-2020 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 pyqtSignal, pyqtSlot, qCritical, QT_VERSION, Qt, QPointF, QRectF, QTimer
- from PyQt5.QtGui import QCursor, QFont, QFontMetrics, QImage, QLinearGradient, QPainter, QPen
- from PyQt5.QtWidgets import QGraphicsItem, QGraphicsObject, QMenu
-
- # ------------------------------------------------------------------------------------------------------------
- # Backwards-compatible horizontalAdvance/width call, depending on Qt version
-
- def fontHorizontalAdvance(font, string):
- if QT_VERSION >= 0x51100:
- return QFontMetrics(font).horizontalAdvance(string)
- return QFontMetrics(font).width(string)
-
- # ------------------------------------------------------------------------------------------------------------
- # 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,
- ACTION_INLINE_DISPLAY,
- 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
-
- # ------------------------------------------------------------------------------------------------------------
-
- class cb_line_t(object):
- def __init__(self, line, connection_id):
- self.line = line
- self.connection_id = connection_id
-
- # ------------------------------------------------------------------------------------------------------------
-
- class CanvasBox(QGraphicsObject):
- # signals
- positionChanged = pyqtSignal(int, bool, int, int)
-
- # enums
- INLINE_DISPLAY_DISABLED = 0
- INLINE_DISPLAY_ENABLED = 1
- INLINE_DISPLAY_CACHED = 2
-
- def __init__(self, group_id, group_name, icon, parent=None):
- QGraphicsObject.__init__(self)
- self.setParentItem(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
- self.m_plugin_inline = self.INLINE_DISPLAY_DISABLED
-
- # Base Variables
- self.p_width = 50
- self.p_width_in = 0
- self.p_width_out = 0
- 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_inline_image = None
- self.m_inline_scaling = 1.0
- self.m_inline_first = True
- self.m_will_signal_pos_change = 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
- # FIXME FX on top of graphic items make them lose high-dpi
- # See https://bugreports.qt.io/browse/QTBUG-65035
- # FIXME Random crashes while using Qt's drop shadow, let's just disable it
- if False and options.eyecandy and canvas.scene.getDevicePixelRatioF() == 1.0:
- self.shadow = CanvasBoxShadow(self.toGraphicsObject())
- self.shadow.setFakeParent(self)
- self.setGraphicsEffect(self.shadow)
- else:
- self.shadow = None
-
- # Final touches
- self.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
-
- # Wait for at least 1 port
- if options.auto_hide_groups:
- self.setVisible(False)
-
- if options.auto_select_items:
- self.setAcceptHoverEvents(True)
-
- self.updatePositions()
-
- self.visibleChanged.connect(self.slot_signalPositionChangedLater)
- self.xChanged.connect(self.slot_signalPositionChangedLater)
- self.yChanged.connect(self.slot_signalPositionChangedLater)
-
- 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 redrawInlineDisplay(self):
- if self.m_plugin_inline == self.INLINE_DISPLAY_CACHED:
- self.m_plugin_inline = self.INLINE_DISPLAY_ENABLED
- self.update()
-
- def removeAsPlugin(self):
- #del self.m_inline_image
- #self.m_inline_image = None
- #self.m_inline_scaling = 1.0
-
- self.m_plugin_id = -1
- self.m_plugin_ui = False
- self.m_plugin_inline = self.INLINE_DISPLAY_DISABLED
-
- def setAsPlugin(self, plugin_id, hasUI, hasInlineDisplay):
- if hasInlineDisplay and not options.inline_displays:
- hasInlineDisplay = False
-
- if not hasInlineDisplay:
- del self.m_inline_image
- self.m_inline_image = None
- self.m_inline_scaling = 1.0
-
- self.m_plugin_id = plugin_id
- self.m_plugin_ui = hasUI
- self.m_plugin_inline = self.INLINE_DISPLAY_ENABLED if hasInlineDisplay else self.INLINE_DISPLAY_DISABLED
- self.update()
-
- 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 is not None:
- 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.blockSignals(True)
- self.setVisible(True)
- self.blockSignals(False)
-
- 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.blockSignals(True)
- self.setVisible(False)
- self.blockSignals(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 canvas.size_rect.isNull():
- return
-
- pos = self.scenePos()
- if (canvas.size_rect.contains(pos) and
- canvas.size_rect.contains(pos + QPointF(self.p_width, self.p_height))):
- return
-
- 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 = fontHorizontalAdvance(self.m_font_name, 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
- self.p_width_in = 0
- self.p_width_out = 0
- 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 = fontHorizontalAdvance(self.m_font_port, 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)
- self.p_width_in = max_in_width
- self.p_width_out = 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.width()
-
- self.repositionPorts(port_list)
-
- self.repaintLines(True)
- self.update()
-
- def repositionPorts(self, port_list = None):
- if port_list is None:
- 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)
-
- # Horizontal ports re-positioning
- inX = canvas.theme.port_offset
- outX = self.p_width - self.p_width_out - canvas.theme.port_offset - 12
- for port in port_list:
- if port.port_mode == PORT_MODE_INPUT:
- port.widget.setX(inX)
- port.widget.setPortWidth(self.p_width_in)
-
- elif port.port_mode == PORT_MODE_OUTPUT:
- port.widget.setX(outX)
- port.widget.setPortWidth(self.p_width_out)
-
- 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 triggerSignalPositionChanged(self):
- self.positionChanged.emit(self.m_group_id, self.m_splitted, self.x(), self.y())
- self.m_will_signal_pos_change = False
-
- @pyqtSlot()
- def slot_signalPositionChangedLater(self):
- if self.m_will_signal_pos_change:
- return
- self.m_will_signal_pos_change = True
- QTimer.singleShot(0, self.triggerSignalPositionChanged)
-
- def type(self):
- return CanvasBoxType
-
- def contextMenuEvent(self, event):
- event.accept()
- menu = QMenu()
-
- # Conenct menu stuff
- connMenu = QMenu("Connect", menu)
-
- our_port_types = []
- our_port_outs = {
- PORT_TYPE_AUDIO_JACK: [],
- PORT_TYPE_MIDI_JACK: [],
- PORT_TYPE_MIDI_ALSA: [],
- PORT_TYPE_PARAMETER: [],
- }
- for port in canvas.port_list:
- if port.group_id != self.m_group_id:
- continue
- if port.port_mode != PORT_MODE_OUTPUT:
- continue
- if port.port_id not in self.m_port_list_ids:
- continue
- if port.port_type not in our_port_types:
- our_port_types.append(port.port_type)
- our_port_outs[port.port_type].append((port.group_id, port.port_id))
-
- if len(our_port_types) != 0:
- act_x_conn = None
- for group in canvas.group_list:
- if self.m_group_id == group.group_id:
- continue
-
- has_ports = False
- target_ports = {
- PORT_TYPE_AUDIO_JACK: [],
- PORT_TYPE_MIDI_JACK: [],
- PORT_TYPE_MIDI_ALSA: [],
- PORT_TYPE_PARAMETER: [],
- }
-
- for port in canvas.port_list:
- if port.group_id != group.group_id:
- continue
- if port.port_mode != PORT_MODE_INPUT:
- continue
- if port.port_type not in our_port_types:
- continue
- has_ports = True
- target_ports[port.port_type].append((port.group_id, port.port_id))
-
- if not has_ports:
- continue
-
- act_x_conn = connMenu.addAction(group.group_name)
- act_x_conn.setData((our_port_outs, target_ports))
- act_x_conn.triggered.connect(canvas.qobject.PortContextMenuConnect)
-
- if act_x_conn is None:
- act_x_disc = connMenu.addAction("Nothing to connect to")
- act_x_disc.setEnabled(False)
-
- else:
- act_x_disc = connMenu.addAction("No output ports")
- act_x_disc.setEnabled(False)
-
- # Disconnect menu stuff
- 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(connMenu)
- 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
- QGraphicsObject.keyPressEvent(self, event)
-
- def hoverEnterEvent(self, event):
- if options.auto_select_items:
- if len(canvas.scene.selectedItems()) > 0:
- canvas.scene.clearSelection()
- self.setSelected(True)
- QGraphicsObject.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
-
- QGraphicsObject.mouseDoubleClickEvent(self, event)
-
- def mousePressEvent(self, event):
- if event.button() == Qt.MiddleButton or event.source() == Qt.MouseEventSynthesizedByApplication:
- event.ignore()
- return
-
- 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
-
- QGraphicsObject.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()
- QGraphicsObject.mouseMoveEvent(self, event)
-
- def mouseReleaseEvent(self, event):
- if self.m_cursor_moving:
- self.unsetCursor()
- QTimer.singleShot(0, self.fixPos)
- self.m_mouse_down = False
- self.m_cursor_moving = False
- QGraphicsObject.mouseReleaseEvent(self, event)
-
- def fixPos(self):
- self.blockSignals(True)
- self.setX(round(self.x()))
- self.setY(round(self.y()))
- self.blockSignals(False)
-
- 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 plugin inline display if supported
- self.paintInlineDisplay(painter)
-
- # 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 = fontHorizontalAdvance(self.m_font_name, 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()
-
- def paintInlineDisplay(self, painter):
- if self.m_plugin_inline == self.INLINE_DISPLAY_DISABLED:
- return
- if not options.inline_displays:
- return
-
- inwidth = self.p_width - 16 - self.p_width_in - self.p_width_out
- inheight = self.p_height - 3 - canvas.theme.box_header_height - canvas.theme.box_header_spacing - canvas.theme.port_spacing
-
- scaling = canvas.scene.getScaleFactor() * canvas.scene.getDevicePixelRatioF()
-
- if self.m_plugin_id >= 0 and self.m_plugin_id <= MAX_PLUGIN_ID_ALLOWED and (
- self.m_plugin_inline == self.INLINE_DISPLAY_ENABLED or self.m_inline_scaling != scaling):
- if self.m_inline_first:
- size = "%i:%i" % (int(50*scaling), int(50*scaling))
- else:
- size = "%i:%i" % (int(inwidth*scaling), int(inheight*scaling))
- data = canvas.callback(ACTION_INLINE_DISPLAY, self.m_plugin_id, 0, size)
- if data is None:
- return
-
- self.m_inline_image = QImage(data['data'], data['width'], data['height'], data['stride'],
- QImage.Format_ARGB32)
- self.m_inline_scaling = scaling
- self.m_plugin_inline = self.INLINE_DISPLAY_CACHED
-
- # make room for inline display, in a square shape
- if self.m_inline_first:
- self.m_inline_first = False
- aspectRatio = data['width'] / data['height']
- self.p_height = int(max(50*scaling, self.p_height))
- self.p_width += int(max(0, min((80 - 14)*scaling, (inheight-inwidth) * aspectRatio * scaling)))
- self.repositionPorts()
- self.repaintLines(True)
- self.update()
- return
-
- if self.m_inline_image is None:
- print("ERROR: inline display image is None for", self.m_plugin_id, self.m_group_name)
- return
-
- swidth = self.m_inline_image.width() / scaling
- sheight = self.m_inline_image.height() / scaling
-
- srcx = int(self.p_width_in + (self.p_width - self.p_width_in - self.p_width_out) / 2 - swidth / 2)
- srcy = int(canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1 + (inheight - sheight) / 2)
-
- painter.drawImage(QRectF(srcx, srcy, swidth, sheight), self.m_inline_image)
-
- # ------------------------------------------------------------------------------------------------------------
|