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.

352 lines
13KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Custom Mini Canvas Preview, a custom Qt4 widget
  4. # Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or 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 doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. from math import floor, ceil
  23. if config_UseQt5:
  24. from PyQt5.QtCore import pyqtSignal, Qt, QRectF, QTimer, QEvent, QPoint
  25. from PyQt5.QtGui import QBrush, QColor, QCursor, QPainter, QPainterPath, QPen, QCursor, QPixmap
  26. from PyQt5.QtWidgets import QFrame, QWidget
  27. else:
  28. from PyQt4.QtCore import pyqtSignal, Qt, QRectF, QTimer, QEvent, QPoint
  29. from PyQt4.QtGui import QBrush, QColor, QCursor, QPainter, QPainterPath, QPen, QCursor, QPixmap
  30. from PyQt5.QtGui import QFrame, QWidget
  31. # ------------------------------------------------------------------------------------------------------------
  32. # Antialiasing settings
  33. from patchcanvas import options, ANTIALIASING_FULL
  34. # ------------------------------------------------------------------------------------------------------------
  35. # Static Variables
  36. iX = 0
  37. iY = 1
  38. iWidth = 2
  39. iHeight = 3
  40. MOUSE_MODE_NONE = 0
  41. MOUSE_MODE_MOVE = 1
  42. MOUSE_MODE_SCALE = 2
  43. # ------------------------------------------------------------------------------------------------------------
  44. # Widget Class
  45. class CanvasPreviewFrame(QFrame):
  46. miniCanvasMoved = pyqtSignal(float, float)
  47. # x = 2
  48. # y = 2
  49. # w = width-4
  50. # h = height-3
  51. # bounds -1 px
  52. kInternalWidth = 210-6 # -4 for width + -1px*2 bounds
  53. kInternalHeight = 162-5 # -3 for height + -1px*2 bounds
  54. kInternalRatio = kInternalWidth / kInternalHeight
  55. def __init__(self, parent):
  56. QFrame.__init__(self, parent)
  57. #self.setMinimumWidth(210)
  58. #self.setMinimumHeight(162)
  59. #self.setMaximumHeight(162)
  60. self.fRealParent = None
  61. self.fScene = None
  62. self.fRenderSource = QRectF(0.0, 0.0, 0.0, 0.0)
  63. self.fRenderTarget = QRectF(0.0, 0.0, 0.0, 0.0)
  64. self.fUseCustomPaint = False
  65. self.fFrameWidth = 0.0
  66. self.fInitialX = 0.0
  67. self.fInitialY = 0.0
  68. self.fScale = 1.0
  69. self.fViewBg = QColor(0, 0, 0)
  70. self.fViewBrush = QBrush(QColor(75, 75, 255, 30))
  71. self.fViewPen = QPen(Qt.blue, 1)
  72. self.fViewRect = [3.0, 3.0, 10.0, 10.0]
  73. self.fMouseMode = MOUSE_MODE_NONE
  74. self.fMouseLeftDown = False
  75. self.fMouseRightDown = False
  76. self.fMousePos = None
  77. def init(self, scene, realWidth, realHeight, useCustomPaint = False):
  78. realWidth,realHeight = float(realWidth),float(realHeight)
  79. self.fScene = scene
  80. self.fRenderSource = QRectF(0.0, 0.0, realWidth, realHeight)
  81. self.kInternalRatio = realWidth / realHeight
  82. self.updateStyle()
  83. if self.fUseCustomPaint != useCustomPaint:
  84. self.fUseCustomPaint = useCustomPaint
  85. self.repaint()
  86. def updateStyle(self):
  87. self.fFrameWidth = 1 if self.fUseCustomPaint else self.frameWidth()
  88. def changeEvent(self, event):
  89. if event.type() in (QEvent.StyleChange, QEvent.PaletteChange):
  90. self.updateStyle()
  91. QWidget.changeEvent(self, event)
  92. def setRealParent(self, parent):
  93. self.fRealParent = parent
  94. def setViewPosX(self, xp):
  95. self.fViewRect[iX] = xp * (self.kInternalWidth - self.fViewRect[iWidth]/self.fScale)
  96. self.update()
  97. def setViewPosY(self, yp):
  98. self.fViewRect[iY] = yp * (self.kInternalHeight - self.fViewRect[iHeight]/self.fScale)
  99. self.update()
  100. def setViewScale(self, scale):
  101. self.fScale = scale
  102. if self.fRealParent is not None:
  103. QTimer.singleShot(0, self.fRealParent.slot_miniCanvasCheckAll)
  104. def setViewSize(self, width, height):
  105. self.fViewRect[iWidth] = width * self.kInternalWidth
  106. self.fViewRect[iHeight] = height * self.kInternalHeight
  107. self.update()
  108. def setViewTheme(self, bgColor, brushColor, penColor):
  109. bg_black = bgColor.blackF()
  110. brush_black = brushColor.blackF()
  111. r0,g0,b0,a = bgColor.getRgb()
  112. r1,g1,b1,a = brushColor.getRgb()
  113. if brush_black < bg_black:
  114. self.fRubberBandBlending = 1
  115. brushColor = QColor(r1-r0, g1-g0, b1-b0, 40)
  116. elif bg_black < brush_black:
  117. self.fRubberBandBlending = -1
  118. brushColor = QColor(r0-r1, g0-g1, b0-b1, 40)
  119. else:
  120. bgColor.setAlpha(40)
  121. self.fRubberBandBlending = 0
  122. penColor.setAlpha(100)
  123. self.fViewBg = bgColor
  124. self.fViewBrush = QBrush(brushColor)
  125. self.fViewPen = QPen(penColor, 1)
  126. cur_color = "black" if bg_black < 0.5 else "white"
  127. self.fScaleCursors = ( QCursor(QPixmap(":/cursors/zoom-generic-"+cur_color+".png"), 8, 7),
  128. QCursor(QPixmap(":/cursors/zoom-in-"+cur_color+".png"), 8, 7),
  129. QCursor(QPixmap(":/cursors/zoom-out-"+cur_color+".png"), 8, 7) )
  130. def moveViewRect(self, x, y):
  131. x = float(x) - self.fInitialX
  132. y = float(y) - self.fInitialY
  133. fixPos = False
  134. rCentX = self.fViewRect[iWidth] / self.fScale / 2
  135. rCentY = self.fViewRect[iHeight] / self.fScale / 2
  136. if x < rCentX:
  137. x = rCentX
  138. fixPos = True
  139. elif x > self.kInternalWidth - rCentX:
  140. x = self.kInternalWidth - rCentX
  141. fixPos = True
  142. if y < rCentY:
  143. y = rCentY
  144. fixPos = True
  145. elif y > self.kInternalHeight - rCentY:
  146. y = self.kInternalHeight - rCentY
  147. fixPos = True
  148. if fixPos:
  149. globalPos = self.mapToGlobal(QPoint(self.fInitialX + x, self.fInitialY + y))
  150. self.cursor().setPos(globalPos)
  151. x = self.fRenderSource.width() * x / self.kInternalWidth
  152. y = self.fRenderSource.height() * y / self.kInternalHeight
  153. self.fScene.m_view.centerOn(x, y)
  154. def updateMouseMode(self, event=None):
  155. if self.fMouseLeftDown and self.fMouseRightDown:
  156. self.fMousePos = event.globalPos()
  157. self.setCursor(self.fScaleCursors[0])
  158. self.fMouseMode = MOUSE_MODE_SCALE
  159. elif self.fMouseLeftDown:
  160. self.setCursor(QCursor(Qt.SizeAllCursor))
  161. if self.fMouseMode == MOUSE_MODE_NONE:
  162. self.moveViewRect(event.x(), event.y())
  163. self.fMouseMode = MOUSE_MODE_MOVE
  164. else:
  165. self.unsetCursor()
  166. self.fMouseMode = MOUSE_MODE_NONE
  167. def mousePressEvent(self, event):
  168. if event.button() == Qt.LeftButton:
  169. self.fMouseLeftDown = True
  170. self.updateMouseMode(event)
  171. return event.accept()
  172. elif event.button() == Qt.RightButton:
  173. self.fMouseRightDown = True
  174. self.updateMouseMode(event)
  175. return event.accept()
  176. elif event.button() == Qt.MidButton:
  177. self.fMouseLeftDown = True
  178. self.fMouseRightDown = True
  179. self.updateMouseMode(event)
  180. return event.accept()
  181. QFrame.mouseMoveEvent(self, event)
  182. def mouseMoveEvent(self, event):
  183. if self.fMouseMode == MOUSE_MODE_MOVE:
  184. self.moveViewRect(event.x(), event.y())
  185. return event.accept()
  186. if self.fMouseMode == MOUSE_MODE_SCALE:
  187. dy = self.fMousePos.y() - event.globalY()
  188. if dy != 0:
  189. self.setCursor(self.fScaleCursors[1 if dy > 0 else 2])
  190. self.fScene.zoom_wheel(dy)
  191. self.cursor().setPos(self.fMousePos)
  192. QFrame.mouseMoveEvent(self, event)
  193. def mouseReleaseEvent(self, event):
  194. if event.button() == Qt.LeftButton:
  195. self.fMouseLeftDown = False
  196. self.updateMouseMode()
  197. return event.accept()
  198. elif event.button() == Qt.RightButton:
  199. self.fMouseRightDown = False
  200. self.updateMouseMode(event)
  201. return event.accept()
  202. elif event.button() == Qt.MidButton:
  203. self.fMouseLeftDown = event.buttons() & Qt.LeftButton
  204. self.fMouseRightDown = event.buttons() & Qt.RightButton
  205. self.updateMouseMode(event)
  206. return event.accept()
  207. QFrame.mouseReleaseEvent(self, event)
  208. def wheelEvent(self, event):
  209. self.fScene.zoom_wheel(event.angleDelta().y())
  210. return event.accept()
  211. def paintEvent(self, event):
  212. painter = QPainter(self)
  213. painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL))
  214. # Brightness-aware out-of-canvas shading
  215. bg_color = self.fViewBg
  216. bg_black = bg_color.black()
  217. bg_shade = -12 if bg_black < 127 else 12
  218. r,g,b,a = bg_color.getRgb()
  219. bg_color = QColor(r+bg_shade, g+bg_shade, b+bg_shade)
  220. frameWidth = self.fFrameWidth
  221. if self.fUseCustomPaint:
  222. # Inner shadow
  223. color = QColor.fromHsv(40, 0, 255-max(210, bg_color.black(), bg_black))
  224. painter.setBrush(Qt.transparent)
  225. painter.setPen(color)
  226. painter.drawRect(QRectF(0.5, 0.5, self.width()-1, self.height()-1))
  227. # Background
  228. painter.setBrush(bg_color)
  229. painter.setPen(bg_color)
  230. painter.drawRect(QRectF(1.5, 1.5, self.width()-3, self.height()-3))
  231. else:
  232. use_rounding = int(frameWidth > 1)
  233. rounding = 0.5 * use_rounding
  234. painter.setBrush(bg_color)
  235. painter.setPen(bg_color)
  236. painter.drawRoundedRect(QRectF(0.5+frameWidth, 0.5+frameWidth, self.width()-1-frameWidth*2, self.height()-1-frameWidth*2), rounding, rounding)
  237. clipPath = QPainterPath()
  238. rounding = 1.0 * use_rounding
  239. clipPath.addRoundedRect(QRectF(frameWidth, frameWidth, self.width()-frameWidth*2, self.height()-frameWidth*2), rounding, rounding)
  240. painter.setClipPath(clipPath)
  241. self.fScene.render(painter, self.fRenderTarget, self.fRenderSource, Qt.KeepAspectRatio)
  242. # Allow cursor frame to look joined with minicanvas frame
  243. painter.setClipping(False)
  244. width = self.fViewRect[iWidth]/self.fScale
  245. height = self.fViewRect[iHeight]/self.fScale
  246. # cursor
  247. lineHinting = self.fViewPen.widthF() / 2
  248. x = self.fViewRect[iX]+self.fInitialX
  249. y = self.fViewRect[iY]+self.fInitialY
  250. scr_x = floor(x)
  251. scr_y = floor(y)
  252. rect = QRectF(
  253. scr_x+lineHinting,
  254. scr_y+lineHinting,
  255. ceil(width+x-scr_x)-lineHinting*2,
  256. ceil(height+y-scr_y)-lineHinting*2 )
  257. if self.fRubberBandBlending == 1:
  258. painter.setCompositionMode(QPainter.CompositionMode_Plus)
  259. elif self.fRubberBandBlending == -1:
  260. painter.setCompositionMode(QPainter.CompositionMode_Difference)
  261. painter.setBrush(self.fViewBrush)
  262. painter.setPen(Qt.NoPen)
  263. painter.drawRect(rect)
  264. painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
  265. painter.setBrush(Qt.NoBrush)
  266. painter.setPen(self.fViewPen)
  267. painter.drawRect(rect)
  268. if self.fUseCustomPaint:
  269. event.accept()
  270. else:
  271. QFrame.paintEvent(self, event)
  272. def resizeEvent(self, event):
  273. size = event.size()
  274. width = size.width()
  275. height = size.height()
  276. extRatio = (width - self.fFrameWidth * 2) / (height - self.fFrameWidth * 2)
  277. if extRatio >= self.kInternalRatio:
  278. self.kInternalHeight = floor(height - self.fFrameWidth * 2)
  279. self.kInternalWidth = floor(self.kInternalHeight * self.kInternalRatio)
  280. self.fInitialX = floor(float(width - self.kInternalWidth) / 2.0)
  281. self.fInitialY = self.fFrameWidth
  282. else:
  283. self.kInternalWidth = floor(width - self.fFrameWidth * 2)
  284. self.kInternalHeight = floor(self.kInternalWidth / self.kInternalRatio)
  285. self.fInitialX = self.fFrameWidth
  286. self.fInitialY = floor(float(height - self.kInternalHeight) / 2.0)
  287. self.fRenderTarget = QRectF(self.fInitialX, self.fInitialY, float(self.kInternalWidth), float(self.kInternalHeight))
  288. if self.fRealParent is not None:
  289. QTimer.singleShot(0, self.fRealParent.slot_miniCanvasCheckAll)
  290. QFrame.resizeEvent(self, event)