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.

2641 lines
92KB

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