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.

2652 lines
92KB

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