Audio plugin host https://kx.studio/carla
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.

qgraphicsembedscene.py 12KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # QGraphicsEmbedScene class, based on Qt3D C++ code
  4. # Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
  5. # Copyright (C) 2014 Filipe Coelho <falktx@falktx.com>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License as
  9. # published by the Free Software Foundation; either version 2 of
  10. # the License, or any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # For a full copy of the GNU General Public License see the doc/GPL.txt file.
  18. # ------------------------------------------------------------------------------------------------------------
  19. # Imports (Config)
  20. from carla_config import *
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Global)
  23. if config_UseQt5:
  24. from PyQt5.QtCore import pyqtSignal, pyqtSlot, qRound, Qt, QPoint, QPointF, QRectF, QSize
  25. from PyQt5.QtGui import QPainter
  26. from PyQt5.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat
  27. from PyQt5.QtWidgets import QApplication, QEvent, QGraphicsItem, QGraphicsScene
  28. else:
  29. from PyQt4.QtCore import pyqtSignal, pyqtSlot, qRound, Qt, QEvent, QObject, QPoint, QPointF, QRectF, QSize
  30. from PyQt4.QtOpenGL import QGLFramebufferObject, QGLFramebufferObjectFormat
  31. from PyQt4.QtGui import QApplication, QPainter, QGraphicsItem, QGraphicsScene
  32. # ------------------------------------------------------------------------------------------------------------
  33. # Returns the next power of two that is greater than or
  34. # equal to @a value. The @a value must be positive or the
  35. # result is undefined.
  36. #
  37. # This is a convenience function for use with GL texture
  38. # handling code.
  39. def nextPowerOfTwo(value):
  40. value -= 1
  41. value |= value >> 1
  42. value |= value >> 2
  43. value |= value >> 4
  44. value |= value >> 8
  45. value |= value >> 16
  46. value += 1
  47. return value
  48. # ------------------------------------------------------------------------------------------------------------
  49. # Widget Class
  50. class QGraphicsEmbedScene(QGraphicsScene):
  51. def __init__(self, parent):
  52. QGraphicsScene.__init__(self, parent)
  53. self.dirty = True
  54. self.fbo = None
  55. self.format = QGLFramebufferObjectFormat()
  56. self.format.setAttachment(QGLFramebufferObject.CombinedDepthStencil)
  57. self.pressedPos = QPoint()
  58. self.changed.connect(self.slot_update)
  59. self.sceneRectChanged.connect(self.slot_update)
  60. def format(self):
  61. return self.format
  62. def setFormat(self, format):
  63. self.format = format
  64. def renderToTexture(self, levelOfDetail = 1.0):
  65. # Determine the fbo size we will need.
  66. size = (self.sceneRect().size() * levelOfDetail).toSize()
  67. fboSize = nextPowerOfTwo(size)
  68. if fboSize.isEmpty():
  69. fboSize = QSize(16, 16)
  70. # Create or re-create the fbo.
  71. if self.fbo is None or self.fbo.size() != fboSize:
  72. #del self.fbo
  73. self.fbo = QGLFramebufferObject(fboSize, self.format)
  74. if not self.fbo.isValid():
  75. #del self.fbo
  76. self.fbo = None
  77. return 0
  78. self.dirty = True
  79. # Return the previous texture contents if the scene hasn't changed.
  80. if self.fbo is not None and not self.dirty:
  81. return self.fbo.texture()
  82. # Render the scene into the fbo, scaling the QPainter's view
  83. # transform up to the power-of-two fbo size.
  84. painter = QPainter(self.fbo)
  85. painter.setWindow(0, 0, size.width(), size.height())
  86. painter.setViewport(0, 0, fboSize.width(), fboSize.height())
  87. self.render(painter)
  88. painter.end()
  89. self.dirty = False
  90. return self.fbo.texture()
  91. def deliverEvent(self, event, texCoord):
  92. # Map the texture co-ordinate into "screen" co-ordinates.
  93. # Mouse move and release events can extend beyond the boundaries
  94. # of the scene, for "click and drag off-screen" operations.
  95. # Mouse press and double-click events need to be constrained.
  96. bounds = self.sceneRect()
  97. screenX = qRound(texCoord.x() * bounds.width())
  98. screenY = qRound((1.0 - texCoord.y()) * bounds.height())
  99. if event.type() in (QEvent.GraphicsSceneMousePress,
  100. QEvent.GraphicsSceneMouseDoubleClick,
  101. QEvent.MouseButtonPress,
  102. QEvent.MouseButtonDblClick):
  103. if screenX < 0:
  104. screenX = 0
  105. elif screenX >= bounds.width():
  106. screenX = qRound(bounds.width() - 1)
  107. if screenY < 0:
  108. screenY = 0
  109. elif screenY >= bounds.height():
  110. screenY = qRound(bounds.height() - 1)
  111. self.pressedPos = QPoint(screenX, screenY)
  112. # Convert the event and deliver it to the scene.
  113. eventType = event.type()
  114. if eventType in (QEvent.GraphicsSceneMouseMove,
  115. QEvent.GraphicsSceneMousePress,
  116. QEvent.GraphicsSceneMouseRelease,
  117. QEvent.GraphicsSceneMouseDoubleClick):
  118. pass
  119. #QGraphicsSceneMouseEvent *ev =
  120. #static_cast<QGraphicsSceneMouseEvent *>(event)
  121. #QGraphicsSceneMouseEvent e(ev->type())
  122. #e.setPos(QPointF(screenX, screenY))
  123. #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y()))
  124. #e.setScreenPos(QPoint(screenX, screenY))
  125. #e.setButtonDownScreenPos(ev->button(), d->pressedPos)
  126. #e.setButtonDownScenePos
  127. #(ev->button(), QPointF(d->pressedPos.x() + bounds.x(),
  128. #d->pressedPos.y() + bounds.y()))
  129. #e.setButtons(ev->buttons())
  130. #e.setButton(ev->button())
  131. #e.setModifiers(ev->modifiers())
  132. #e.setAccepted(false)
  133. #QApplication::sendEvent(this, &e)
  134. elif eventType == QEvent.GraphicsSceneWheel:
  135. pass
  136. #QGraphicsSceneWheelEvent *ev =
  137. #static_cast<QGraphicsSceneWheelEvent *>(event)
  138. #QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel)
  139. #e.setPos(QPointF(screenX, screenY))
  140. #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y()))
  141. #e.setScreenPos(QPoint(screenX, screenY))
  142. #e.setButtons(ev->buttons())
  143. #e.setModifiers(ev->modifiers())
  144. #e.setDelta(ev->delta())
  145. #e.setOrientation(ev->orientation())
  146. #e.setAccepted(false)
  147. #QApplication::sendEvent(this, &e)
  148. elif eventType in (QEvent.MouseButtonPress,
  149. QEvent.MouseButtonRelease,
  150. QEvent.MouseButtonDblClick,
  151. QEvent.MouseMove):
  152. pass
  153. #QMouseEvent *ev = static_cast<QMouseEvent *>(event)
  154. #QEvent::Type type
  155. #if (ev->type() == QEvent::MouseButtonPress)
  156. #type = QEvent::GraphicsSceneMousePress
  157. #else if (ev->type() == QEvent::MouseButtonRelease)
  158. #type = QEvent::GraphicsSceneMouseRelease
  159. #else if (ev->type() == QEvent::MouseButtonDblClick)
  160. #type = QEvent::GraphicsSceneMouseDoubleClick
  161. #else
  162. #type = QEvent::GraphicsSceneMouseMove
  163. #QGraphicsSceneMouseEvent e(type)
  164. #e.setPos(QPointF(screenX, screenY))
  165. #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y()))
  166. #e.setScreenPos(QPoint(screenX, screenY))
  167. #e.setButtonDownScreenPos(ev->button(), d->pressedPos)
  168. #e.setButtonDownScenePos
  169. #(ev->button(), QPointF(d->pressedPos.x() + bounds.x(),
  170. #d->pressedPos.y() + bounds.y()))
  171. #e.setButtons(ev->buttons())
  172. #e.setButton(ev->button())
  173. #e.setModifiers(ev->modifiers())
  174. #e.setAccepted(false)
  175. #QApplication::sendEvent(this, &e)
  176. elif eventType == QEvent.Wheel:
  177. pass
  178. #QWheelEvent *ev = static_cast<QWheelEvent *>(event)
  179. #QGraphicsSceneWheelEvent e(QEvent::GraphicsSceneWheel)
  180. #e.setPos(QPointF(screenX, screenY))
  181. #e.setScenePos(QPointF(screenX + bounds.x(), screenY + bounds.y()))
  182. #e.setScreenPos(QPoint(screenX, screenY))
  183. #e.setButtons(ev->buttons())
  184. #e.setModifiers(ev->modifiers())
  185. #e.setDelta(ev->delta())
  186. #e.setOrientation(ev->orientation())
  187. #e.setAccepted(false)
  188. #QApplication::sendEvent(this, &e)
  189. #else:
  190. # Send the event directly without any conversion.
  191. # Typically used for keyboard, focus, and enter/leave events.
  192. #QApplication.sendEvent(self, event)
  193. QApplication.sendEvent(self, event)
  194. def drawBackground(self, painter, rect):
  195. if self.backgroundBrush().style() == Qt.NoBrush:
  196. # Fill the fbo with the transparent color as there won't
  197. # be a window or graphics item drawing a previous background.
  198. painter.save()
  199. painter.setCompositionMode(QPainter.CompositionMode_Source)
  200. painter.fillRect(rect, Qt.transparent)
  201. painter.restore()
  202. else:
  203. QGraphicsScene.drawBackground(painter, rect)
  204. @pyqtSlot()
  205. def slot_update(self):
  206. self.dirty = True
  207. # ------------------------------------------------------------------------------------------------------------
  208. # PatchScene compatible class
  209. # object types
  210. CanvasBoxType = QGraphicsItem.UserType + 1
  211. CanvasIconType = QGraphicsItem.UserType + 2
  212. CanvasPortType = QGraphicsItem.UserType + 3
  213. CanvasLineType = QGraphicsItem.UserType + 4
  214. CanvasBezierLineType = QGraphicsItem.UserType + 5
  215. CanvasLineMovType = QGraphicsItem.UserType + 6
  216. CanvasBezierLineMovType = QGraphicsItem.UserType + 7
  217. class PatchScene3D(QGraphicsEmbedScene):
  218. scaleChanged = pyqtSignal(float)
  219. sceneGroupMoved = pyqtSignal(int, int, QPointF)
  220. pluginSelected = pyqtSignal(list)
  221. def __init__(self, parent, view):
  222. QGraphicsEmbedScene.__init__(self, parent)
  223. self.m_ctrl_down = False
  224. self.m_mouse_down_init = False
  225. self.m_mouse_rubberband = False
  226. self.addRubberBand()
  227. self.m_view = view
  228. #if not self.m_view:
  229. #qFatal("PatchCanvas::PatchScene() - invalid view")
  230. self.selectionChanged.connect(self.slot_selectionChanged)
  231. def addRubberBand(self):
  232. self.m_rubberband = self.addRect(QRectF(0, 0, 0, 0))
  233. self.m_rubberband.setZValue(-1)
  234. self.m_rubberband.hide()
  235. self.m_rubberband_selection = False
  236. self.m_rubberband_orig_point = QPointF(0, 0)
  237. def clear(self):
  238. QGraphicsEmbedScene.clear(self)
  239. # Re-add rubberband, that just got deleted
  240. self.addRubberBand()
  241. def fixScaleFactor(self):
  242. pass
  243. def updateTheme(self):
  244. pass
  245. def zoom_fit(self):
  246. pass
  247. def zoom_in(self):
  248. pass
  249. def zoom_out(self):
  250. pass
  251. def zoom_reset(self):
  252. pass
  253. @pyqtSlot()
  254. def slot_selectionChanged(self):
  255. items_list = self.selectedItems()
  256. if len(items_list) == 0:
  257. self.pluginSelected.emit([])
  258. return
  259. plugin_list = []
  260. for item in items_list:
  261. if item and item.isVisible():
  262. group_item = None
  263. if item.type() == CanvasBoxType:
  264. group_item = item
  265. elif item.type() == CanvasPortType:
  266. group_item = item.parentItem()
  267. #elif item.type() in (CanvasLineType, CanvasBezierLineType, CanvasLineMovType, CanvasBezierLineMovType):
  268. #plugin_list = []
  269. #break
  270. if group_item is not None and group_item.m_plugin_id >= 0:
  271. plugin_list.append(group_item.m_plugin_id)
  272. self.pluginSelected.emit(plugin_list)