Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2755 lines
96KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # PatchBay Canvas engine using QGraphicsView/Scene
  4. # Copyright (C) 2010-2013 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the GPL.txt file
  17. # Imports (Global)
  18. from PyQt4.QtCore import pyqtSignal, pyqtSlot, qDebug, qCritical, qFatal, qWarning, Qt, QObject
  19. from PyQt4.QtCore import QAbstractAnimation, QLineF, QPointF, QRectF, QSizeF, QSettings, QTimer
  20. from PyQt4.QtGui import QColor, QLinearGradient, QPen, QBrush, QPolygonF, QPainter, QPainterPath, QPainterPathStroker
  21. from PyQt4.QtGui import QCursor, QFont, QFontMetrics
  22. from PyQt4.QtGui import QGraphicsScene, QGraphicsItem, QGraphicsLineItem, QGraphicsPathItem
  23. from PyQt4.QtGui import QGraphicsColorizeEffect, QGraphicsDropShadowEffect
  24. from PyQt4.QtGui import QInputDialog, QLineEdit, QMenu
  25. from PyQt4.QtSvg import QGraphicsSvgItem, QSvgRenderer
  26. # Imports (Theme)
  27. from patchcanvas_theme import *
  28. # ------------------------------------------------------------------------------
  29. # patchcanvas-api.h
  30. # Port Mode
  31. PORT_MODE_NULL = 0
  32. PORT_MODE_INPUT = 1
  33. PORT_MODE_OUTPUT = 2
  34. # Port Type
  35. PORT_TYPE_NULL = 0
  36. PORT_TYPE_AUDIO_JACK = 1
  37. PORT_TYPE_MIDI_JACK = 2
  38. PORT_TYPE_MIDI_A2J = 3
  39. PORT_TYPE_MIDI_ALSA = 4
  40. # Callback Action
  41. ACTION_GROUP_INFO = 0 # group_id, N, N
  42. ACTION_GROUP_RENAME = 1 # group_id, N, new_name
  43. ACTION_GROUP_SPLIT = 2 # group_id, N, N
  44. ACTION_GROUP_JOIN = 3 # group_id, N, N
  45. ACTION_PORT_INFO = 4 # port_id, N, N
  46. ACTION_PORT_RENAME = 5 # port_id, N, new_name
  47. ACTION_PORTS_CONNECT = 6 # out_id, in_id, N
  48. ACTION_PORTS_DISCONNECT = 7 # conn_id, N, N
  49. # Icon
  50. ICON_APPLICATION = 0
  51. ICON_HARDWARE = 1
  52. ICON_DISTRHO = 2
  53. ICON_FILE = 3
  54. ICON_PLUGIN = 4
  55. ICON_LADISH_ROOM = 5
  56. # Split Option
  57. SPLIT_UNDEF = 0
  58. SPLIT_NO = 1
  59. SPLIT_YES = 2
  60. # Antialiasing Option
  61. ANTIALIASING_NONE = 0
  62. ANTIALIASING_SMALL = 1
  63. ANTIALIASING_FULL = 2
  64. # Eye-Candy Option
  65. EYECANDY_NONE = 0
  66. EYECANDY_SMALL = 1
  67. EYECANDY_FULL = 2
  68. # Canvas options
  69. class options_t(object):
  70. __slots__ = [
  71. 'theme_name',
  72. 'auto_hide_groups',
  73. 'use_bezier_lines',
  74. 'antialiasing',
  75. 'eyecandy'
  76. ]
  77. # Canvas features
  78. class features_t(object):
  79. __slots__ = [
  80. 'group_info',
  81. 'group_rename',
  82. 'port_info',
  83. 'port_rename',
  84. 'handle_group_pos'
  85. ]
  86. # ------------------------------------------------------------------------------
  87. # patchcanvas.h
  88. # object types
  89. CanvasBoxType = QGraphicsItem.UserType + 1
  90. CanvasIconType = QGraphicsItem.UserType + 2
  91. CanvasPortType = QGraphicsItem.UserType + 3
  92. CanvasLineType = QGraphicsItem.UserType + 4
  93. CanvasBezierLineType = QGraphicsItem.UserType + 5
  94. CanvasLineMovType = QGraphicsItem.UserType + 6
  95. CanvasBezierLineMovType = QGraphicsItem.UserType + 7
  96. # object lists
  97. class group_dict_t(object):
  98. __slots__ = [
  99. 'group_id',
  100. 'group_name',
  101. 'split',
  102. 'icon',
  103. 'widgets'
  104. ]
  105. class port_dict_t(object):
  106. __slots__ = [
  107. 'group_id',
  108. 'port_id',
  109. 'port_name',
  110. 'port_mode',
  111. 'port_type',
  112. 'widget'
  113. ]
  114. class connection_dict_t(object):
  115. __slots__ = [
  116. 'connection_id',
  117. 'port_in_id',
  118. 'port_out_id',
  119. 'widget'
  120. ]
  121. class animation_dict_t(object):
  122. __slots__ = [
  123. 'animation',
  124. 'item'
  125. ]
  126. # Main Canvas object
  127. class Canvas(object):
  128. __slots__ = [
  129. 'scene',
  130. 'callback',
  131. 'debug',
  132. 'last_z_value',
  133. 'last_connection_id',
  134. 'initial_pos',
  135. 'size_rect',
  136. 'group_list',
  137. 'port_list',
  138. 'connection_list',
  139. 'animation_list',
  140. 'qobject',
  141. 'settings',
  142. 'theme',
  143. 'initiated'
  144. ]
  145. # ------------------------------------------------------------------------------
  146. # patchcanvas.cpp
  147. class CanvasObject(QObject):
  148. def __init__(self, parent=None):
  149. QObject.__init__(self, parent)
  150. @pyqtSlot()
  151. def AnimationIdle(self):
  152. animation = self.sender()
  153. if animation:
  154. CanvasRemoveAnimation(animation)
  155. @pyqtSlot()
  156. def AnimationHide(self):
  157. animation = self.sender()
  158. if animation:
  159. item = animation.item()
  160. if item:
  161. item.hide()
  162. CanvasRemoveAnimation(animation)
  163. @pyqtSlot()
  164. def AnimationDestroy(self):
  165. animation = self.sender()
  166. if animation:
  167. item = animation.item()
  168. if item:
  169. CanvasRemoveItemFX(item)
  170. CanvasRemoveAnimation(animation)
  171. @pyqtSlot()
  172. def PortContextMenuDisconnect(self):
  173. try:
  174. connectionId = int(self.sender().data())
  175. except:
  176. return
  177. CanvasCallback(ACTION_PORTS_DISCONNECT, connectionId, 0, "")
  178. # Global objects
  179. canvas = Canvas()
  180. canvas.qobject = None
  181. canvas.settings = None
  182. canvas.theme = None
  183. canvas.initiated = False
  184. canvas.group_list = []
  185. canvas.port_list = []
  186. canvas.connection_list = []
  187. canvas.animation_list = []
  188. options = options_t()
  189. options.theme_name = getDefaultThemeName()
  190. options.auto_hide_groups = False
  191. options.use_bezier_lines = True
  192. options.antialiasing = ANTIALIASING_SMALL
  193. options.eyecandy = EYECANDY_SMALL
  194. features = features_t()
  195. features.group_info = False
  196. features.group_rename = False
  197. features.port_info = False
  198. features.port_rename = False
  199. features.handle_group_pos = False
  200. # Internal functions
  201. def bool2str(check):
  202. return "True" if check else "False"
  203. def port_mode2str(port_mode):
  204. if port_mode == PORT_MODE_NULL:
  205. return "PORT_MODE_NULL"
  206. elif port_mode == PORT_MODE_INPUT:
  207. return "PORT_MODE_INPUT"
  208. elif port_mode == PORT_MODE_OUTPUT:
  209. return "PORT_MODE_OUTPUT"
  210. else:
  211. return "PORT_MODE_???"
  212. def port_type2str(port_type):
  213. if port_type == PORT_TYPE_NULL:
  214. return "PORT_TYPE_NULL"
  215. elif port_type == PORT_TYPE_AUDIO_JACK:
  216. return "PORT_TYPE_AUDIO_JACK"
  217. elif port_type == PORT_TYPE_MIDI_JACK:
  218. return "PORT_TYPE_MIDI_JACK"
  219. elif port_type == PORT_TYPE_MIDI_A2J:
  220. return "PORT_TYPE_MIDI_A2J"
  221. elif port_type == PORT_TYPE_MIDI_ALSA:
  222. return "PORT_TYPE_MIDI_ALSA"
  223. else:
  224. return "PORT_TYPE_???"
  225. def icon2str(icon):
  226. if icon == ICON_HARDWARE:
  227. return "ICON_HARDWARE"
  228. elif ICON_APPLICATION:
  229. return "ICON_APPLICATION"
  230. elif ICON_LADISH_ROOM:
  231. return "ICON_LADISH_ROOM"
  232. else:
  233. return "ICON_???"
  234. def split2str(split):
  235. if split == SPLIT_UNDEF:
  236. return "SPLIT_UNDEF"
  237. elif split == SPLIT_NO:
  238. return "SPLIT_NO"
  239. elif split == SPLIT_YES:
  240. return "SPLIT_YES"
  241. else:
  242. return "SPLIT_???"
  243. # PatchCanvas API
  244. def setOptions(new_options):
  245. if canvas.initiated: return
  246. options.theme_name = new_options.theme_name
  247. options.auto_hide_groups = new_options.auto_hide_groups
  248. options.use_bezier_lines = new_options.use_bezier_lines
  249. options.antialiasing = new_options.antialiasing
  250. options.eyecandy = new_options.eyecandy
  251. def setFeatures(new_features):
  252. if canvas.initiated: return
  253. features.group_info = new_features.group_info
  254. features.group_rename = new_features.group_rename
  255. features.port_info = new_features.port_info
  256. features.port_rename = new_features.port_rename
  257. features.handle_group_pos = new_features.handle_group_pos
  258. def init(appName, scene, callback, debug=False):
  259. if debug:
  260. qDebug("PatchCanvas::init(\"%s\", %s, %s, %s)" % (appName, scene, callback, bool2str(debug)))
  261. if canvas.initiated:
  262. qCritical("PatchCanvas::init() - already initiated")
  263. return
  264. if not callback:
  265. qFatal("PatchCanvas::init() - fatal error: callback not set")
  266. return
  267. canvas.scene = scene
  268. canvas.callback = callback
  269. canvas.debug = debug
  270. canvas.last_z_value = 0
  271. canvas.last_connection_id = 0
  272. canvas.initial_pos = QPointF(0, 0)
  273. canvas.size_rect = QRectF()
  274. if not canvas.qobject: canvas.qobject = CanvasObject()
  275. if not canvas.settings: canvas.settings = QSettings("falkTX", appName)
  276. if canvas.theme:
  277. del canvas.theme
  278. canvas.theme = None
  279. for i in range(Theme.THEME_MAX):
  280. this_theme_name = getThemeName(i)
  281. if this_theme_name == options.theme_name:
  282. canvas.theme = Theme(i)
  283. break
  284. if not canvas.theme:
  285. canvas.theme = Theme(getDefaultTheme())
  286. canvas.scene.updateTheme()
  287. canvas.initiated = True
  288. def clear():
  289. if canvas.debug:
  290. qDebug("PatchCanvas::clear()")
  291. group_list_ids = []
  292. port_list_ids = []
  293. connection_list_ids = []
  294. for group in canvas.group_list:
  295. group_list_ids.append(group.group_id)
  296. for port in canvas.port_list:
  297. port_list_ids.append(port.port_id)
  298. for connection in canvas.connection_list:
  299. connection_list_ids.append(connection.connection_id)
  300. for idx in connection_list_ids:
  301. disconnectPorts(idx)
  302. for idx in port_list_ids:
  303. removePort(idx)
  304. for idx in group_list_ids:
  305. removeGroup(idx)
  306. canvas.last_z_value = 0
  307. canvas.last_connection_id = 0
  308. canvas.group_list = []
  309. canvas.port_list = []
  310. canvas.connection_list = []
  311. canvas.scene.clear()
  312. canvas.initiated = False
  313. def setInitialPos(x, y):
  314. if canvas.debug:
  315. qDebug("PatchCanvas::setInitialPos(%i, %i)" % (x, y))
  316. canvas.initial_pos.setX(x)
  317. canvas.initial_pos.setY(y)
  318. def setCanvasSize(x, y, width, height):
  319. if canvas.debug:
  320. qDebug("PatchCanvas::setCanvasSize(%i, %i, %i, %i)" % (x, y, width, height))
  321. canvas.size_rect.setX(x)
  322. canvas.size_rect.setY(y)
  323. canvas.size_rect.setWidth(width)
  324. canvas.size_rect.setHeight(height)
  325. def addGroup(group_id, group_name, split=SPLIT_UNDEF, icon=ICON_APPLICATION):
  326. if canvas.debug:
  327. qDebug("PatchCanvas::addGroup(%i, %s, %s, %s)" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
  328. for group in canvas.group_list:
  329. if group.group_id == group_id:
  330. qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists" % (group_id, group_name.encode(), split2str(split), icon2str(icon)))
  331. return
  332. if split == SPLIT_UNDEF and features.handle_group_pos:
  333. split = canvas.settings.value("CanvasPositions/%s_SPLIT" % group_name, split, type=int)
  334. group_box = CanvasBox(group_id, group_name, icon)
  335. group_dict = group_dict_t()
  336. group_dict.group_id = group_id
  337. group_dict.group_name = group_name
  338. group_dict.split = bool(split == SPLIT_YES)
  339. group_dict.icon = icon
  340. group_dict.widgets = [group_box, None]
  341. if split == SPLIT_YES:
  342. group_box.setSplit(True, PORT_MODE_OUTPUT)
  343. if features.handle_group_pos:
  344. group_box.setPos(canvas.settings.value("CanvasPositions/%s_OUTPUT" % group_name, CanvasGetNewGroupPos(), type=QPointF))
  345. else:
  346. group_box.setPos(CanvasGetNewGroupPos())
  347. group_sbox = CanvasBox(group_id, group_name, icon)
  348. group_sbox.setSplit(True, PORT_MODE_INPUT)
  349. group_dict.widgets[1] = group_sbox
  350. if features.handle_group_pos:
  351. group_sbox.setPos(canvas.settings.value("CanvasPositions/%s_INPUT" % group_name, CanvasGetNewGroupPos(True), type=QPointF))
  352. else:
  353. group_sbox.setPos(CanvasGetNewGroupPos(True))
  354. canvas.last_z_value += 1
  355. group_sbox.setZValue(canvas.last_z_value)
  356. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  357. CanvasItemFX(group_sbox, True)
  358. group_sbox.checkItemPos()
  359. else:
  360. group_box.setSplit(False)
  361. if features.handle_group_pos:
  362. group_box.setPos(canvas.settings.value("CanvasPositions/%s" % group_name, CanvasGetNewGroupPos(), type=QPointF))
  363. else:
  364. # Special ladish fake-split groups
  365. horizontal = bool(icon == ICON_HARDWARE or icon == ICON_LADISH_ROOM)
  366. group_box.setPos(CanvasGetNewGroupPos(horizontal))
  367. group_box.checkItemPos()
  368. canvas.last_z_value += 1
  369. group_box.setZValue(canvas.last_z_value)
  370. canvas.group_list.append(group_dict)
  371. if options.eyecandy == EYECANDY_FULL and not options.auto_hide_groups:
  372. CanvasItemFX(group_box, True)
  373. QTimer.singleShot(0, canvas.scene.update)
  374. def removeGroup(group_id):
  375. if canvas.debug:
  376. qDebug("PatchCanvas::removeGroup(%i)" % group_id)
  377. for group in canvas.group_list:
  378. if group.group_id == group_id:
  379. item = group.widgets[0]
  380. group_name = group.group_name
  381. if group.split:
  382. s_item = group.widgets[1]
  383. if features.handle_group_pos:
  384. canvas.settings.setValue("CanvasPositions/%s_OUTPUT" % group_name, item.pos())
  385. canvas.settings.setValue("CanvasPositions/%s_INPUT" % group_name, s_item.pos())
  386. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_YES)
  387. if options.eyecandy == EYECANDY_FULL:
  388. CanvasItemFX(s_item, False, True)
  389. else:
  390. s_item.removeIconFromScene()
  391. canvas.scene.removeItem(s_item)
  392. del s_item
  393. else:
  394. if features.handle_group_pos:
  395. canvas.settings.setValue("CanvasPositions/%s" % group_name, item.pos())
  396. canvas.settings.setValue("CanvasPositions/%s_SPLIT" % group_name, SPLIT_NO)
  397. if options.eyecandy == EYECANDY_FULL:
  398. CanvasItemFX(item, False, True)
  399. else:
  400. item.removeIconFromScene()
  401. canvas.scene.removeItem(item)
  402. del item
  403. canvas.group_list.remove(group)
  404. QTimer.singleShot(0, canvas.scene.update)
  405. return
  406. qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove" % group_id)
  407. def renameGroup(group_id, new_group_name):
  408. if canvas.debug:
  409. qDebug("PatchCanvas::renameGroup(%i, %s)" % (group_id, new_group_name.encode()))
  410. for group in canvas.group_list:
  411. if group.group_id == group_id:
  412. group.group_name = new_group_name
  413. group.widgets[0].setGroupName(new_group_name)
  414. if group.split and group.widgets[1]:
  415. group.widgets[1].setGroupName(new_group_name)
  416. QTimer.singleShot(0, canvas.scene.update)
  417. return
  418. qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename" % (group_id, new_group_name.encode()))
  419. def splitGroup(group_id):
  420. if canvas.debug:
  421. qDebug("PatchCanvas::splitGroup(%i)" % group_id)
  422. item = None
  423. group_name = ""
  424. group_icon = ICON_APPLICATION
  425. ports_data = []
  426. conns_data = []
  427. # Step 1 - Store all Item data
  428. for group in canvas.group_list:
  429. if group.group_id == group_id:
  430. if group.split:
  431. qCritical("PatchCanvas::splitGroup(%i) - group is already splitted" % group_id)
  432. return
  433. item = group.widgets[0]
  434. group_name = group.group_name
  435. group_icon = group.icon
  436. break
  437. if not item:
  438. qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split" % group_id)
  439. return
  440. port_list_ids = list(item.getPortList())
  441. for port in canvas.port_list:
  442. if port.port_id in port_list_ids:
  443. port_dict = port_dict_t()
  444. port_dict.group_id = port.group_id
  445. port_dict.port_id = port.port_id
  446. port_dict.port_name = port.port_name
  447. port_dict.port_mode = port.port_mode
  448. port_dict.port_type = port.port_type
  449. port_dict.widget = None
  450. ports_data.append(port_dict)
  451. for connection in canvas.connection_list:
  452. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  453. connection_dict = connection_dict_t()
  454. connection_dict.connection_id = connection.connection_id
  455. connection_dict.port_in_id = connection.port_in_id
  456. connection_dict.port_out_id = connection.port_out_id
  457. connection_dict.widget = None
  458. conns_data.append(connection_dict)
  459. # Step 2 - Remove Item and Children
  460. for conn in conns_data:
  461. disconnectPorts(conn.connection_id)
  462. for port_id in port_list_ids:
  463. removePort(port_id)
  464. removeGroup(group_id)
  465. # Step 3 - Re-create Item, now splitted
  466. addGroup(group_id, group_name, SPLIT_YES, group_icon)
  467. for port in ports_data:
  468. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type)
  469. for conn in conns_data:
  470. connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id)
  471. QTimer.singleShot(0, canvas.scene.update)
  472. def joinGroup(group_id):
  473. if canvas.debug:
  474. qDebug("PatchCanvas::joinGroup(%i)" % group_id)
  475. item = None
  476. s_item = None
  477. group_name = ""
  478. group_icon = ICON_APPLICATION
  479. ports_data = []
  480. conns_data = []
  481. # Step 1 - Store all Item data
  482. for group in canvas.group_list:
  483. if group.group_id == group_id:
  484. if not group.split:
  485. qCritical("PatchCanvas::joinGroup(%i) - group is not splitted" % group_id)
  486. return
  487. item = group.widgets[0]
  488. s_item = group.widgets[1]
  489. group_name = group.group_name
  490. group_icon = group.icon
  491. break
  492. # FIXME
  493. if not (item and s_item):
  494. qCritical("PatchCanvas::joinGroup(%i) - unable to find groups to join" % group_id)
  495. return
  496. port_list_ids = list(item.getPortList())
  497. port_list_idss = s_item.getPortList()
  498. for port_id in port_list_idss:
  499. if port_id not in port_list_ids:
  500. port_list_ids.append(port_id)
  501. for port in canvas.port_list:
  502. if port.port_id in port_list_ids:
  503. port_dict = port_dict_t()
  504. port_dict.group_id = port.group_id
  505. port_dict.port_id = port.port_id
  506. port_dict.port_name = port.port_name
  507. port_dict.port_mode = port.port_mode
  508. port_dict.port_type = port.port_type
  509. port_dict.widget = None
  510. ports_data.append(port_dict)
  511. for connection in canvas.connection_list:
  512. if connection.port_out_id in port_list_ids or connection.port_in_id in port_list_ids:
  513. connection_dict = connection_dict_t()
  514. connection_dict.connection_id = connection.connection_id
  515. connection_dict.port_in_id = connection.port_in_id
  516. connection_dict.port_out_id = connection.port_out_id
  517. connection_dict.widget = None
  518. conns_data.append(connection_dict)
  519. # Step 2 - Remove Item and Children
  520. for conn in conns_data:
  521. disconnectPorts(conn.connection_id)
  522. for port_id in port_list_ids:
  523. removePort(port_id)
  524. removeGroup(group_id)
  525. # Step 3 - Re-create Item, now together
  526. addGroup(group_id, group_name, SPLIT_NO, group_icon)
  527. for port in ports_data:
  528. addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type)
  529. for conn in conns_data:
  530. connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id)
  531. QTimer.singleShot(0, canvas.scene.update)
  532. def getGroupPos(group_id, port_mode=PORT_MODE_OUTPUT):
  533. if canvas.debug:
  534. qDebug("PatchCanvas::getGroupPos(%i, %s)" % (group_id, port_mode2str(port_mode)))
  535. for group in canvas.group_list:
  536. if group.group_id == group_id:
  537. if group.split:
  538. if port_mode == PORT_MODE_OUTPUT:
  539. return group.widgets[0].pos()
  540. elif port_mode == PORT_MODE_INPUT:
  541. return group.widgets[1].pos()
  542. else:
  543. return QPointF(0, 0)
  544. else:
  545. return group.widgets[0].pos()
  546. qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group" % (group_id, port_mode2str(port_mode)))
  547. return QPointF(0, 0)
  548. def setGroupPos(group_id, group_pos_x, group_pos_y):
  549. setGroupPosFull(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y)
  550. def setGroupPosFull(group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i):
  551. if canvas.debug:
  552. qDebug("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)" % (group_id, group_pos_x_o, group_pos_y_o, group_pos_x_i, group_pos_y_i))
  553. for group in canvas.group_list:
  554. if group.group_id == group_id:
  555. group.widgets[0].setPos(group_pos_x_o, group_pos_y_o)
  556. if group.split and group.widgets[1]:
  557. group.widgets[1].setPos(group_pos_x_i, group_pos_y_i)
  558. QTimer.singleShot(0, canvas.scene.update)
  559. return
  560. 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))
  561. def setGroupIcon(group_id, icon):
  562. if canvas.debug:
  563. qDebug("PatchCanvas::setGroupIcon(%i, %s)" % (group_id, icon2str(icon)))
  564. for group in canvas.group_list:
  565. if group.group_id == group_id:
  566. group.icon = icon
  567. group.widgets[0].setIcon(icon)
  568. if group.split and group.widgets[1]:
  569. group.widgets[1].setIcon(icon)
  570. QTimer.singleShot(0, canvas.scene.update)
  571. return
  572. qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon" % (group_id, icon2str(icon)))
  573. def addPort(group_id, port_id, port_name, port_mode, port_type):
  574. if canvas.debug:
  575. qDebug("PatchCanvas::addPort(%i, %i, %s, %s, %s)" % (group_id, port_id, port_name.encode(), port_mode2str(port_mode), port_type2str(port_type)))
  576. for port in canvas.port_list:
  577. if port.group_id == group_id and port.port_id == port_id:
  578. 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)))
  579. return
  580. box_widget = None
  581. port_widget = None
  582. for group in canvas.group_list:
  583. if group.group_id == group_id:
  584. if group.split and group.widgets[0].getSplittedMode() != port_mode and group.widgets[1]:
  585. n = 1
  586. else:
  587. n = 0
  588. box_widget = group.widgets[n]
  589. port_widget = box_widget.addPortFromGroup(port_id, port_mode, port_type, port_name)
  590. break
  591. if not (box_widget and port_widget):
  592. 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)))
  593. return
  594. if options.eyecandy == EYECANDY_FULL:
  595. CanvasItemFX(port_widget, True)
  596. port_dict = port_dict_t()
  597. port_dict.group_id = group_id
  598. port_dict.port_id = port_id
  599. port_dict.port_name = port_name
  600. port_dict.port_mode = port_mode
  601. port_dict.port_type = port_type
  602. port_dict.widget = port_widget
  603. canvas.port_list.append(port_dict)
  604. box_widget.updatePositions()
  605. QTimer.singleShot(0, canvas.scene.update)
  606. def removePort(port_id):
  607. if canvas.debug:
  608. qDebug("PatchCanvas::removePort(%i)" % port_id)
  609. for port in canvas.port_list:
  610. if port.port_id == port_id:
  611. item = port.widget
  612. item.parentItem().removePortFromGroup(port_id)
  613. canvas.scene.removeItem(item)
  614. del item
  615. canvas.port_list.remove(port)
  616. QTimer.singleShot(0, canvas.scene.update)
  617. return
  618. qCritical("PatchCanvas::removePort(%i) - Unable to find port to remove" % port_id)
  619. def renamePort(port_id, new_port_name):
  620. if canvas.debug:
  621. qDebug("PatchCanvas::renamePort(%i, %s)" % (port_id, new_port_name.encode()))
  622. for port in canvas.port_list:
  623. if port.port_id == port_id:
  624. port.port_name = new_port_name
  625. port.widget.setPortName(new_port_name)
  626. port.widget.parentItem().updatePositions()
  627. QTimer.singleShot(0, canvas.scene.update)
  628. return
  629. qCritical("PatchCanvas::renamePort(%i, %s) - Unable to find port to rename" % (port_id, new_port_name.encode()))
  630. def connectPorts(connection_id, port_out_id, port_in_id):
  631. if canvas.debug:
  632. qDebug("PatchCanvas::connectPorts(%i, %i, %i)" % (connection_id, port_out_id, port_in_id))
  633. port_out = None
  634. port_in = None
  635. port_out_parent = None
  636. port_in_parent = None
  637. for port in canvas.port_list:
  638. if port.port_id == port_out_id:
  639. port_out = port.widget
  640. port_out_parent = port_out.parentItem()
  641. elif port.port_id == port_in_id:
  642. port_in = port.widget
  643. port_in_parent = port_in.parentItem()
  644. # FIXME
  645. if not (port_out and port_in):
  646. qCritical("PatchCanvas::connectPorts(%i, %i, %i) - unable to find ports to connect" % (connection_id, port_out_id, port_in_id))
  647. return
  648. connection_dict = connection_dict_t()
  649. connection_dict.connection_id = connection_id
  650. connection_dict.port_out_id = port_out_id
  651. connection_dict.port_in_id = port_in_id
  652. if options.use_bezier_lines:
  653. connection_dict.widget = CanvasBezierLine(port_out, port_in, None)
  654. else:
  655. connection_dict.widget = CanvasLine(port_out, port_in, None)
  656. canvas.scene.addItem(connection_dict.widget)
  657. port_out_parent.addLineFromGroup(connection_dict.widget, connection_id)
  658. port_in_parent.addLineFromGroup(connection_dict.widget, connection_id)
  659. canvas.last_z_value += 1
  660. port_out_parent.setZValue(canvas.last_z_value)
  661. port_in_parent.setZValue(canvas.last_z_value)
  662. canvas.last_z_value += 1
  663. connection_dict.widget.setZValue(canvas.last_z_value)
  664. canvas.connection_list.append(connection_dict)
  665. if options.eyecandy == EYECANDY_FULL:
  666. item = connection_dict.widget
  667. CanvasItemFX(item, True)
  668. QTimer.singleShot(0, canvas.scene.update)
  669. def disconnectPorts(connection_id):
  670. if canvas.debug:
  671. qDebug("PatchCanvas::disconnectPorts(%i)" % connection_id)
  672. port_1_id = port_2_id = 0
  673. line = None
  674. item1 = None
  675. item2 = None
  676. for connection in canvas.connection_list:
  677. if connection.connection_id == connection_id:
  678. port_1_id = connection.port_out_id
  679. port_2_id = connection.port_in_id
  680. line = connection.widget
  681. canvas.connection_list.remove(connection)
  682. break
  683. if not line:
  684. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports" % connection_id)
  685. return
  686. for port in canvas.port_list:
  687. if port.port_id == port_1_id:
  688. item1 = port.widget
  689. break
  690. if not item1:
  691. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port" % connection_id)
  692. return
  693. for port in canvas.port_list:
  694. if port.port_id == port_2_id:
  695. item2 = port.widget
  696. break
  697. if not item2:
  698. qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port" % connection_id)
  699. return
  700. item1.parentItem().removeLineFromGroup(connection_id)
  701. item2.parentItem().removeLineFromGroup(connection_id)
  702. if options.eyecandy == EYECANDY_FULL:
  703. CanvasItemFX(line, False, True)
  704. else:
  705. line.deleteFromScene()
  706. QTimer.singleShot(0, canvas.scene.update)
  707. def arrange():
  708. if canvas.debug:
  709. qDebug("PatchCanvas::arrange()")
  710. def updateZValues():
  711. if canvas.debug:
  712. qDebug("PatchCanvas::updateZValues()")
  713. for group in canvas.group_list:
  714. group.widgets[0].resetLinesZValue()
  715. if group.split and group.widgets[1]:
  716. group.widgets[1].resetLinesZValue()
  717. # Extra Internal functions
  718. def CanvasGetGroupName(group_id):
  719. if canvas.debug:
  720. qDebug("PatchCanvas::CanvasGetGroupName(%i)" % group_id)
  721. for group in canvas.group_list:
  722. if group.group_id == group_id:
  723. return group.group_name
  724. qCritical("PatchCanvas::CanvasGetGroupName(%i) - unable to find group" % group_id)
  725. return ""
  726. def CanvasGetGroupPortCount(group_id):
  727. if canvas.debug:
  728. qDebug("PatchCanvas::CanvasGetGroupPortCount(%i)" % group_id)
  729. port_count = 0
  730. for port in canvas.port_list:
  731. if port.group_id == group_id:
  732. port_count += 1
  733. return port_count
  734. def CanvasGetNewGroupPos(horizontal=False):
  735. if canvas.debug:
  736. qDebug("PatchCanvas::CanvasGetNewGroupPos(%s)" % bool2str(horizontal))
  737. new_pos = QPointF(canvas.initial_pos.x(), canvas.initial_pos.y())
  738. items = canvas.scene.items()
  739. break_loop = False
  740. while not break_loop:
  741. break_for = False
  742. for i in range(len(items)):
  743. item = items[i]
  744. if item and item.type() == CanvasBoxType:
  745. if item.sceneBoundingRect().contains(new_pos):
  746. if horizontal:
  747. new_pos += QPointF(item.boundingRect().width() + 15, 0)
  748. else:
  749. new_pos += QPointF(0, item.boundingRect().height() + 15)
  750. break_for = True
  751. break
  752. if i >= len(items) - 1 and not break_for:
  753. break_loop = True
  754. return new_pos
  755. def CanvasGetFullPortName(port_id):
  756. if canvas.debug:
  757. qDebug("PatchCanvas::CanvasGetFullPortName(%i)" % port_id)
  758. for port in canvas.port_list:
  759. if port.port_id == port_id:
  760. group_id = port.group_id
  761. for group in canvas.group_list:
  762. if group.group_id == group_id:
  763. return group.group_name + ":" + port.port_name
  764. break
  765. qCritical("PatchCanvas::CanvasGetFullPortName(%i) - unable to find port" % port_id)
  766. return ""
  767. def CanvasGetPortConnectionList(port_id):
  768. if canvas.debug:
  769. qDebug("PatchCanvas::CanvasGetPortConnectionList(%i)" % port_id)
  770. port_con_list = []
  771. for connection in canvas.connection_list:
  772. if connection.port_out_id == port_id or connection.port_in_id == port_id:
  773. port_con_list.append(connection.connection_id)
  774. return port_con_list
  775. def CanvasGetConnectedPort(connection_id, port_id):
  776. if canvas.debug:
  777. qDebug("PatchCanvas::CanvasGetConnectedPort(%i, %i)" % (connection_id, port_id))
  778. for connection in canvas.connection_list:
  779. if connection.connection_id == connection_id:
  780. if connection.port_out_id == port_id:
  781. return connection.port_in_id
  782. else:
  783. return connection.port_out_id
  784. qCritical("PatchCanvas::CanvasGetConnectedPort(%i, %i) - unable to find connection" % (connection_id, port_id))
  785. return 0
  786. def CanvasRemoveAnimation(f_animation):
  787. if canvas.debug:
  788. qDebug("PatchCanvas::CanvasRemoveAnimation(%s)" % f_animation)
  789. for animation in canvas.animation_list:
  790. if animation.animation == f_animation:
  791. canvas.animation_list.remove(animation)
  792. del animation.animation
  793. break
  794. def CanvasCallback(action, value1, value2, value_str):
  795. if canvas.debug:
  796. qDebug("PatchCanvas::CanvasCallback(%i, %i, %i, %s)" % (action, value1, value2, value_str.encode()))
  797. canvas.callback(action, value1, value2, value_str)
  798. def CanvasItemFX(item, show, destroy=False):
  799. if canvas.debug:
  800. qDebug("PatchCanvas::CanvasItemFX(%s, %s, %s)" % (item, bool2str(show), bool2str(destroy)))
  801. # Check if item already has an animationItemFX
  802. for animation in canvas.animation_list:
  803. if animation.item == item:
  804. animation.animation.stop()
  805. canvas.animation_list.remove(animation)
  806. del animation.animation
  807. break
  808. animation = CanvasFadeAnimation(item, show)
  809. animation.setDuration(750 if show else 500)
  810. animation_dict = animation_dict_t()
  811. animation_dict.animation = animation
  812. animation_dict.item = item
  813. canvas.animation_list.append(animation_dict)
  814. if show:
  815. animation.finished.connect(canvas.qobject.AnimationIdle)
  816. else:
  817. if destroy:
  818. animation.finished.connect(canvas.qobject.AnimationDestroy)
  819. else:
  820. animation.finished.connect(canvas.qobject.AnimationHide)
  821. animation.start()
  822. def CanvasRemoveItemFX(item):
  823. if canvas.debug:
  824. qDebug("PatchCanvas::CanvasRemoveItemFX(%s)" % item)
  825. if item.type() == CanvasBoxType:
  826. item.removeIconFromScene()
  827. canvas.scene.removeItem(item)
  828. elif item.type() == CanvasPortType:
  829. canvas.scene.removeItem(item)
  830. elif item.type() in (CanvasLineType, CanvasBezierLineType):
  831. pass #item.deleteFromScene()
  832. # Force deletion of item if needed
  833. if item.type() in (CanvasBoxType, CanvasPortType):
  834. del item
  835. # ------------------------------------------------------------------------------
  836. # patchscene.cpp
  837. class PatchScene(QGraphicsScene):
  838. scaleChanged = pyqtSignal(float)
  839. sceneGroupMoved = pyqtSignal(int, int, QPointF)
  840. def __init__(self, parent, view):
  841. QGraphicsScene.__init__(self, parent)
  842. self.m_ctrl_down = False
  843. self.m_mouse_down_init = False
  844. self.m_mouse_rubberband = False
  845. self.addRubberBand()
  846. self.m_view = view
  847. if not self.m_view:
  848. qFatal("PatchCanvas::PatchScene() - invalid view")
  849. def addRubberBand(self):
  850. self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0))
  851. self.m_rubberband.setZValue(-1)
  852. self.m_rubberband.hide()
  853. self.m_rubberband_selection = False
  854. self.m_rubberband_orig_point = QPointF(0, 0)
  855. def clear(self):
  856. QGraphicsScene.clear(self)
  857. # Re-add rubberband, that just got deleted
  858. self.addRubberBand()
  859. def fixScaleFactor(self):
  860. scale = self.m_view.transform().m11()
  861. if scale > 3.0:
  862. self.m_view.resetTransform()
  863. self.m_view.scale(3.0, 3.0)
  864. elif scale < 0.2:
  865. self.m_view.resetTransform()
  866. self.m_view.scale(0.2, 0.2)
  867. self.scaleChanged.emit(self.m_view.transform().m11())
  868. def updateTheme(self):
  869. self.setBackgroundBrush(canvas.theme.canvas_bg)
  870. self.m_rubberband.setPen(canvas.theme.rubberband_pen)
  871. self.m_rubberband.setBrush(canvas.theme.rubberband_brush)
  872. def zoom_fit(self):
  873. min_x = min_y = max_x = max_y = None
  874. first_value = True
  875. items_list = self.items()
  876. if len(items_list) > 0:
  877. for item in items_list:
  878. if item and item.isVisible() and item.type() == CanvasBoxType:
  879. pos = item.scenePos()
  880. rect = item.boundingRect()
  881. if first_value:
  882. min_x = pos.x()
  883. elif pos.x() < min_x:
  884. min_x = pos.x()
  885. if first_value:
  886. min_y = pos.y()
  887. elif pos.y() < min_y:
  888. min_y = pos.y()
  889. if first_value:
  890. max_x = pos.x() + rect.width()
  891. elif pos.x() + rect.width() > max_x:
  892. max_x = pos.x() + rect.width()
  893. if first_value:
  894. max_y = pos.y() + rect.height()
  895. elif pos.y() + rect.height() > max_y:
  896. max_y = pos.y() + rect.height()
  897. first_value = False
  898. if not first_value:
  899. self.m_view.fitInView(min_x, min_y, abs(max_x - min_x), abs(max_y - min_y), Qt.KeepAspectRatio)
  900. self.fixScaleFactor()
  901. def zoom_in(self):
  902. if self.m_view.transform().m11() < 3.0:
  903. self.m_view.scale(1.2, 1.2)
  904. self.scaleChanged.emit(self.m_view.transform().m11())
  905. def zoom_out(self):
  906. if self.m_view.transform().m11() > 0.2:
  907. self.m_view.scale(0.8, 0.8)
  908. self.scaleChanged.emit(self.m_view.transform().m11())
  909. def zoom_reset(self):
  910. self.m_view.resetTransform()
  911. self.scaleChanged.emit(1.0)
  912. def keyPressEvent(self, event):
  913. if not self.m_view:
  914. return event.ignore()
  915. if event.key() == Qt.Key_Control:
  916. self.m_ctrl_down = True
  917. elif event.key() == Qt.Key_Home:
  918. self.zoom_fit()
  919. return event.accept()
  920. elif self.m_ctrl_down:
  921. if event.key() == Qt.Key_Plus:
  922. self.zoom_in()
  923. return event.accept()
  924. elif event.key() == Qt.Key_Minus:
  925. self.zoom_out()
  926. return event.accept()
  927. elif event.key() == Qt.Key_1:
  928. self.zoom_reset()
  929. return event.accept()
  930. QGraphicsScene.keyPressEvent(self, event)
  931. def keyReleaseEvent(self, event):
  932. if event.key() == Qt.Key_Control:
  933. self.m_ctrl_down = False
  934. QGraphicsScene.keyReleaseEvent(self, event)
  935. def mousePressEvent(self, event):
  936. self.m_mouse_down_init = bool(event.button() == Qt.LeftButton)
  937. self.m_mouse_rubberband = False
  938. QGraphicsScene.mousePressEvent(self, event)
  939. def mouseMoveEvent(self, event):
  940. if self.m_mouse_down_init:
  941. self.m_mouse_down_init = False
  942. self.m_mouse_rubberband = bool(len(self.selectedItems()) == 0)
  943. if self.m_mouse_rubberband:
  944. if not self.m_rubberband_selection:
  945. self.m_rubberband.show()
  946. self.m_rubberband_selection = True
  947. self.m_rubberband_orig_point = event.scenePos()
  948. pos = event.scenePos()
  949. if pos.x() > self.m_rubberband_orig_point.x():
  950. x = self.m_rubberband_orig_point.x()
  951. else:
  952. x = pos.x()
  953. if pos.y() > self.m_rubberband_orig_point.y():
  954. y = self.m_rubberband_orig_point.y()
  955. else:
  956. y = pos.y()
  957. self.m_rubberband.setRect(x, y, abs(pos.x() - self.m_rubberband_orig_point.x()), abs(pos.y() - self.m_rubberband_orig_point.y()))
  958. return event.accept()
  959. QGraphicsScene.mouseMoveEvent(self, event)
  960. def mouseReleaseEvent(self, event):
  961. if self.m_rubberband_selection:
  962. items_list = self.items()
  963. if len(items_list) > 0:
  964. for item in items_list:
  965. if item and item.isVisible() and item.type() == CanvasBoxType:
  966. item_rect = item.sceneBoundingRect()
  967. item_top_left = QPointF(item_rect.x(), item_rect.y())
  968. item_bottom_right = QPointF(item_rect.x() + item_rect.width(), item_rect.y() + item_rect.height())
  969. if self.m_rubberband.contains(item_top_left) and self.m_rubberband.contains(item_bottom_right):
  970. item.setSelected(True)
  971. self.m_rubberband.hide()
  972. self.m_rubberband.setRect(0, 0, 0, 0)
  973. self.m_rubberband_selection = False
  974. else:
  975. items_list = self.selectedItems()
  976. for item in items_list:
  977. if item and item.isVisible() and item.type() == CanvasBoxType:
  978. item.checkItemPos()
  979. self.sceneGroupMoved.emit(item.getGroupId(), item.getSplittedMode(), item.scenePos())
  980. if len(items_list) > 1:
  981. canvas.scene.update()
  982. self.m_mouse_down_init = False
  983. self.m_mouse_rubberband = False
  984. QGraphicsScene.mouseReleaseEvent(self, event)
  985. def wheelEvent(self, event):
  986. if not self.m_view:
  987. return event.ignore()
  988. if self.m_ctrl_down:
  989. factor = 1.41 ** (event.delta() / 240.0)
  990. self.m_view.scale(factor, factor)
  991. self.fixScaleFactor()
  992. return event.accept()
  993. QGraphicsScene.wheelEvent(self, event)
  994. # ------------------------------------------------------------------------------
  995. # canvasfadeanimation.cpp
  996. class CanvasFadeAnimation(QAbstractAnimation):
  997. def __init__(self, item, show, parent=None):
  998. QAbstractAnimation.__init__(self, parent)
  999. self.m_show = show
  1000. self.m_duration = 0
  1001. self.m_item = item
  1002. def item(self):
  1003. return self.m_item
  1004. def setDuration(self, time):
  1005. if self.m_item.opacity() == 0 and not self.m_show:
  1006. self._duration = 0
  1007. else:
  1008. self.m_item.show()
  1009. self.m_duration = time
  1010. def duration(self):
  1011. return self.m_duration
  1012. def updateCurrentTime(self, time):
  1013. if self.m_duration == 0:
  1014. return
  1015. if self.m_show:
  1016. value = float(time) / self.m_duration
  1017. else:
  1018. value = 1.0 - (float(time) / self.m_duration)
  1019. self.m_item.setOpacity(value)
  1020. if self.m_item.type() == CanvasBoxType:
  1021. self.m_item.setShadowOpacity(value)
  1022. def updateDirection(self, direction):
  1023. pass
  1024. def updateState(self, oldState, newState):
  1025. pass
  1026. # ------------------------------------------------------------------------------
  1027. # canvasline.cpp
  1028. class CanvasLine(QGraphicsLineItem):
  1029. def __init__(self, item1, item2, parent):
  1030. QGraphicsLineItem.__init__(self, parent)
  1031. self.item1 = item1
  1032. self.item2 = item2
  1033. self.m_locked = False
  1034. self.m_lineSelected = False
  1035. self.m_mouse_over = False
  1036. self.setAcceptHoverEvents(True)
  1037. self.setGraphicsEffect(None)
  1038. self.updateLinePos()
  1039. def deleteFromScene(self):
  1040. canvas.scene.removeItem(self)
  1041. del self
  1042. def isLocked(self):
  1043. return self.m_locked
  1044. def setLocked(self, yesno):
  1045. self.m_locked = yesno
  1046. def isLineSelected(self):
  1047. return self.m_lineSelected
  1048. def setLineSelected(self, yesno):
  1049. if self.m_locked:
  1050. return
  1051. if options.eyecandy == EYECANDY_FULL:
  1052. if yesno:
  1053. self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
  1054. else:
  1055. self.setGraphicsEffect(None)
  1056. self.m_lineSelected = yesno
  1057. self.updateLineGradient()
  1058. def hoverEnterEvent(self, event):
  1059. self.m_mouse_over = True
  1060. self.setLineSelected(True)
  1061. def hoverLeaveEvent(self, event):
  1062. self.m_mouse_over = False
  1063. self.setLineSelected(False)
  1064. def mousePressEvent(self, event):
  1065. if event.button() == Qt.MidButton:
  1066. for connection in canvas.connection_list:
  1067. if (connection.port_out_id == self.item1.getPortId() and connection.port_in_id == self.item2.getPortId()):
  1068. canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
  1069. break
  1070. def updateLinePos(self):
  1071. if self.item1.getPortMode() == PORT_MODE_OUTPUT:
  1072. rect1 = self.item1.sceneBoundingRect()
  1073. rect2 = self.item2.sceneBoundingRect()
  1074. line = QLineF(rect1.right(),
  1075. rect1.top() + float(canvas.theme.port_height)/2,
  1076. rect2.left(),
  1077. rect2.top() + float(canvas.theme.port_height)/2)
  1078. self.setLine(line)
  1079. self.m_lineSelected = False
  1080. self.updateLineGradient()
  1081. def type(self):
  1082. return CanvasLineType
  1083. def updateLineGradient(self):
  1084. pos_top = self.boundingRect().top()
  1085. pos_bot = self.boundingRect().bottom()
  1086. if self.item2.scenePos().y() >= self.item1.scenePos().y():
  1087. pos1 = 0
  1088. pos2 = 1
  1089. else:
  1090. pos1 = 1
  1091. pos2 = 0
  1092. port_type1 = self.item1.getPortType()
  1093. port_type2 = self.item2.getPortType()
  1094. port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
  1095. if port_type1 == PORT_TYPE_AUDIO_JACK:
  1096. port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1097. elif port_type1 == PORT_TYPE_MIDI_JACK:
  1098. port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1099. elif port_type1 == PORT_TYPE_MIDI_A2J:
  1100. port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
  1101. elif port_type1 == PORT_TYPE_MIDI_ALSA:
  1102. port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1103. if port_type2 == PORT_TYPE_AUDIO_JACK:
  1104. port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1105. elif port_type2 == PORT_TYPE_MIDI_JACK:
  1106. port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1107. elif port_type2 == PORT_TYPE_MIDI_A2J:
  1108. port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
  1109. elif port_type2 == PORT_TYPE_MIDI_ALSA:
  1110. port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1111. pen = QPen(port_gradient, 2)
  1112. pen.setCapStyle(Qt.RoundCap)
  1113. self.setPen(pen)
  1114. def paint(self, painter, option, widget):
  1115. painter.save()
  1116. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1117. QGraphicsLineItem.paint(self, painter, option, widget)
  1118. painter.restore()
  1119. # ------------------------------------------------------------------------------
  1120. # canvasbezierline.cpp
  1121. class CanvasBezierLine(QGraphicsPathItem):
  1122. def __init__(self, item1, item2, parent):
  1123. QGraphicsPathItem.__init__(self, parent)
  1124. self.item1 = item1
  1125. self.item2 = item2
  1126. self.m_locked = False
  1127. self.m_lineSelected = False
  1128. self.m_mouse_over = False
  1129. self.setAcceptHoverEvents(True)
  1130. self.setBrush(QColor(0, 0, 0, 0))
  1131. self.setGraphicsEffect(None)
  1132. self.updateLinePos()
  1133. def deleteFromScene(self):
  1134. canvas.scene.removeItem(self)
  1135. del self
  1136. def isLocked(self):
  1137. return self.m_locked
  1138. def setLocked(self, yesno):
  1139. self.m_locked = yesno
  1140. def isLineSelected(self):
  1141. return self.m_lineSelected
  1142. def setLineSelected(self, yesno):
  1143. if self.m_locked:
  1144. return
  1145. if options.eyecandy == EYECANDY_FULL:
  1146. if yesno:
  1147. self.setGraphicsEffect(CanvasPortGlow(self.item1.getPortType(), self.toGraphicsObject()))
  1148. else:
  1149. self.setGraphicsEffect(None)
  1150. self.m_lineSelected = yesno
  1151. self.updateLineGradient()
  1152. def hoverEnterEvent(self, event):
  1153. self.m_mouse_over = True
  1154. self.setLineSelected(True)
  1155. def hoverLeaveEvent(self, event):
  1156. self.m_mouse_over = False
  1157. self.setLineSelected(False)
  1158. def mousePressEvent(self, event):
  1159. if event.button() == Qt.MidButton:
  1160. for connection in canvas.connection_list:
  1161. if (connection.port_out_id == self.item1.getPortId() and connection.port_in_id == self.item2.getPortId()):
  1162. canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
  1163. break
  1164. def updateLinePos(self):
  1165. if self.item1.getPortMode() == PORT_MODE_OUTPUT:
  1166. rect1 = self.item1.sceneBoundingRect()
  1167. rect2 = self.item2.sceneBoundingRect()
  1168. item1_x = rect1.right()
  1169. item2_x = rect2.left()
  1170. item1_y = rect1.top() + float(canvas.theme.port_height)/2
  1171. item2_y = rect2.top() + float(canvas.theme.port_height)/2
  1172. item1_new_x = item1_x + abs(item1_x - item2_x) / 2
  1173. item2_new_x = item2_x - abs(item1_x - item2_x) / 2
  1174. path = QPainterPath(QPointF(item1_x, item1_y))
  1175. path.cubicTo(item1_new_x, item1_y, item2_new_x, item2_y, item2_x, item2_y)
  1176. stroker = QPainterPathStroker()
  1177. stroker.setCapStyle(Qt.FlatCap)
  1178. stroker.setWidth(2)
  1179. self.setPath(stroker.createStroke(path))
  1180. self.m_lineSelected = False
  1181. self.updateLineGradient()
  1182. def type(self):
  1183. return CanvasBezierLineType
  1184. def updateLineGradient(self):
  1185. pos_top = self.boundingRect().top()
  1186. pos_bot = self.boundingRect().bottom()
  1187. if self.item2.scenePos().y() >= self.item1.scenePos().y():
  1188. pos1 = 0
  1189. pos2 = 1
  1190. else:
  1191. pos1 = 1
  1192. pos2 = 0
  1193. port_type1 = self.item1.getPortType()
  1194. port_type2 = self.item2.getPortType()
  1195. port_gradient = QLinearGradient(0, pos_top, 0, pos_bot)
  1196. if port_type1 == PORT_TYPE_AUDIO_JACK:
  1197. port_gradient.setColorAt(pos1, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1198. elif port_type1 == PORT_TYPE_MIDI_JACK:
  1199. port_gradient.setColorAt(pos1, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1200. elif port_type1 == PORT_TYPE_MIDI_A2J:
  1201. port_gradient.setColorAt(pos1, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
  1202. elif port_type1 == PORT_TYPE_MIDI_ALSA:
  1203. port_gradient.setColorAt(pos1, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1204. if port_type2 == PORT_TYPE_AUDIO_JACK:
  1205. port_gradient.setColorAt(pos2, canvas.theme.line_audio_jack_sel if self.m_lineSelected else canvas.theme.line_audio_jack)
  1206. elif port_type2 == PORT_TYPE_MIDI_JACK:
  1207. port_gradient.setColorAt(pos2, canvas.theme.line_midi_jack_sel if self.m_lineSelected else canvas.theme.line_midi_jack)
  1208. elif port_type2 == PORT_TYPE_MIDI_A2J:
  1209. port_gradient.setColorAt(pos2, canvas.theme.line_midi_a2j_sel if self.m_lineSelected else canvas.theme.line_midi_a2j)
  1210. elif port_type2 == PORT_TYPE_MIDI_ALSA:
  1211. port_gradient.setColorAt(pos2, canvas.theme.line_midi_alsa_sel if self.m_lineSelected else canvas.theme.line_midi_alsa)
  1212. self.setBrush(QBrush(port_gradient))
  1213. self.setPen(QPen(Qt.NoPen))
  1214. def paint(self, painter, option, widget):
  1215. painter.save()
  1216. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1217. QGraphicsPathItem.paint(self, painter, option, widget)
  1218. painter.restore()
  1219. # ------------------------------------------------------------------------------
  1220. # canvaslivemov.cpp
  1221. class CanvasLineMov(QGraphicsLineItem):
  1222. def __init__(self, port_mode, port_type, parent):
  1223. QGraphicsLineItem.__init__(self, parent)
  1224. self.m_port_mode = port_mode
  1225. self.m_port_type = port_type
  1226. # Port position doesn't change while moving around line
  1227. self.p_lineX = self.scenePos().x()
  1228. self.p_lineY = self.scenePos().y()
  1229. self.p_width = self.parentItem().getPortWidth()
  1230. if port_type == PORT_TYPE_AUDIO_JACK:
  1231. pen = QPen(canvas.theme.line_audio_jack, 2)
  1232. elif port_type == PORT_TYPE_MIDI_JACK:
  1233. pen = QPen(canvas.theme.line_midi_jack, 2)
  1234. elif port_type == PORT_TYPE_MIDI_A2J:
  1235. pen = QPen(canvas.theme.line_midi_a2j, 2)
  1236. elif port_type == PORT_TYPE_MIDI_ALSA:
  1237. pen = QPen(canvas.theme.line_midi_alsa, 2)
  1238. else:
  1239. qWarning("PatchCanvas::CanvasLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
  1240. pen = QPen(Qt.black)
  1241. pen.setCapStyle(Qt.RoundCap)
  1242. self.setPen(pen)
  1243. def deleteFromScene(self):
  1244. canvas.scene.removeItem(self)
  1245. del self
  1246. def updateLinePos(self, scenePos):
  1247. item_pos = [0, 0]
  1248. if self.m_port_mode == PORT_MODE_INPUT:
  1249. item_pos[0] = 0
  1250. item_pos[1] = float(canvas.theme.port_height)/2
  1251. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1252. item_pos[0] = self.p_width + 12
  1253. item_pos[1] = float(canvas.theme.port_height)/2
  1254. else:
  1255. return
  1256. line = QLineF(item_pos[0], item_pos[1], scenePos.x() - self.p_lineX, scenePos.y() - self.p_lineY)
  1257. self.setLine(line)
  1258. def type(self):
  1259. return CanvasLineMovType
  1260. def paint(self, painter, option, widget):
  1261. painter.save()
  1262. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1263. QGraphicsLineItem.paint(self, painter, option, widget)
  1264. painter.restore()
  1265. # ------------------------------------------------------------------------------
  1266. # canvasbezierlinemov.cpp
  1267. class CanvasBezierLineMov(QGraphicsPathItem):
  1268. def __init__(self, port_mode, port_type, parent):
  1269. QGraphicsPathItem.__init__(self, parent)
  1270. self.m_port_mode = port_mode
  1271. self.m_port_type = port_type
  1272. # Port position doesn't change while moving around line
  1273. self.p_itemX = self.scenePos().x()
  1274. self.p_itemY = self.scenePos().y()
  1275. self.p_width = self.parentItem().getPortWidth()
  1276. if port_type == PORT_TYPE_AUDIO_JACK:
  1277. pen = QPen(canvas.theme.line_audio_jack, 2)
  1278. elif port_type == PORT_TYPE_MIDI_JACK:
  1279. pen = QPen(canvas.theme.line_midi_jack, 2)
  1280. elif port_type == PORT_TYPE_MIDI_A2J:
  1281. pen = QPen(canvas.theme.line_midi_a2j, 2)
  1282. elif port_type == PORT_TYPE_MIDI_ALSA:
  1283. pen = QPen(canvas.theme.line_midi_alsa, 2)
  1284. else:
  1285. qWarning("PatchCanvas::CanvasBezierLineMov(%s, %s, %s) - invalid port type" % (port_mode2str(port_mode), port_type2str(port_type), parent))
  1286. pen = QPen(Qt.black)
  1287. pen.setCapStyle(Qt.FlatCap)
  1288. self.setBrush(QColor(0, 0, 0, 0))
  1289. self.setPen(pen)
  1290. def deleteFromScene(self):
  1291. canvas.scene.removeItem(self)
  1292. del self
  1293. def updateLinePos(self, scenePos):
  1294. if self.m_port_mode == PORT_MODE_INPUT:
  1295. old_x = 0
  1296. old_y = float(canvas.theme.port_height)/2
  1297. mid_x = abs(scenePos.x() - self.p_itemX) / 2
  1298. new_x = old_x - mid_x
  1299. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1300. old_x = self.p_width + 12
  1301. old_y = float(canvas.theme.port_height)/2
  1302. mid_x = abs(scenePos.x() - (self.p_itemX + old_x)) / 2
  1303. new_x = old_x + mid_x
  1304. else:
  1305. return
  1306. final_x = scenePos.x() - self.p_itemX
  1307. final_y = scenePos.y() - self.p_itemY
  1308. path = QPainterPath(QPointF(old_x, old_y))
  1309. path.cubicTo(new_x, old_y, new_x, final_y, final_x, final_y)
  1310. self.setPath(path)
  1311. def type(self):
  1312. return CanvasBezierLineMovType
  1313. def paint(self, painter, option, widget):
  1314. painter.save()
  1315. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing))
  1316. QGraphicsPathItem.paint(self, painter, option, widget)
  1317. painter.restore()
  1318. # ------------------------------------------------------------------------------
  1319. # canvasport.cpp
  1320. class CanvasPort(QGraphicsItem):
  1321. def __init__(self, port_id, port_name, port_mode, port_type, parent):
  1322. QGraphicsItem.__init__(self, parent)
  1323. # Save Variables, useful for later
  1324. self.m_port_id = port_id
  1325. self.m_port_mode = port_mode
  1326. self.m_port_type = port_type
  1327. self.m_port_name = port_name
  1328. # Base Variables
  1329. self.m_port_width = 15
  1330. self.m_port_height = canvas.theme.port_height
  1331. self.m_port_font = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, canvas.theme.port_font_state)
  1332. self.m_line_mov = None
  1333. self.m_hover_item = None
  1334. self.m_last_selected_state = False
  1335. self.m_mouse_down = False
  1336. self.m_mouse_over = False
  1337. self.m_cursor_moving = False
  1338. self.setFlags(QGraphicsItem.ItemIsSelectable)
  1339. self.setAcceptHoverEvents(True)
  1340. def getPortId(self):
  1341. return self.m_port_id
  1342. def getPortMode(self):
  1343. return self.m_port_mode
  1344. def getPortType(self):
  1345. return self.m_port_type
  1346. def getPortName(self):
  1347. return self.m_port_name
  1348. def getFullPortName(self):
  1349. return self.parentItem().getGroupName() + ":" + self.m_port_name
  1350. def getPortWidth(self):
  1351. return self.m_port_width
  1352. def getPortHeight(self):
  1353. return self.m_port_height
  1354. def setPortMode(self, port_mode):
  1355. self.m_port_mode = port_mode
  1356. self.update()
  1357. def setPortType(self, port_type):
  1358. self.m_port_type = port_type
  1359. self.update()
  1360. def setPortName(self, port_name):
  1361. if QFontMetrics(self.m_port_font).width(port_name) < QFontMetrics(self.m_port_font).width(self.m_port_name):
  1362. QTimer.singleShot(0, canvas.scene.update)
  1363. self.m_port_name = port_name
  1364. self.update()
  1365. def setPortWidth(self, port_width):
  1366. if port_width < self.m_port_width:
  1367. QTimer.singleShot(0, canvas.scene.update)
  1368. self.m_port_width = port_width
  1369. self.update()
  1370. def type(self):
  1371. return CanvasPortType
  1372. def hoverEnterEvent(self, event):
  1373. self.m_mouse_over = True
  1374. QGraphicsItem.hoverEnterEvent(self, event)
  1375. def hoverLeaveEvent(self, event):
  1376. self.m_mouse_over = False
  1377. QGraphicsItem.hoverLeaveEvent(self, event)
  1378. def mousePressEvent(self, event):
  1379. self.m_hover_item = None
  1380. self.m_mouse_down = bool(event.button() == Qt.LeftButton)
  1381. self.m_cursor_moving = False
  1382. QGraphicsItem.mousePressEvent(self, event)
  1383. def mouseMoveEvent(self, event):
  1384. if self.m_mouse_down:
  1385. if not self.m_cursor_moving:
  1386. self.setCursor(QCursor(Qt.CrossCursor))
  1387. self.m_cursor_moving = True
  1388. for connection in canvas.connection_list:
  1389. if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id:
  1390. connection.widget.setLocked(True)
  1391. if not self.m_line_mov:
  1392. if options.use_bezier_lines:
  1393. self.m_line_mov = CanvasBezierLineMov(self.m_port_mode, self.m_port_type, self)
  1394. else:
  1395. self.m_line_mov = CanvasLineMov(self.m_port_mode, self.m_port_type, self)
  1396. canvas.last_z_value += 1
  1397. self.m_line_mov.setZValue(canvas.last_z_value)
  1398. canvas.last_z_value += 1
  1399. self.parentItem().setZValue(canvas.last_z_value)
  1400. item = None
  1401. items = canvas.scene.items(event.scenePos(), Qt.ContainsItemShape, Qt.AscendingOrder)
  1402. for i in range(len(items)):
  1403. if items[i].type() == CanvasPortType:
  1404. if items[i] != self:
  1405. if not item:
  1406. item = items[i]
  1407. elif items[i].parentItem().zValue() > item.parentItem().zValue():
  1408. item = items[i]
  1409. if self.m_hover_item and self.m_hover_item != item:
  1410. self.m_hover_item.setSelected(False)
  1411. if item:
  1412. a2j_connection = bool(item.getPortType() == PORT_TYPE_MIDI_JACK and self.m_port_type == PORT_TYPE_MIDI_A2J) or (item.getPortType() == PORT_TYPE_MIDI_A2J and self.m_port_type == PORT_TYPE_MIDI_JACK)
  1413. if item.getPortMode() != self.m_port_mode and (item.getPortType() == self.m_port_type or a2j_connection):
  1414. item.setSelected(True)
  1415. self.m_hover_item = item
  1416. else:
  1417. self.m_hover_item = None
  1418. else:
  1419. self.m_hover_item = None
  1420. self.m_line_mov.updateLinePos(event.scenePos())
  1421. return event.accept()
  1422. QGraphicsItem.mouseMoveEvent(self, event)
  1423. def mouseReleaseEvent(self, event):
  1424. if self.m_mouse_down:
  1425. if self.m_line_mov:
  1426. self.m_line_mov.deleteFromScene()
  1427. self.m_line_mov = None
  1428. for connection in canvas.connection_list:
  1429. if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id:
  1430. connection.widget.setLocked(False)
  1431. if self.m_hover_item:
  1432. check = False
  1433. for connection in canvas.connection_list:
  1434. if ( (connection.port_out_id == self.m_port_id and connection.port_in_id == self.m_hover_item.getPortId()) or
  1435. (connection.port_out_id == self.m_hover_item.getPortId() and connection.port_in_id == self.m_port_id) ):
  1436. canvas.callback(ACTION_PORTS_DISCONNECT, connection.connection_id, 0, "")
  1437. check = True
  1438. break
  1439. if not check:
  1440. if self.m_port_mode == PORT_MODE_OUTPUT:
  1441. canvas.callback(ACTION_PORTS_CONNECT, self.m_port_id, self.m_hover_item.getPortId(), "")
  1442. else:
  1443. canvas.callback(ACTION_PORTS_CONNECT, self.m_hover_item.getPortId(), self.m_port_id, "")
  1444. canvas.scene.clearSelection()
  1445. if self.m_cursor_moving:
  1446. self.setCursor(QCursor(Qt.ArrowCursor))
  1447. self.m_hover_item = None
  1448. self.m_mouse_down = False
  1449. self.m_cursor_moving = False
  1450. QGraphicsItem.mouseReleaseEvent(self, event)
  1451. def contextMenuEvent(self, event):
  1452. canvas.scene.clearSelection()
  1453. self.setSelected(True)
  1454. menu = QMenu()
  1455. discMenu = QMenu("Disconnect", menu)
  1456. port_con_list = CanvasGetPortConnectionList(self.m_port_id)
  1457. if len(port_con_list) > 0:
  1458. for port_id in port_con_list:
  1459. port_con_id = CanvasGetConnectedPort(port_id, self.m_port_id)
  1460. act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id))
  1461. act_x_disc.setData(port_id)
  1462. act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect)
  1463. else:
  1464. act_x_disc = discMenu.addAction("No connections")
  1465. act_x_disc.setEnabled(False)
  1466. menu.addMenu(discMenu)
  1467. act_x_disc_all = menu.addAction("Disconnect &All")
  1468. act_x_sep_1 = menu.addSeparator()
  1469. act_x_info = menu.addAction("Get &Info")
  1470. act_x_rename = menu.addAction("&Rename")
  1471. if not features.port_info:
  1472. act_x_info.setVisible(False)
  1473. if not features.port_rename:
  1474. act_x_rename.setVisible(False)
  1475. if not (features.port_info and features.port_rename):
  1476. act_x_sep_1.setVisible(False)
  1477. act_selected = menu.exec_(event.screenPos())
  1478. if act_selected == act_x_disc_all:
  1479. for port_id in port_con_list:
  1480. canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "")
  1481. elif act_selected == act_x_info:
  1482. canvas.callback(ACTION_PORT_INFO, self.m_port_id, 0, "")
  1483. elif act_selected == act_x_rename:
  1484. new_name_try = QInputDialog.getText(None, "Rename Port", "New name:", QLineEdit.Normal, self.m_port_name)
  1485. if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
  1486. canvas.callback(ACTION_PORT_RENAME, self.m_port_id, 0, new_name_try[0])
  1487. event.accept()
  1488. def boundingRect(self):
  1489. return QRectF(0, 0, self.m_port_width + 12, self.m_port_height)
  1490. def paint(self, painter, option, widget):
  1491. painter.save()
  1492. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
  1493. line_width = canvas.theme.port_audio_jack_pen.widthF()
  1494. poly_locx = [0, 0, 0, 0, 0]
  1495. if self.m_port_mode == PORT_MODE_INPUT:
  1496. if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
  1497. poly_locx[0] = line_width / 2
  1498. poly_locx[1] = self.m_port_width + 5 - line_width / 2
  1499. poly_locx[2] = self.m_port_width + 12 - line_width / 2
  1500. poly_locx[3] = self.m_port_width + 5 - line_width / 2
  1501. poly_locx[4] = line_width / 2
  1502. elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
  1503. poly_locx[0] = line_width / 2
  1504. poly_locx[1] = self.m_port_width + 5 - line_width / 2
  1505. poly_locx[2] = self.m_port_width + 5 - line_width / 2
  1506. poly_locx[3] = self.m_port_width + 5 - line_width / 2
  1507. poly_locx[4] = line_width / 2
  1508. else:
  1509. qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
  1510. return
  1511. elif self.m_port_mode == PORT_MODE_OUTPUT:
  1512. if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
  1513. poly_locx[0] = self.m_port_width + 12 - line_width / 2
  1514. poly_locx[1] = 7 + line_width / 2
  1515. poly_locx[2] = 0 + line_width / 2
  1516. poly_locx[3] = 7 + line_width / 2
  1517. poly_locx[4] = self.m_port_width + 12 - line_width / 2
  1518. elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE:
  1519. poly_locx[0] = self.m_port_width + 12 - line_width / 2
  1520. poly_locx[1] = 5 + line_width / 2
  1521. poly_locx[2] = 5 + line_width / 2
  1522. poly_locx[3] = 5 + line_width / 2
  1523. poly_locx[4] = self.m_port_width + 12 - line_width / 2
  1524. else:
  1525. qCritical("PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode)
  1526. return
  1527. else:
  1528. qCritical("PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode))
  1529. return
  1530. if self.m_port_type == PORT_TYPE_AUDIO_JACK:
  1531. poly_color = canvas.theme.port_audio_jack_bg_sel if self.isSelected() else canvas.theme.port_audio_jack_bg
  1532. poly_pen = canvas.theme.port_audio_jack_pen_sel if self.isSelected() else canvas.theme.port_audio_jack_pen
  1533. text_pen = canvas.theme.port_audio_jack_text_sel if self.isSelected() else canvas.theme.port_audio_jack_text
  1534. conn_pen = canvas.theme.port_audio_jack_pen_sel
  1535. elif self.m_port_type == PORT_TYPE_MIDI_JACK:
  1536. poly_color = canvas.theme.port_midi_jack_bg_sel if self.isSelected() else canvas.theme.port_midi_jack_bg
  1537. poly_pen = canvas.theme.port_midi_jack_pen_sel if self.isSelected() else canvas.theme.port_midi_jack_pen
  1538. text_pen = canvas.theme.port_midi_jack_text_sel if self.isSelected() else canvas.theme.port_midi_jack_text
  1539. conn_pen = canvas.theme.port_midi_jack_pen_sel
  1540. elif self.m_port_type == PORT_TYPE_MIDI_A2J:
  1541. poly_color = canvas.theme.port_midi_a2j_bg_sel if self.isSelected() else canvas.theme.port_midi_a2j_bg
  1542. poly_pen = canvas.theme.port_midi_a2j_pen_sel if self.isSelected() else canvas.theme.port_midi_a2j_pen
  1543. text_pen = canvas.theme.port_midi_a2j_text_sel if self.isSelected() else canvas.theme.port_midi_a2j_text
  1544. conn_pen = canvas.theme.port_midi_a2j_pen_sel
  1545. elif self.m_port_type == PORT_TYPE_MIDI_ALSA:
  1546. poly_color = canvas.theme.port_midi_alsa_bg_sel if self.isSelected() else canvas.theme.port_midi_alsa_bg
  1547. poly_pen = canvas.theme.port_midi_alsa_pen_sel if self.isSelected() else canvas.theme.port_midi_alsa_pen
  1548. text_pen = canvas.theme.port_midi_alsa_text_sel if self.isSelected() else canvas.theme.port_midi_alsa_text
  1549. conn_pen = canvas.theme.port_midi_alsa_pen_sel
  1550. else:
  1551. qCritical("PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type))
  1552. return
  1553. path = QPainterPath()
  1554. rounding = canvas.theme.port_rounding
  1555. if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON:
  1556. polygon = QPolygonF()
  1557. polygon += QPointF(poly_locx[0], line_width / 2)
  1558. polygon += QPointF(poly_locx[1], line_width / 2)
  1559. polygon += QPointF(poly_locx[2], float(canvas.theme.port_height)/2)
  1560. polygon += QPointF(poly_locx[3], canvas.theme.port_height - line_width / 2)
  1561. polygon += QPointF(poly_locx[4], canvas.theme.port_height - line_width / 2)
  1562. polygon += QPointF(poly_locx[0], line_width / 2)
  1563. path.addPolygon(polygon)
  1564. # portRect is used later if OOStudio style is active
  1565. portRect = path.boundingRect()
  1566. else:
  1567. # same
  1568. portRect = QRectF()
  1569. portRect.setCoords(poly_locx[0], line_width / 2, poly_locx[1], canvas.theme.port_height - line_width / 2)
  1570. portRect = portRect.normalized()
  1571. rect = QRectF(portRect)
  1572. if self.m_port_mode == PORT_MODE_INPUT:
  1573. rect.adjust(-rounding, 0, 0.001, 0)
  1574. else:
  1575. rect.adjust(-0.001, 0, rounding, 0)
  1576. clipPath = QPainterPath()
  1577. clipPath.addRect(portRect)
  1578. path.addRoundedRect(rect, rounding, rounding)
  1579. path = path.intersected(clipPath)
  1580. if canvas.theme.port_bg_pixmap:
  1581. portPos = portRect.topLeft()
  1582. painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos)
  1583. else:
  1584. painter.setBrush(poly_color)
  1585. painter.setPen(poly_pen)
  1586. painter.drawPath(path)
  1587. if self.m_mouse_over and canvas.theme.port_hover:
  1588. if poly_pen.style() == Qt.NoPen:
  1589. painter.setBrush(canvas.theme.port_hover)
  1590. else:
  1591. pen = QPen(poly_pen)
  1592. pen.setColor(canvas.theme.port_hover)
  1593. painter.setPen(pen)
  1594. painter.setBrush(Qt.NoBrush)
  1595. painter.drawPath(path)
  1596. painter.setPen(text_pen)
  1597. painter.setFont(self.m_port_font)
  1598. textRect = QRectF(portRect)
  1599. if self.m_port_mode == PORT_MODE_INPUT:
  1600. textRect.setRight(poly_locx[1] - rounding)
  1601. textRect.adjust(3, 0, 0, 0)
  1602. else:
  1603. textRect.setLeft(poly_locx[1] + rounding)
  1604. textRect.adjust(0, 0, -3, 0)
  1605. painter.drawText(textRect, Qt.AlignCenter|Qt.TextDontClip, self.m_port_name)
  1606. if self.isSelected() != self.m_last_selected_state:
  1607. for connection in canvas.connection_list:
  1608. if connection.port_out_id == self.m_port_id or connection.port_in_id == self.m_port_id:
  1609. connection.widget.setLineSelected(self.isSelected())
  1610. if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap:
  1611. painter.setPen(QPen(conn_pen.brush(), 2))
  1612. if self.m_port_mode == PORT_MODE_INPUT:
  1613. painter.drawLine(portRect.left(), portRect.top()+line_width/2,
  1614. portRect.left(), portRect.bottom()-line_width/2)
  1615. else:
  1616. painter.drawLine(portRect.right(), portRect.top()+line_width/2,
  1617. portRect.right(), portRect.bottom()-line_width/2)
  1618. self.m_last_selected_state = self.isSelected()
  1619. painter.restore()
  1620. # ------------------------------------------------------------------------------
  1621. # canvasbox.cpp
  1622. class cb_line_t(object):
  1623. __slots__ = [
  1624. 'line',
  1625. 'connection_id'
  1626. ]
  1627. class CanvasBox(QGraphicsItem):
  1628. def __init__(self, group_id, group_name, icon, parent=None):
  1629. QGraphicsItem.__init__(self, parent)
  1630. # Save Variables, useful for later
  1631. self.m_group_id = group_id
  1632. self.m_group_name = group_name
  1633. # Base Variables
  1634. self.p_width = 50
  1635. self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
  1636. self.m_last_pos = QPointF()
  1637. self.m_splitted = False
  1638. self.m_splitted_mode = PORT_MODE_NULL
  1639. self.m_cursor_moving = False
  1640. self.m_forced_split = False
  1641. self.m_mouse_down = False
  1642. self.m_mouse_over = False
  1643. self.m_port_list_ids = []
  1644. self.m_connection_lines = []
  1645. # Set Font
  1646. self.m_font_name = QFont(canvas.theme.box_font_name, canvas.theme.box_font_size, canvas.theme.box_font_state)
  1647. self.m_font_port = QFont(canvas.theme.port_font_name, canvas.theme.port_font_size, canvas.theme.port_font_state)
  1648. # Icon
  1649. if canvas.theme.box_use_icon:
  1650. self.icon_svg = CanvasIcon(icon, self.m_group_name, self)
  1651. else:
  1652. self.icon_svg = None
  1653. # Shadow
  1654. if options.eyecandy:
  1655. self.shadow = CanvasBoxShadow(self.toGraphicsObject())
  1656. self.shadow.setFakeParent(self)
  1657. self.setGraphicsEffect(self.shadow)
  1658. else:
  1659. self.shadow = None
  1660. # Final touches
  1661. self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
  1662. self.setAcceptHoverEvents(True)
  1663. # Wait for at least 1 port
  1664. if options.auto_hide_groups:
  1665. self.setVisible(False)
  1666. self.updatePositions()
  1667. canvas.scene.addItem(self)
  1668. def getGroupId(self):
  1669. return self.m_group_id
  1670. def getGroupName(self):
  1671. return self.m_group_name
  1672. def isSplitted(self):
  1673. return self.m_splitted
  1674. def getSplittedMode(self):
  1675. return self.m_splitted_mode
  1676. def getPortCount(self):
  1677. return len(self.m_port_list_ids)
  1678. def getPortList(self):
  1679. return self.m_port_list_ids
  1680. def setIcon(self, icon):
  1681. if self.icon_svg:
  1682. self.icon_svg.setIcon(icon, self.m_group_name)
  1683. def setSplit(self, split, mode=PORT_MODE_NULL):
  1684. self.m_splitted = split
  1685. self.m_splitted_mode = mode
  1686. def setGroupName(self, group_name):
  1687. self.m_group_name = group_name
  1688. self.updatePositions()
  1689. def setShadowOpacity(self, opacity):
  1690. if self.shadow:
  1691. self.shadow.setOpacity(opacity)
  1692. def addPortFromGroup(self, port_id, port_mode, port_type, port_name):
  1693. if len(self.m_port_list_ids) == 0:
  1694. if options.auto_hide_groups:
  1695. if options.eyecandy == EYECANDY_FULL:
  1696. CanvasItemFX(self, True)
  1697. self.setVisible(True)
  1698. new_widget = CanvasPort(port_id, port_name, port_mode, port_type, self)
  1699. port_dict = port_dict_t()
  1700. port_dict.group_id = self.m_group_id
  1701. port_dict.port_id = port_id
  1702. port_dict.port_name = port_name
  1703. port_dict.port_mode = port_mode
  1704. port_dict.port_type = port_type
  1705. port_dict.widget = new_widget
  1706. self.m_port_list_ids.append(port_id)
  1707. return new_widget
  1708. def removePortFromGroup(self, port_id):
  1709. if port_id in self.m_port_list_ids:
  1710. self.m_port_list_ids.remove(port_id)
  1711. else:
  1712. qCritical("PatchCanvas::CanvasBox.removePort(%i) - unable to find port to remove" % port_id)
  1713. return
  1714. if len(self.m_port_list_ids) > 0:
  1715. self.updatePositions()
  1716. elif self.isVisible():
  1717. if options.auto_hide_groups:
  1718. if options.eyecandy == EYECANDY_FULL:
  1719. CanvasItemFX(self, False)
  1720. else:
  1721. self.setVisible(False)
  1722. def addLineFromGroup(self, line, connection_id):
  1723. new_cbline = cb_line_t()
  1724. new_cbline.line = line
  1725. new_cbline.connection_id = connection_id
  1726. self.m_connection_lines.append(new_cbline)
  1727. def removeLineFromGroup(self, connection_id):
  1728. for connection in self.m_connection_lines:
  1729. if connection.connection_id == connection_id:
  1730. self.m_connection_lines.remove(connection)
  1731. return
  1732. qCritical("PatchCanvas::CanvasBox.removeLineFromGroup(%i) - unable to find line to remove" % connection_id)
  1733. def checkItemPos(self):
  1734. if not canvas.size_rect.isNull():
  1735. pos = self.scenePos()
  1736. if not (canvas.size_rect.contains(pos) and canvas.size_rect.contains(pos + QPointF(self.p_width, self.p_height))):
  1737. if pos.x() < canvas.size_rect.x():
  1738. self.setPos(canvas.size_rect.x(), pos.y())
  1739. elif pos.x() + self.p_width > canvas.size_rect.width():
  1740. self.setPos(canvas.size_rect.width() - self.p_width, pos.y())
  1741. pos = self.scenePos()
  1742. if pos.y() < canvas.size_rect.y():
  1743. self.setPos(pos.x(), canvas.size_rect.y())
  1744. elif pos.y() + self.p_height > canvas.size_rect.height():
  1745. self.setPos(pos.x(), canvas.size_rect.height() - self.p_height)
  1746. def removeIconFromScene(self):
  1747. if self.icon_svg:
  1748. canvas.scene.removeItem(self.icon_svg)
  1749. def updatePositions(self):
  1750. self.prepareGeometryChange()
  1751. max_in_width = max_out_width = 0
  1752. max_in_height = max_out_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + canvas.theme.box_rounding
  1753. port_spacing = canvas.theme.port_height + canvas.theme.port_spacing
  1754. have_audio_jack_in = have_midi_jack_in = have_midi_a2j_in = have_midi_alsa_in = False
  1755. have_audio_jack_out = have_midi_jack_out = have_midi_a2j_out = have_midi_alsa_out = False
  1756. # reset box size
  1757. self.p_width = 50
  1758. self.p_height = canvas.theme.box_header_height + canvas.theme.box_header_spacing + 1
  1759. # Check Text Name size
  1760. app_name_size = QFontMetrics(self.m_font_name).width(self.m_group_name) + 30
  1761. if app_name_size > self.p_width:
  1762. self.p_width = app_name_size
  1763. # Get Port List
  1764. port_list = []
  1765. for port in canvas.port_list:
  1766. if port.port_id in self.m_port_list_ids:
  1767. port_list.append(port)
  1768. # Get Max Box Width/Height
  1769. for port in port_list:
  1770. if port.port_mode == PORT_MODE_INPUT:
  1771. max_in_height += port_spacing
  1772. size = QFontMetrics(self.m_font_port).width(port.port_name)
  1773. if size > max_in_width:
  1774. max_in_width = size
  1775. if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_in:
  1776. have_audio_jack_in = True
  1777. max_in_height += canvas.theme.port_spacingT
  1778. elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_in:
  1779. have_midi_jack_in = True
  1780. max_in_height += canvas.theme.port_spacingT
  1781. elif port.port_type == PORT_TYPE_MIDI_A2J and not have_midi_a2j_in:
  1782. have_midi_a2j_in = True
  1783. max_in_height += canvas.theme.port_spacingT
  1784. elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_in:
  1785. have_midi_alsa_in = True
  1786. max_in_height += canvas.theme.port_spacingT
  1787. elif port.port_mode == PORT_MODE_OUTPUT:
  1788. max_out_height += port_spacing
  1789. size = QFontMetrics(self.m_font_port).width(port.port_name)
  1790. if size > max_out_width:
  1791. max_out_width = size
  1792. if port.port_type == PORT_TYPE_AUDIO_JACK and not have_audio_jack_out:
  1793. have_audio_jack_out = True
  1794. max_out_height += canvas.theme.port_spacingT
  1795. elif port.port_type == PORT_TYPE_MIDI_JACK and not have_midi_jack_out:
  1796. have_midi_jack_out = True
  1797. max_out_height += canvas.theme.port_spacingT
  1798. elif port.port_type == PORT_TYPE_MIDI_A2J and not have_midi_a2j_out:
  1799. have_midi_a2j_out = True
  1800. max_out_height += canvas.theme.port_spacingT
  1801. elif port.port_type == PORT_TYPE_MIDI_ALSA and not have_midi_alsa_out:
  1802. have_midi_alsa_out = True
  1803. max_out_height += canvas.theme.port_spacingT
  1804. if (canvas.theme.port_spacingT == 0 and canvas.theme.box_rounding == 0):
  1805. max_in_height += 2
  1806. max_out_height += 2
  1807. final_width = 30 + max_in_width + max_out_width
  1808. if final_width > self.p_width:
  1809. self.p_width = final_width
  1810. if max_in_height > self.p_height:
  1811. self.p_height = max_in_height
  1812. if max_out_height > self.p_height:
  1813. self.p_height = max_out_height
  1814. # Remove bottom space
  1815. self.p_height -= canvas.theme.port_spacingT
  1816. if canvas.theme.box_header_spacing > 0:
  1817. if len(port_list) == 0:
  1818. self.p_height -= canvas.theme.box_header_spacing
  1819. else:
  1820. self.p_height -= canvas.theme.box_header_spacing/2
  1821. last_in_pos = last_out_pos = canvas.theme.box_header_height + canvas.theme.box_header_spacing
  1822. last_in_type = last_out_type = PORT_TYPE_NULL
  1823. # Re-position ports, AUDIO_JACK
  1824. for port in port_list:
  1825. if port.port_type == PORT_TYPE_AUDIO_JACK:
  1826. if port.port_mode == PORT_MODE_INPUT:
  1827. port.widget.setPos(QPointF(canvas.theme.port_offset, last_in_pos))
  1828. port.widget.setPortWidth(max_in_width)
  1829. last_in_pos += port_spacing
  1830. last_in_type = port.port_type
  1831. elif port.port_mode == PORT_MODE_OUTPUT:
  1832. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 12, last_out_pos))
  1833. port.widget.setPortWidth(max_out_width)
  1834. last_out_pos += port_spacing
  1835. last_out_type = port.port_type
  1836. # Re-position ports, MIDI_JACK
  1837. for port in port_list:
  1838. if port.port_type == PORT_TYPE_MIDI_JACK:
  1839. if port.port_mode == PORT_MODE_INPUT:
  1840. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1841. last_in_pos += canvas.theme.port_spacingT
  1842. port.widget.setPos(QPointF(canvas.theme.port_offset, last_in_pos))
  1843. port.widget.setPortWidth(max_in_width)
  1844. last_in_pos += port_spacing
  1845. last_in_type = port.port_type
  1846. elif port.port_mode == PORT_MODE_OUTPUT:
  1847. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1848. last_out_pos += canvas.theme.port_spacingT
  1849. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 12, last_out_pos))
  1850. port.widget.setPortWidth(max_out_width)
  1851. last_out_pos += port_spacing
  1852. last_out_type = port.port_type
  1853. # Re-position ports, MIDI_A2J
  1854. for port in port_list:
  1855. if port.port_type == PORT_TYPE_MIDI_A2J:
  1856. if port.port_mode == PORT_MODE_INPUT:
  1857. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1858. last_in_pos += canvas.theme.port_spacingT
  1859. port.widget.setPos(QPointF(canvas.theme.port_offset, last_in_pos))
  1860. port.widget.setPortWidth(max_in_width)
  1861. last_in_pos += port_spacing
  1862. last_in_type = port.port_type
  1863. elif port.port_mode == PORT_MODE_OUTPUT:
  1864. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1865. last_out_pos += canvas.theme.port_spacingT
  1866. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 12, last_out_pos))
  1867. port.widget.setPortWidth(max_out_width)
  1868. last_out_pos += port_spacing
  1869. last_out_type = port.port_type
  1870. # Re-position ports, MIDI_ALSA
  1871. for port in port_list:
  1872. if port.port_type == PORT_TYPE_MIDI_ALSA:
  1873. if port.port_mode == PORT_MODE_INPUT:
  1874. if last_in_type != PORT_TYPE_NULL and port.port_type != last_in_type:
  1875. last_in_pos += canvas.theme.port_spacingT
  1876. port.widget.setPos(QPointF(canvas.theme.port_offset, last_in_pos))
  1877. port.widget.setPortWidth(max_in_width)
  1878. last_in_pos += port_spacing
  1879. last_in_type = port.port_type
  1880. elif port.port_mode == PORT_MODE_OUTPUT:
  1881. if last_out_type != PORT_TYPE_NULL and port.port_type != last_out_type:
  1882. last_out_pos += canvas.theme.port_spacingT
  1883. port.widget.setPos(QPointF(self.p_width - max_out_width - canvas.theme.port_offset - 12, last_out_pos))
  1884. port.widget.setPortWidth(max_out_width)
  1885. last_out_pos += port_spacing
  1886. last_out_type = port.port_type
  1887. self.repaintLines(True)
  1888. self.update()
  1889. def repaintLines(self, forced=False):
  1890. if self.pos() != self.m_last_pos or forced:
  1891. for connection in self.m_connection_lines:
  1892. connection.line.updateLinePos()
  1893. self.m_last_pos = self.pos()
  1894. def resetLinesZValue(self):
  1895. for connection in canvas.connection_list:
  1896. if connection.port_out_id in self.m_port_list_ids and connection.port_in_id in self.m_port_list_ids:
  1897. z_value = canvas.last_z_value
  1898. else:
  1899. z_value = canvas.last_z_value - 1
  1900. connection.widget.setZValue(z_value)
  1901. def type(self):
  1902. return CanvasBoxType
  1903. def contextMenuEvent(self, event):
  1904. menu = QMenu()
  1905. discMenu = QMenu("Disconnect", menu)
  1906. port_con_list = []
  1907. port_con_list_ids = []
  1908. for port_id in self.m_port_list_ids:
  1909. tmp_port_con_list = CanvasGetPortConnectionList(port_id)
  1910. for port_con_id in tmp_port_con_list:
  1911. if port_con_id not in port_con_list:
  1912. port_con_list.append(port_con_id)
  1913. port_con_list_ids.append(port_id)
  1914. if len(port_con_list) > 0:
  1915. for i in range(len(port_con_list)):
  1916. port_con_id = CanvasGetConnectedPort(port_con_list[i], port_con_list_ids[i])
  1917. act_x_disc = discMenu.addAction(CanvasGetFullPortName(port_con_id))
  1918. act_x_disc.setData(port_con_list[i])
  1919. act_x_disc.triggered.connect(canvas.qobject.PortContextMenuDisconnect)
  1920. else:
  1921. act_x_disc = discMenu.addAction("No connections")
  1922. act_x_disc.setEnabled(False)
  1923. menu.addMenu(discMenu)
  1924. act_x_disc_all = menu.addAction("Disconnect &All")
  1925. act_x_sep1 = menu.addSeparator()
  1926. act_x_info = menu.addAction("&Info")
  1927. act_x_rename = menu.addAction("&Rename")
  1928. act_x_sep2 = menu.addSeparator()
  1929. act_x_split_join = menu.addAction("Join" if self.m_splitted else "Split")
  1930. if not features.group_info:
  1931. act_x_info.setVisible(False)
  1932. if not features.group_rename:
  1933. act_x_rename.setVisible(False)
  1934. if not (features.group_info and features.group_rename):
  1935. act_x_sep1.setVisible(False)
  1936. haveIns = haveOuts = False
  1937. for port in canvas.port_list:
  1938. if port.port_id in self.m_port_list_ids:
  1939. if port.port_mode == PORT_MODE_INPUT:
  1940. haveIns = True
  1941. elif port.port_mode == PORT_MODE_OUTPUT:
  1942. haveOuts = True
  1943. if not (self.m_splitted or bool(haveIns and haveOuts)):
  1944. act_x_sep2.setVisible(False)
  1945. act_x_split_join.setVisible(False)
  1946. act_selected = menu.exec_(event.screenPos())
  1947. if act_selected == act_x_disc_all:
  1948. for port_id in port_con_list:
  1949. canvas.callback(ACTION_PORTS_DISCONNECT, port_id, 0, "")
  1950. elif act_selected == act_x_info:
  1951. canvas.callback(ACTION_GROUP_INFO, self.m_group_id, 0, "")
  1952. elif act_selected == act_x_rename:
  1953. new_name_try = QInputDialog.getText(None, "Rename Group", "New name:", QLineEdit.Normal, self.m_group_name)
  1954. if new_name_try[1] and new_name_try[0]: # 1 - bool ok, 0 - return text
  1955. canvas.callback(ACTION_GROUP_RENAME, self.m_group_id, 0, new_name_try[0])
  1956. elif act_selected == act_x_split_join:
  1957. if self.m_splitted:
  1958. canvas.callback(ACTION_GROUP_JOIN, self.m_group_id, 0, "")
  1959. else:
  1960. canvas.callback(ACTION_GROUP_SPLIT, self.m_group_id, 0, "")
  1961. event.accept()
  1962. def hoverEnterEvent(self, event):
  1963. self.m_mouse_over = True
  1964. QGraphicsItem.hoverEnterEvent(self, event)
  1965. def hoverLeaveEvent(self, event):
  1966. self.m_mouse_over = False
  1967. QGraphicsItem.hoverLeaveEvent(self, event)
  1968. def mousePressEvent(self, event):
  1969. canvas.last_z_value += 1
  1970. self.setZValue(canvas.last_z_value)
  1971. self.resetLinesZValue()
  1972. self.m_cursor_moving = False
  1973. if event.button() == Qt.RightButton:
  1974. canvas.scene.clearSelection()
  1975. self.setSelected(True)
  1976. self.m_mouse_down = False
  1977. return event.accept()
  1978. elif event.button() == Qt.LeftButton:
  1979. if self.sceneBoundingRect().contains(event.scenePos()):
  1980. self.m_mouse_down = True
  1981. else:
  1982. # Fix a weird Qt behaviour with right-click mouseMove
  1983. self.m_mouse_down = False
  1984. return event.ignore()
  1985. else:
  1986. self.m_mouse_down = False
  1987. QGraphicsItem.mousePressEvent(self, event)
  1988. def mouseMoveEvent(self, event):
  1989. if self.m_mouse_down:
  1990. if not self.m_cursor_moving:
  1991. self.setCursor(QCursor(Qt.SizeAllCursor))
  1992. self.m_cursor_moving = True
  1993. self.repaintLines()
  1994. QGraphicsItem.mouseMoveEvent(self, event)
  1995. def mouseReleaseEvent(self, event):
  1996. if self.m_cursor_moving:
  1997. self.setCursor(QCursor(Qt.ArrowCursor))
  1998. # Box with fractional coordinates has weird look, since graphics is drawen between pixels
  1999. # possible situation, when moved with scaled viewport.
  2000. self.setX(round(self.x()))
  2001. self.setY(round(self.y()))
  2002. self.m_mouse_down = False
  2003. self.m_cursor_moving = False
  2004. QGraphicsItem.mouseReleaseEvent(self, event)
  2005. def boundingRect(self):
  2006. return QRectF(0, 0, self.p_width, self.p_height)
  2007. def paint(self, painter, option, widget):
  2008. painter.save()
  2009. rect = QRectF(0, 0, self.p_width, self.p_height)
  2010. line_width = canvas.theme.box_pen.widthF()
  2011. rounding = canvas.theme.box_rounding
  2012. # Draw rectangle
  2013. if self.isSelected():
  2014. painter.setPen(canvas.theme.box_pen_sel)
  2015. else:
  2016. painter.setPen(canvas.theme.box_pen)
  2017. if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT:
  2018. box_gradient = QLinearGradient(0, 0, 0, self.p_height)
  2019. box_gradient.setColorAt(0, canvas.theme.box_bg_1)
  2020. box_gradient.setColorAt(1, canvas.theme.box_bg_2)
  2021. painter.setBrush(box_gradient)
  2022. else:
  2023. painter.setBrush(canvas.theme.box_bg_1)
  2024. rect.adjust(line_width / 2, line_width / 2, -line_width / 2, -line_width / 2)
  2025. path = QPainterPath()
  2026. path.addRoundedRect(rect, rounding, rounding)
  2027. painter.drawPath(path)
  2028. if self.m_mouse_over and canvas.theme.box_hover:
  2029. pen = QPen(canvas.theme.box_pen)
  2030. pen.setColor(canvas.theme.box_hover)
  2031. painter.setPen(pen)
  2032. painter.setBrush(Qt.NoBrush)
  2033. painter.drawPath(path)
  2034. # Draw pixmap header
  2035. rect.setHeight(canvas.theme.box_header_height)
  2036. if canvas.theme.box_header_pixmap:
  2037. painter.setPen(Qt.NoPen)
  2038. painter.setBrush(canvas.theme.box_bg_2)
  2039. rect.adjust(line_width / 2, line_width / 2, -line_width / 2, line_width / 2)
  2040. rounding -= line_width / 2
  2041. painter.drawRoundedRect(rect, rounding, rounding)
  2042. rect.adjust(1, 1, -1, 0)
  2043. rounding -= 1
  2044. clipPath = QPainterPath()
  2045. clipPath.addRoundedRect(rect, rounding, rounding)
  2046. painter.setClipPath(clipPath)
  2047. painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, QPointF(1, 1))
  2048. painter.setClipping(False)
  2049. # Draw text
  2050. painter.setFont(self.m_font_name)
  2051. if self.isSelected():
  2052. painter.setPen(canvas.theme.box_text_sel)
  2053. else:
  2054. painter.setPen(canvas.theme.box_text)
  2055. if canvas.theme.box_use_icon:
  2056. rect.setCoords(self.icon_svg.p_size.right(), self.icon_svg.p_size.top(),
  2057. self.p_width-rounding/2, self.icon_svg.p_size.bottom())
  2058. painter.drawText(rect, Qt.AlignCenter|Qt.TextDontClip, self.m_group_name)
  2059. self.repaintLines()
  2060. painter.restore()
  2061. # ------------------------------------------------------------------------------
  2062. # canvasicon.cpp
  2063. class CanvasIcon(QGraphicsSvgItem):
  2064. def __init__(self, icon, name, parent):
  2065. QGraphicsSvgItem.__init__(self, parent)
  2066. self.m_renderer = None
  2067. self.p_size = QRectF(0, 0, 0, 0)
  2068. self.m_colorFX = QGraphicsColorizeEffect(self)
  2069. self.m_colorFX.setColor(canvas.theme.box_text.color())
  2070. self.setGraphicsEffect(self.m_colorFX)
  2071. self.setIcon(icon, name)
  2072. def setIcon(self, icon, name):
  2073. name = name.lower()
  2074. icon_path = ""
  2075. if icon == ICON_APPLICATION:
  2076. self.p_size = QRectF(3, 2, 19, 18)
  2077. if "audacious" in name:
  2078. icon_path = ":/scalable/pb_audacious.svg"
  2079. self.p_size = QRectF(5, 4, 16, 16)
  2080. elif "clementine" in name:
  2081. icon_path = ":/scalable/pb_clementine.svg"
  2082. self.p_size = QRectF(5, 4, 16, 16)
  2083. elif "distrho" in name:
  2084. icon_path = ":/scalable/pb_distrho.svg"
  2085. self.p_size = QRectF(5, 4, 14, 14)
  2086. elif "jamin" in name:
  2087. icon_path = ":/scalable/pb_jamin.svg"
  2088. self.p_size = QRectF(5, 3, 16, 16)
  2089. elif "mplayer" in name:
  2090. icon_path = ":/scalable/pb_mplayer.svg"
  2091. self.p_size = QRectF(5, 4, 16, 16)
  2092. elif "vlc" in name:
  2093. icon_path = ":/scalable/pb_vlc.svg"
  2094. self.p_size = QRectF(5, 3, 16, 16)
  2095. else:
  2096. icon_path = ":/scalable/pb_generic.svg"
  2097. self.p_size = QRectF(4, 3, 16, 16)
  2098. elif icon == ICON_HARDWARE:
  2099. icon_path = ":/scalable/pb_hardware.svg"
  2100. self.p_size = QRectF(5, 2, 16, 16)
  2101. elif icon == ICON_DISTRHO:
  2102. icon_path = ":/scalable/pb_distrho.svg"
  2103. self.p_size = QRectF(5, 4, 14, 14)
  2104. elif icon == ICON_FILE:
  2105. icon_path = ":/scalable/pb_file.svg"
  2106. self.p_size = QRectF(5, 4, 12, 14)
  2107. elif icon == ICON_PLUGIN:
  2108. icon_path = ":/scalable/pb_plugin.svg"
  2109. self.p_size = QRectF(5, 4, 14, 14)
  2110. elif icon == ICON_LADISH_ROOM:
  2111. # TODO - make a unique ladish-room icon
  2112. icon_path = ":/scalable/pb_hardware.svg"
  2113. self.p_size = QRectF(5, 2, 16, 16)
  2114. else:
  2115. self.p_size = QRectF(0, 0, 0, 0)
  2116. qCritical("PatchCanvas::CanvasIcon.setIcon(%s, %s) - unsupported icon requested" % (icon2str(icon), name.encode()))
  2117. return
  2118. self.m_renderer = QSvgRenderer(icon_path, canvas.scene)
  2119. self.setSharedRenderer(self.m_renderer)
  2120. self.update()
  2121. def type(self):
  2122. return CanvasIconType
  2123. def boundingRect(self):
  2124. return self.p_size
  2125. def paint(self, painter, option, widget):
  2126. if self.m_renderer:
  2127. painter.save()
  2128. painter.setRenderHint(QPainter.Antialiasing, False)
  2129. painter.setRenderHint(QPainter.TextAntialiasing, False)
  2130. self.m_renderer.render(painter, self.p_size)
  2131. painter.restore()
  2132. else:
  2133. QGraphicsSvgItem.paint(self, painter, option, widget)
  2134. # ------------------------------------------------------------------------------
  2135. # canvasportglow.cpp
  2136. class CanvasPortGlow(QGraphicsDropShadowEffect):
  2137. def __init__(self, port_type, parent):
  2138. QGraphicsDropShadowEffect.__init__(self, parent)
  2139. self.setBlurRadius(12)
  2140. self.setOffset(0, 0)
  2141. if port_type == PORT_TYPE_AUDIO_JACK:
  2142. self.setColor(canvas.theme.line_audio_jack_glow)
  2143. elif port_type == PORT_TYPE_MIDI_JACK:
  2144. self.setColor(canvas.theme.line_midi_jack_glow)
  2145. elif port_type == PORT_TYPE_MIDI_A2J:
  2146. self.setColor(canvas.theme.line_midi_a2j_glow)
  2147. elif port_type == PORT_TYPE_MIDI_ALSA:
  2148. self.setColor(canvas.theme.line_midi_alsa_glow)
  2149. # ------------------------------------------------------------------------------
  2150. # canvasboxshadow.cpp
  2151. class CanvasBoxShadow(QGraphicsDropShadowEffect):
  2152. def __init__(self, parent):
  2153. QGraphicsDropShadowEffect.__init__(self, parent)
  2154. self.m_fakeParent = None
  2155. self.setBlurRadius(20)
  2156. self.setColor(canvas.theme.box_shadow)
  2157. self.setOffset(0, 0)
  2158. def setFakeParent(self, fakeParent):
  2159. self.m_fakeParent = fakeParent
  2160. def setOpacity(self, opacity):
  2161. color = QColor(canvas.theme.box_shadow)
  2162. color.setAlphaF(opacity)
  2163. self.setColor(color)
  2164. def draw(self, painter):
  2165. if self.m_fakeParent:
  2166. self.m_fakeParent.repaintLines()
  2167. QGraphicsDropShadowEffect.draw(self, painter)