PageRenderTime 61ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/views/sliceview/virtualhelixitem.py

https://github.com/sdouglas/cadnano2
Python | 277 lines | 201 code | 19 blank | 57 comment | 2 complexity | b91e8d74a08c256ebcb988424127598f MD5 | raw file
  1. # The MIT License
  2. #
  3. # Copyright (c) 2011 Wyss Institute at Harvard University
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. # THE SOFTWARE.
  22. #
  23. # http://www.opensource.org/licenses/mit-license.php
  24. """
  25. virtualhelixitem.py
  26. Created by Shawn on 2010-06-15.
  27. """
  28. from views import styles
  29. from model.virtualhelix import VirtualHelix
  30. from model.enum import Parity, StrandType
  31. from controllers.itemcontrollers.virtualhelixitemcontroller import VirtualHelixItemController
  32. import util
  33. # import Qt stuff into the module namespace with PySide, PyQt4 independence
  34. util.qtWrapImport('QtCore', globals(), ['Qt', 'QEvent', 'QString', 'QRectF',\
  35. 'QPointF'])
  36. util.qtWrapImport('QtGui', globals(), ['QGraphicsEllipseItem', 'QGraphicsItem',\
  37. 'QGraphicsSimpleTextItem', 'QBrush', 'QPen', \
  38. 'QPainterPath', 'QPolygonF', 'QGraphicsLineItem', 'QColor'])
  39. class VirtualHelixItem(QGraphicsEllipseItem):
  40. """
  41. The VirtualHelixItem is an individual circle that gets drawn in the SliceView
  42. as a child of the PartItem. Taken as a group, many SliceHelix
  43. instances make up the crossection of the DNAPart. Clicking on a SliceHelix
  44. adds a VirtualHelix to the DNAPart. The SliceHelix then changes appearence
  45. and paints its corresponding VirtualHelix number.
  46. """
  47. # set up default, hover, and active drawing styles
  48. _useBrush = QBrush(styles.orangefill)
  49. _usePen = QPen(styles.orangestroke, styles.SLICE_HELIX_STROKE_WIDTH)
  50. _radius = styles.SLICE_HELIX_RADIUS
  51. _outOfSlicePen = QPen(styles.lightorangestroke,\
  52. styles.SLICE_HELIX_STROKE_WIDTH)
  53. _outOfSliceBrush = QBrush(styles.lightorangefill)
  54. _rect = QRectF(0, 0, 2 * _radius, 2 * _radius)
  55. _font = styles.SLICE_NUM_FONT
  56. _ZVALUE = styles.ZSLICEHELIX+3
  57. def __init__(self, modelVirtualHelix, emptyHelixItem):
  58. """
  59. emptyHelixItem is a EmptyHelixItem that will act as a QGraphicsItem parent
  60. """
  61. super(VirtualHelixItem, self).__init__(parent=emptyHelixItem)
  62. self._virtualHelix = modelVirtualHelix
  63. self._emptyHelixItem = emptyHelixItem
  64. self.hide()
  65. # drawing related
  66. self.isHovered = False
  67. self.setAcceptsHoverEvents(True)
  68. # self.setFlag(QGraphicsItem.ItemIsSelectable)
  69. self.setZValue(self._ZVALUE)
  70. self.lastMousePressAddedBases = False
  71. self.setBrush(self._outOfSliceBrush)
  72. self.setPen(self._outOfSlicePen)
  73. self.setRect(self._rect)
  74. # handle the label specific stuff
  75. self._label = self.createLabel()
  76. self.setNumber()
  77. self.createArrow()
  78. self._controller = VirtualHelixItemController(self, modelVirtualHelix)
  79. self.show()
  80. # end def
  81. ### SIGNALS ###
  82. ### SLOTS ###
  83. def virtualHelixNumberChangedSlot(self, virtualHelix):
  84. """
  85. receives a signal containing a virtualHelix and the oldNumber
  86. as a safety check
  87. """
  88. self.setNumber()
  89. # end def
  90. def virtualHelixRemovedSlot(self, virtualHelix):
  91. self._controller.disconnectSignals()
  92. self._controller = None
  93. self._emptyHelixItem.setNotHovered()
  94. self._virtualHelix = None
  95. self._emptyHelixItem = None
  96. self.scene().removeItem(self._label)
  97. self._label = None
  98. self.scene().removeItem(self)
  99. # end def
  100. def strandAddedSlot(self, sender, strand):
  101. pass
  102. # end def
  103. ###
  104. def createLabel(self):
  105. label = QGraphicsSimpleTextItem("%d" % self._virtualHelix.number())
  106. label.setFont(self._font)
  107. label.setZValue(self._ZVALUE)
  108. label.setParentItem(self)
  109. return label
  110. # end def
  111. def createArrow(self):
  112. rad = self._radius
  113. pen = QPen()
  114. pen.setWidth(3)
  115. color = QColor(Qt.blue)
  116. color.setAlphaF(0.25)
  117. pen.setBrush(color)
  118. if self._virtualHelix.isEvenParity():
  119. arrow = QGraphicsLineItem(rad, rad, 2*rad, rad, self)
  120. else:
  121. arrow = QGraphicsLineItem(0, rad, rad, rad, self)
  122. arrow.setTransformOriginPoint(rad, rad)
  123. arrow.setZValue(400)
  124. arrow.setPen(pen)
  125. self.arrow = arrow
  126. self.arrow.hide()
  127. # end def
  128. def updateArrow(self, idx):
  129. part = self.part()
  130. tpb = part._twistPerBase
  131. angle = idx*tpb
  132. self.arrow.setRotation(angle + part._twistOffset)
  133. # end def
  134. def setNumber(self):
  135. """docstring for setNumber"""
  136. vh = self._virtualHelix
  137. num = vh.number()
  138. label = self._label
  139. radius = self._radius
  140. if num != None:
  141. label.setText("%d" % num)
  142. else:
  143. return
  144. y_val = radius / 3
  145. if num < 10:
  146. label.setPos(radius / 1.5, y_val)
  147. elif num < 100:
  148. label.setPos(radius / 3, y_val)
  149. else: # _number >= 100
  150. label.setPos(0, y_val)
  151. bRect = label.boundingRect()
  152. posx = bRect.width()/2
  153. posy = bRect.height()/2
  154. label.setPos(radius-posx, radius-posy)
  155. # end def
  156. def part(self):
  157. return self._emptyHelixItem.part()
  158. def virtualHelix(self):
  159. return self._virtualHelix
  160. # end def
  161. def number(self):
  162. return self.virtualHelix().number()
  163. def setActiveSliceView(self, isActiveNow, idx):
  164. if isActiveNow:
  165. self.setPen(self._usePen)
  166. self.setBrush(self._useBrush)
  167. self.updateArrow(idx)
  168. self.arrow.show()
  169. else:
  170. self.setPen(self._outOfSlicePen)
  171. self.setBrush(self._outOfSliceBrush)
  172. self.arrow.hide()
  173. # end def
  174. ############################ User Interaction ############################
  175. def sceneEvent(self, event):
  176. """Included for unit testing in order to grab events that are sent
  177. via QGraphicsScene.sendEvent()."""
  178. # if self._parent.sliceController.testRecorder:
  179. # coord = (self._row, self._col)
  180. # self._parent.sliceController.testRecorder.sliceSceneEvent(event, coord)
  181. if event.type() == QEvent.MouseButtonPress:
  182. self.mousePressEvent(event)
  183. return True
  184. elif event.type() == QEvent.MouseButtonRelease:
  185. self.mouseReleaseEvent(event)
  186. return True
  187. elif event.type() == QEvent.MouseMove:
  188. self.mouseMoveEvent(event)
  189. return True
  190. QGraphicsItem.sceneEvent(self, event)
  191. return False
  192. def hoverEnterEvent(self, event):
  193. """
  194. If the selection is configured to always select
  195. everything, we don't draw a focus ring around everything,
  196. instead we only draw a focus ring around the hovered obj.
  197. """
  198. # if self.selectAllBehavior():
  199. # self.setSelected(True)
  200. # forward the event to the emptyHelixItem as well
  201. self._emptyHelixItem.hoverEnterEvent(event)
  202. # end def
  203. def hoverLeaveEvent(self, event):
  204. # if self.selectAllBehavior():
  205. # self.setSelected(False)
  206. self._emptyHelixItem.hoverEnterEvent(event)
  207. # end def
  208. # def mousePressEvent(self, event):
  209. # action = self.decideAction(event.modifiers())
  210. # action(self)
  211. # self.dragSessionAction = action
  212. #
  213. # def mouseMoveEvent(self, event):
  214. # parent = self._helixItem
  215. # posInParent = parent.mapFromItem(self, QPointF(event.pos()))
  216. # # Qt doesn't have any way to ask for graphicsitem(s) at a
  217. # # particular position but it *can* do intersections, so we
  218. # # just use those instead
  219. # parent.probe.setPos(posInParent)
  220. # for ci in parent.probe.collidingItems():
  221. # if isinstance(ci, SliceHelix):
  222. # self.dragSessionAction(ci)
  223. # # end def
  224. # def mouseReleaseEvent(self, event):
  225. # self.part().needsFittingToView.emit()
  226. # def decideAction(self, modifiers):
  227. # """ On mouse press, an action (add scaffold at the active slice, add
  228. # segment at the active slice, or create virtualhelix if missing) is
  229. # decided upon and will be applied to all other slices happened across by
  230. # mouseMoveEvent. The action is returned from this method in the form of a
  231. # callable function."""
  232. # vh = self.virtualHelix()
  233. # if vh == None: return SliceHelix.addVHIfMissing
  234. # idx = self.part().activeSlice()
  235. # if modifiers & Qt.ShiftModifier:
  236. # if vh.stap().get(idx) == None:
  237. # return SliceHelix.addStapAtActiveSliceIfMissing
  238. # else:
  239. # return SliceHelix.nop
  240. # if vh.scaf().get(idx) == None:
  241. # return SliceHelix.addScafAtActiveSliceIfMissing
  242. # return SliceHelix.nop
  243. #
  244. # def nop(self):
  245. # pass