qt_view.py
Go to the documentation of this file.
00001 from python_qt_binding.QtCore import Qt, QMimeData, QPoint, QEvent
00002 from python_qt_binding.QtGui import QPen, QColor, QSizePolicy, QDrag, QBrush, QGraphicsWidget
00003 from python_qt_binding.QtGui import QGraphicsView, QGraphicsAnchorLayout, QGraphicsScene
00004 from python_qt_binding.QtGui import QFontMetrics, QToolTip, QPixmap, QImage, QPolygon
00005 from python_qt_binding.QtCore import pyqtSignal as Signal
00006 
00007 from diarc.snapkey import gen_snapkey, parse_snapkey
00008 from diarc.util import typecheck, TypedDict
00009 from diarc.view import View
00010 from diarc.view import BlockItemAttributes
00011 from diarc.view import BandItemAttributes
00012 from diarc.view import SnapItemAttributes
00013 from .SpacerContainer import SpacerContainer
00014 import json
00015 import sys
00016 import logging
00017 
00018 log = logging.getLogger('diarc.qt_view')
00019 
00020 class QtBlockItemAttributes(BlockItemAttributes):
00021     def __init__(self):
00022         BlockItemAttributes.__init__(self)
00023         self.__bgcolor = "black"
00024         self.__border_color = "black"
00025         self.__label_color = "black"
00026         self.border_width = 1
00027     @property
00028     def bgcolor(self):
00029         return QColor(self.__bgcolor)
00030     @bgcolor.setter
00031     def bgcolor(self, value):
00032         self.__bgcolor = value
00033     @property
00034     def border_color(self):
00035         return QColor(self.__border_color)
00036     @border_color.setter
00037     def border_color(self, value):
00038         self.__border_color = value
00039     @property
00040     def label_color(self):
00041         return QColor(self.__label_color)
00042     @label_color.setter
00043     def label_color(self, value):
00044         self.__label_color = value
00045 
00046 class QtBandItemAttributes(BandItemAttributes):
00047     def __init__(self):
00048         BandItemAttributes.__init__(self)
00049         self._bgcolor = "black"
00050         self._border_color = "black"
00051         self._label_color = "black"
00052     @property
00053     def bgcolor(self):
00054         return QColor(self._bgcolor)
00055     @bgcolor.setter
00056     def bgcolor(self, value):
00057         self._bgcolor = value
00058     @property
00059     def border_color(self):
00060         return QColor(self._border_color)
00061     @border_color.setter
00062     def border_color(self, value):
00063         self._border_color = value
00064     @property
00065     def label_color(self):
00066         return QColor(self._label_color)
00067     @label_color.setter
00068     def label_color(self, value):
00069         self._label_color = value
00070 
00071 class QtSnapItemAttributes(SnapItemAttributes):
00072     def __init__(self):
00073         SnapItemAttributes.__init__(self)
00074         self._bgcolor = "white"
00075         self._border_color = "black"
00076         self._label_color = "black"
00077     @property
00078     def bgcolor(self):
00079         return QColor(self._bgcolor)
00080     @bgcolor.setter
00081     def bgcolor(self, value):
00082         self._bgcolor = value
00083     @property
00084     def border_color(self):
00085         return QColor(self._border_color)
00086     @border_color.setter
00087     def border_color(self, value):
00088         self._border_color = value
00089     @property
00090     def label_color(self):
00091         return QColor(self._label_color)
00092     @label_color.setter
00093     def label_color(self, value):
00094         self._label_color = value
00095 
00096 
00097 
00098 class BandStack(SpacerContainer):
00099     def __init__(self, parent):
00100         super(BandStack, self).__init__(parent)
00101         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred))
00102         self.setMinimumWidth(15)
00103         self.spacerType = BandSpacer
00104 
00105 class BandSpacer(SpacerContainer.Spacer):
00106     def __init__(self, parent):
00107         super(BandSpacer, self).__init__(parent)
00108         self._layout_manager = parent.parent
00109         self._view = parent.parent.view()
00110         self._adapter = parent.parent.adapter()
00111         self.dragOver = False
00112         self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding))
00113         self.setPreferredHeight(15)
00114         self.setMinimumHeight(15)
00115         self.setAcceptDrops(True)
00116 
00117     def _release(self):
00118         topAltitude = self.topBand.altitude if self.topBand else None
00119         bottomAltitude = self.bottomBand.altitude if self.bottomBand else None
00120         log.debug("... Releasing BandSpacer between topBand %r and botBand %r"%(topAltitude, bottomAltitude))
00121         super(BandSpacer, self)._release()
00122 
00123     @property
00124     def topBand(self):
00125         return self.itemA
00126 
00127     @property
00128     def bottomBand(self):
00129         return self.itemB
00130 
00131     def link(self):
00132         l = self.layout()
00133         # Link To your Top! If you have a band above, connect
00134         if self.topBand:
00135             l.addAnchor(self, Qt.AnchorTop, self.topBand, Qt.AnchorBottom)
00136         # Otherwise, if it is a positive band, connect to the top of the BandStack
00137         elif self.bottomBand.isPositive():
00138             l.addAnchor(self, Qt.AnchorTop, self.parent, Qt.AnchorTop)
00139         # Otherwise, if it is a negative band, connect to Block Container
00140         elif not self.bottomBand.isPositive(): 
00141             l.addAnchor(self, Qt.AnchorTop, self._layout_manager.block_container, Qt.AnchorBottom)
00142 
00143         # Link to your bottom! If you have a band below, connect
00144         if self.bottomBand:
00145             l.addAnchor(self, Qt.AnchorBottom, self.bottomBand, Qt.AnchorTop)
00146         # Otherwise, if it is a positive band, connect to the block ribbon
00147         elif self.topBand.isPositive():
00148             l.addAnchor(self, Qt.AnchorBottom, self._layout_manager.block_container, Qt.AnchorTop)
00149         elif not self.topBand.isPositive():
00150             l.addAnchor(self, Qt.AnchorBottom, self.parent, Qt.AnchorBottom)
00151         
00152         # Connect sides
00153         l.addAnchor(self, Qt.AnchorLeft, self.parent, Qt.AnchorLeft)
00154         l.addAnchor(self, Qt.AnchorRight, self.parent, Qt.AnchorRight)
00155 
00156     def dragEnterEvent(self,event):
00157         if not event.mimeData().hasText():
00158             event.setAccepted(False)
00159             return
00160         data = json.loads(str(event.mimeData().text()))
00161         if not 'band' in data:
00162             event.setAccepted(False)
00163             return
00164         # To know if the band being dragged is on the same side of the blockRibbon,
00165         # we look at the altitudes of the bands on either side of the spacer. 
00166         # We need to look at both because neither is guarenteed to be a real band
00167         # (the furthest and closest bands to the blockRibbon will each have a 
00168         # non existant band on one side).
00169         topAltitude = self.topBand.altitude if self.topBand else 0
00170         bottomAltitude = self.bottomBand.altitude if self.bottomBand else 0
00171         # Don't let bands get dragged to adjacent spots (it results in non movement)
00172         if data['band'] in [topAltitude, bottomAltitude]:
00173             event.setAccepted(False)
00174             return
00175         # Accept a positive altitude band
00176         if data['band'] > 0 and (topAltitude > 0 or bottomAltitude > 0):
00177             event.setAccepted(True)
00178             self.dragOver = True
00179             self.update()
00180             log.debug("Drag Positive ENTER between topBand %r and botBand %r"%(topAltitude, bottomAltitude))
00181         # Accept a negative altitude band
00182         elif data['band'] < 0 and (topAltitude < 0 or bottomAltitude < 0):
00183             event.setAccepted(True)
00184             self.dragOver = True
00185             self.update()
00186             log.debug("Drag Negative ENTER between topBand %r and botBand %r"%(topAltitude, bottomAltitude))
00187         else:
00188             event.setAccepted(False)
00189             return
00190         self.setZValue(max([self.topBand.altitude if self.topBand else None,
00191                             self.bottomBand.altitude if self.bottomBand else None]))
00192 
00193     def dragLeaveEvent(self,event):
00194         self.dragOver = False
00195         self.update()
00196 
00197     def dropEvent(self,event):
00198         self.dragOver = False
00199         self.update()
00200         """ Reorder the band altitudes to put the dropped band in the new position"""
00201         # Not all bands are necessarily being shown due to the fact that depending
00202         # on the ordering of the blocks, edge connections could travel different 
00203         # directions. When reordering bands, we need to take into account ALL
00204         # the bands (both shown and not shown). 
00205 
00206         # Decode the drag metadata into a dict
00207         data = json.loads(str(event.mimeData().text()))
00208         # Get the altitude of the band that was dragged
00209         srcAlt = data['band']
00210         # Get the altitudes of the bands displayed above and below this spacer.
00211         topAltitude = self.topBand.altitude if self.topBand else None
00212         bottomAltitude = self.bottomBand.altitude if self.bottomBand else None
00213         log.debug("Moving Band %d between topBand %r and botBand %r"%(srcAlt, topAltitude, bottomAltitude))
00214         self._adapter.reorder_bands(srcAlt,bottomAltitude,topAltitude)
00215 
00216     def paint(self,painter,option,widget):
00217         if self.dragOver:
00218             pen = QPen()
00219             pen.setBrush(Qt.lightGray)
00220             pen.setStyle(Qt.DashLine)
00221             painter.setPen(pen)
00222         else:
00223             painter.setPen(Qt.NoPen)
00224         painter.drawRect(self.rect())
00225 
00226 class BandItem(SpacerContainer.Item, QtBandItemAttributes):
00227     def __init__(self, parent, altitude, rank):
00228         self._layout_manager = typecheck(parent, LayoutManagerWidget, "parent")
00229         self._view = parent.view()
00230         self._adapter = parent.adapter()
00231         super(BandItem,self).__init__(parent,parent.bandStack)
00232         QtBandItemAttributes.__init__(self)
00233 
00234         # Band properties - these must be kept up to date with topology
00235         self.altitude = altitude
00236         self._rank = rank
00237         self.top_band = None
00238         self.bot_band = None
00239         self.left_most_snap = None
00240         self.right_most_snap = None
00241 
00242         # Set Qt properties
00243         self.setContentsMargins(5,5,5,5)
00244         self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding))
00245         self.set_width(15)
00246         self.setAcceptHoverEvents(True)
00247 #         self.setZValue(rank)
00248 
00249     def release(self):
00250         self.top_band = None
00251         self.bot_band = None
00252         self.left_most_snap = None
00253         self.right_most_snap = None
00254         self.setParent(None)
00255         self.setVisible(False)
00256         super(BandItem, self)._release()
00257 
00258     def itemA(self):
00259         """ Set itemA to be the topBand """
00260         # This is computed and assigned by the adapter prior to linking
00261         return self.top_band
00262 
00263     def itemB(self):
00264         """ Set itemB to be the bottomBand """
00265         # This is computed and assigned by the adapter prior to linking
00266         return self.bot_band
00267 
00268     def isUsed(self):
00269         """ Deprecated """
00270         return True
00271 
00272     @property 
00273     def rank(self):
00274         return self._rank
00275     @rank.setter
00276     def rank(self, value):
00277         self._rank = value
00278         self.setZValue(self._rank)
00279 
00280     def set_attributes(self, attrs):
00281         """ Applies a set of BandItemViewAttributes to this object. Transforms 
00282         some values such as color into qt specific values. """
00283         typecheck(attrs, BandItemAttributes, "attrs")
00284         self.copy_attributes(attrs)
00285         self.set_width(self.width)
00286         self.update(self.rect())
00287 
00288     def set_width(self, width):
00289         """ Sets the 'width' of the band. 
00290         This is actually setting the height, but is referred to as the width.
00291         """
00292         self.setPreferredHeight(width)
00293         self.setMinimumHeight(width)
00294 
00295     def isPositive(self):
00296         return True if self.altitude > 0 else False
00297 
00298     def link(self):
00299         sys.stdout.flush()
00300         # Assign the vertical anchors
00301         super(BandItem,self).link()
00302         # Assign the horizontal Anchors
00303         l = self.parent.layout()
00304         l.addAnchor(self, Qt.AnchorLeft, self.left_most_snap, Qt.AnchorLeft)
00305         l.addAnchor(self, Qt.AnchorRight, self.right_most_snap, Qt.AnchorRight)
00306 
00307     def mousePressEvent(self, event):
00308         """ This is necessary to capture the mouse clicking event to drag"""
00309         pass
00310 
00311     def mouseReleaseEvent(self, event):
00312         log.debug("Bringing band %d to front" % self.altitude)
00313         self._adapter.bring_band_to_front(self.altitude)
00314 
00315     def hoverEnterEvent(self, event):
00316         if self.tooltip_text:
00317             QToolTip.showText(event.screenPos(),self.tooltip_text)
00318 
00319     def hoverLeaveEvent(self, event):
00320         QToolTip.hideText()
00321 
00322 
00323     def mouseMoveEvent(self, event):
00324         if event.buttons() != Qt.LeftButton:
00325             super(BandItem,self).mouseMoveEvent(event)
00326         else:
00327             drag = QDrag(event.widget())
00328             mimeData = QMimeData()
00329             mimeData.setText(json.dumps({'band':self.altitude}))
00330             drag.setMimeData(mimeData)
00331             drag.start()
00332 
00333     def paint(self,painter,option,widget):
00334         brush = QBrush()
00335         brush.setStyle(Qt.SolidPattern)
00336         brush.setColor(self.bgcolor)
00337         painter.fillRect(self.rect(),brush)
00338         painter.setPen(self.border_color)
00339         painter.drawRect(self.rect())
00340         rect = self.geometry()
00341         painter.setPen(self.label_color)
00342         fm = painter.fontMetrics()
00343         elided = fm.elidedText(self.label, Qt.ElideRight, rect.width())
00344         twidth = fm.width(elided)
00345         painter.drawText((rect.width()-twidth)/2,rect.height()-2,elided)
00346 
00347 
00348 
00349 
00350 class BlockContainer(SpacerContainer):
00351     def __init__(self, parent):
00352         super(BlockContainer,self).__init__(parent)
00353         self.spacerType = BlockSpacer
00354         self.parent = typecheck(parent,LayoutManagerWidget, "parent")
00355         self._view = parent.view()
00356         self._adapter = parent.adapter()
00357         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred))
00358         self.setMinimumWidth(15)
00359 
00360     def paint(self, painter, option, widget):
00361         painter.setPen(Qt.NoPen)
00362         painter.drawRect(self.rect())
00363 
00364 class BlockSpacer(SpacerContainer.Spacer):
00365     def __init__(self,parent):
00366         super(BlockSpacer,self).__init__(parent)
00367         self._view = parent.parent.view()
00368         self._adapter = parent.parent.adapter()
00369         self.dragOver = False
00370         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Preferred))
00371         self.setPreferredWidth(50)
00372         self.setMinimumWidth(50)
00373         self.setAcceptDrops(True)
00374 
00375     @property
00376     def leftBlock(self):
00377         return self.itemA
00378 
00379     @property
00380     def rightBlock(self):
00381         return self.itemB
00382 
00383     def link(self):
00384         l = self.parent.parent.layout()
00385         # If you have a block to your left, connect
00386         if self.leftBlock:
00387             l.addAnchor(self, Qt.AnchorLeft, self.leftBlock, Qt.AnchorRight)
00388             l.addAnchor(self, Qt.AnchorTop, self.leftBlock, Qt.AnchorTop)
00389             l.addAnchor(self, Qt.AnchorBottom, self.leftBlock, Qt.AnchorBottom)
00390         # Otherwise connect to left container edge
00391         else:
00392             l.addAnchor(self, Qt.AnchorLeft, self.parent, Qt.AnchorLeft)
00393             l.addAnchor(self, Qt.AnchorTop, self.parent, Qt.AnchorTop)
00394             l.addAnchor(self, Qt.AnchorBottom, self.parent, Qt.AnchorBottom)
00395 
00396         # If you have a block to your right, connect
00397         if self.rightBlock:
00398             l.addAnchor(self, Qt.AnchorRight, self.rightBlock, Qt.AnchorLeft)
00399             l.addAnchor(self, Qt.AnchorTop, self.rightBlock, Qt.AnchorTop)
00400             l.addAnchor(self, Qt.AnchorBottom, self.rightBlock, Qt.AnchorBottom)
00401         # Otherwise connect to the right container edge
00402         else:
00403             l.addAnchor(self, Qt.AnchorRight, self.parent, Qt.AnchorRight)
00404             l.addAnchor(self, Qt.AnchorTop, self.parent, Qt.AnchorTop)
00405             l.addAnchor(self, Qt.AnchorBottom, self.parent, Qt.AnchorBottom)
00406 
00407     def dragEnterEvent(self,event):
00408         if not event.mimeData().hasText():
00409             event.setAccepted(False)
00410             return
00411         data = json.loads(str(event.mimeData().text()))
00412         if 'block' in data:
00413             if self.leftBlock and data['block'] == self.leftBlock.block_index:
00414                 event.setAccepted(False)
00415                 return
00416             if self.rightBlock and data['block'] == self.rightBlock.block_index:
00417                 event.setAccepted(False)
00418                 return
00419             event.setAccepted(True)
00420             self.dragOver = True
00421             log.debug("Drag ENTER")
00422             self.update()
00423         else:
00424             event.setAccepted(False)
00425 
00426     def dragLeaveEvent(self, event):
00427         self.dragOver = False
00428         self.update()
00429 
00430     def dropEvent(self, event):
00431         """ Dropping a 'block' on a BlockSpacer triggers a reordering event """
00432         self.dragOver = False
00433         self.update()
00434         # Dragged Index
00435         data = json.loads(str(event.mimeData().text()))
00436         if not 'block' in data:
00437             raise Exception("Wrong drag data type!")
00438         srcIdx = data['block']
00439         # Left Index
00440         lowerIdx = self.leftBlock.block_index if self.leftBlock else None
00441         upperIdx = self.rightBlock.block_index if self.rightBlock else None
00442         self._adapter.reorder_blocks(srcIdx, lowerIdx, upperIdx)
00443 
00444     def paint(self, painter, option, widget):
00445         if self.dragOver:
00446             pen = QPen()
00447             pen.setBrush(Qt.lightGray)
00448             pen.setStyle(Qt.DashLine)
00449             painter.setPen(pen)
00450         else:
00451             painter.setPen(Qt.NoPen)
00452         painter.drawRect(self.rect())
00453 
00454 class BlockItem(SpacerContainer.Item, QtBlockItemAttributes):
00455     """ This is a QGraphicsWidget for a Diarc Block. """
00456     def __init__(self, parent, block_index):
00457         self._layout_manager = typecheck(parent, LayoutManagerWidget, "parent")
00458         self._view = parent.view()
00459         self._adapter = parent.adapter()
00460         super(BlockItem, self).__init__(parent, parent.block_container)
00461         QtBlockItemAttributes.__init__(self)
00462 
00463         # Qt Settings
00464         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding))
00465         self.setPreferredHeight(5)
00466         self.setMinimumHeight(5)
00467         self.setContentsMargins(5,5,5,5)
00468 
00469         # Properties - these values must be kept up-to-date whenever the model changes
00470         self.block_index = block_index
00471         self.left_block = None
00472         self.right_block = None
00473 
00474         # We want to have a little space above and below the Emitter/Collector,
00475         # Set up top and bottom margin to give that space. 
00476         self._topMargin = BlockItem.HorizontalSpacer(self)
00477         self._botMargin = BlockItem.HorizontalSpacer(self)
00478         self._middleSpacer = BlockItem.MiddleSpacer(self)
00479 
00480         # Set up Emitter and Collector Containers. They will sit "inside" the 
00481         # block margins
00482         self.myEmitter = MyEmitter(self)
00483         self.myCollector = MyCollector(self)
00484 
00485     def set_attributes(self, attrs):
00486         typecheck(attrs, BlockItemAttributes, "attrs")
00487         self.copy_attributes(attrs)
00488         self._middleSpacer.set_width(self.spacerwidth)
00489         self.update(self.rect())
00490 
00491     def release(self):
00492         super(BlockItem, self)._release()
00493         self.left_block = None
00494         self.right_block = None
00495         self._topMargin.release()
00496         self._botMargin.release()
00497         self._middleSpacer.release()
00498         self._topMargin = None
00499         self._botMargin = None
00500         self._middleSpacer = None
00501         self.myEmitter.release()
00502         self.myCollector.release()
00503         self.myEmitter = None
00504         self.myCollector = None
00505         self._layout_manager = None
00506         self._view = None
00507         self._adapter = None
00508 
00509         
00510 
00511     def itemA(self):
00512         """ We use itemA for the BlockItem to the left. """
00513         # Get the index to our left, and return the BlockItem with that value
00514         return self.left_block
00515 
00516     def itemB(self):
00517         """ We use itemB for the BlockItem to the right. """
00518         # Get the index to our right, and return the BlockItem with that value
00519         return self.right_block
00520 
00521     def isUsed(self):
00522         return True
00523 
00524     def link(self):
00525         """ Link to other objects around you. In addition to linking to other 
00526         blocks, we need to link to our top and bottom margins, and the emitter
00527         and collector containers 
00528         """
00529         # Link with other BlockItems
00530         super(BlockItem, self).link()
00531         
00532         l = self.parent.layout()
00533         # Link with top and bottom margins
00534         l.addAnchor(self,Qt.AnchorTop, self._topMargin, Qt.AnchorTop)
00535         l.addAnchor(self,Qt.AnchorLeft, self._topMargin, Qt.AnchorLeft)
00536         l.addAnchor(self,Qt.AnchorRight, self._topMargin, Qt.AnchorRight)
00537         l.addAnchor(self,Qt.AnchorBottom, self._botMargin, Qt.AnchorBottom)
00538         l.addAnchor(self,Qt.AnchorLeft, self._botMargin, Qt.AnchorLeft)
00539         l.addAnchor(self,Qt.AnchorRight, self._botMargin, Qt.AnchorRight)
00540 
00541         # Link with emitter and collector containers
00542         l.addAnchor(self._topMargin, Qt.AnchorBottom, self.myEmitter, Qt.AnchorTop)
00543         l.addAnchor(self._botMargin, Qt.AnchorTop, self.myEmitter, Qt.AnchorBottom)
00544         l.addAnchor(self._topMargin, Qt.AnchorBottom, self.myCollector, Qt.AnchorTop)
00545         l.addAnchor(self._botMargin, Qt.AnchorTop, self.myCollector, Qt.AnchorBottom)
00546 
00547         l.addAnchor(self._middleSpacer, Qt.AnchorTop, self.myEmitter, Qt.AnchorTop)
00548         l.addAnchor(self._middleSpacer, Qt.AnchorTop, self.myCollector, Qt.AnchorTop)
00549         l.addAnchor(self._middleSpacer, Qt.AnchorBottom, self.myEmitter, Qt.AnchorBottom)
00550         l.addAnchor(self._middleSpacer, Qt.AnchorBottom, self.myCollector, Qt.AnchorBottom)
00551 
00552         l.addAnchor(self, Qt.AnchorLeft, self.myCollector, Qt.AnchorLeft)
00553         l.addAnchor(self, Qt.AnchorRight, self.myEmitter, Qt.AnchorRight)
00554         l.addAnchor(self.myCollector, Qt.AnchorRight, self._middleSpacer, Qt.AnchorLeft)
00555         l.addAnchor(self._middleSpacer, Qt.AnchorRight, self.myEmitter, Qt.AnchorLeft)
00556 
00557     def mousePressEvent(self, event):
00558         pass
00559 
00560     def mouseMoveEvent(self, event):
00561         """ Creates a drag event with the block information """
00562         if event.buttons() != Qt.LeftButton:
00563             super(BlockItem, self).mouseMoveEvent(event)
00564         else:
00565             drag = QDrag(event.widget())
00566             mimeData = QMimeData()
00567             mimeData.setText(json.dumps({'block': self.block_index}))
00568             drag.setMimeData(mimeData)
00569             drag.start()
00570 
00571     def paint(self,painter,option,widget):
00572         border_pen = QPen()
00573         border_pen.setBrush(self.border_color)
00574         border_pen.setStyle(Qt.SolidLine)
00575         border_pen.setWidth(self.border_width)
00576         painter.setPen(border_pen)
00577         painter.drawRect(self.rect())
00578 
00579     class MiddleSpacer(QGraphicsWidget):
00580         def __init__(self,parent):
00581             super(BlockItem.MiddleSpacer,self).__init__(parent=parent)
00582             # NOTE: I originally tried to set this to Preferred, MinimumExpanding
00583             # but ran into trouble when the layout could not compute itself when
00584             # it set that mode. For some reason, it was very unhappy about giving
00585             # it height policy information.
00586             self.blockItem = parent
00587             self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
00588             # The width of the block
00589             self.set_width(20)
00590             self.setAcceptHoverEvents(True)
00591 
00592         def set_width(self, width):
00593             self.setPreferredWidth(width)
00594             self.setMinimumWidth(width)
00595 
00596         def release(self):
00597             self.setParent(None)
00598             self.blockItem = None
00599 
00600         def hoverEnterEvent(self, event):
00601             if self.blockItem.tooltip_text:
00602                 QToolTip.showText(event.screenPos(),self.blockItem.tooltip_text)
00603 
00604         def hoverLeaveEvent(self, event):
00605             QToolTip.hideText()
00606 
00607 #         def event(self, event):
00608 #             if event.type() == QEvent.ToolTip:
00609 #             QToolTip.showText(event.screenPos(),self.label+" ToolTip!")
00610 #         else:
00611 #             QToolTip.hideText()
00612 #             event.ignore()
00613 #         return super(BlockItem, self).event(event)
00614 
00615         def paint(self,painter,option,widget):
00616             painter.setPen(Qt.NoPen)
00617             painter.drawRect(self.rect())
00618             painter.setPen(self.blockItem.label_color)
00619             painter.rotate(-90)
00620             rect = self.rect()
00621             fm = painter.fontMetrics()
00622             elided = fm.elidedText(self.blockItem.label, Qt.ElideRight, rect.height()-2)
00623             twidth = fm.width(elided)
00624             painter.drawText(-twidth-(rect.height()-twidth)/2,rect.width()-2,elided)
00625 
00626     class HorizontalSpacer(QGraphicsWidget):
00627         def __init__(self,parent):
00628             super(BlockItem.HorizontalSpacer,self).__init__(parent=parent)
00629             self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Preferred))
00630             self.setPreferredHeight(0)
00631             self.setMinimumHeight(0)
00632             self.setMaximumHeight(0)
00633             self.setMinimumWidth(5)
00634 
00635         def release(self):
00636             self.setParent(None)
00637 
00638 class SnapContainer(SpacerContainer):
00639     def __init__(self,parent):
00640         super(SnapContainer, self).__init__(parent.parent)
00641         self.parentBlock = typecheck(parent, BlockItem, "parent")
00642 
00643         self.spacerType = SnapSpacer
00644         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred))
00645         self.setMinimumWidth(1)
00646         self.setPreferredWidth(1)
00647     
00648     def release(self):
00649         super(SnapContainer, self)._release()
00650         self.parentBlock = None
00651 
00652     def strType(self):
00653         """ prints the container type as a string """
00654         return "emitter" if isinstance(self,MyEmitter) else "collector" if isinstance(self,MyCollector) else "unknown"
00655 
00656     def paint(self,painter,option,widget):
00657         painter.setPen(Qt.green)
00658         painter.drawRect(self.rect())
00659     
00660 
00661 class MyEmitter(SnapContainer):
00662     def paint(self, painter, option, widget):
00663         if self.parentBlock.draw_debug:
00664             painter.setPen(Qt.green)
00665             painter.drawRect(self.rect())
00666    
00667 class MyCollector(SnapContainer):
00668     def paint(self, painter, option, widget):
00669         if self.parentBlock.draw_debug:
00670             painter.setPen(Qt.blue)
00671             painter.drawRect(self.rect())
00672  
00673 
00674 class SnapSpacer(SpacerContainer.Spacer):
00675     def __init__(self,parent):
00676         super(SnapSpacer,self).__init__(parent)
00677         self._layout_manager = parent.parent
00678         self._view = parent.parent.view()
00679         self._adapter = parent.parent.adapter()
00680 
00681         self.dragOver = False
00682         
00683         # Qt Properties
00684         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.Preferred))
00685         self.collapseWidth()
00686         self.setAcceptDrops(True)
00687 
00688     @property
00689     def leftSnap(self):
00690         return self.itemA
00691 
00692     @property
00693     def rightSnap(self):
00694         return self.itemB
00695 
00696     def link(self):
00697         l = self.parent.parent.layout()
00698         # If you have a snap to your left, connect
00699         if self.leftSnap:
00700             l.addAnchor(self, Qt.AnchorLeft, self.leftSnap, Qt.AnchorRight)
00701             l.addAnchor(self, Qt.AnchorTop, self.leftSnap, Qt.AnchorTop)
00702             l.addAnchor(self, Qt.AnchorBottom, self.leftSnap, Qt.AnchorBottom)
00703         # Otherwise connect to left container edge
00704         else:
00705             l.addAnchor(self, Qt.AnchorLeft, self.parent, Qt.AnchorLeft)
00706             l.addAnchor(self, Qt.AnchorTop, self.parent, Qt.AnchorTop)
00707             l.addAnchor(self, Qt.AnchorBottom, self.parent, Qt.AnchorBottom)
00708         # if you have a snap to your right, connect
00709         if self.rightSnap:
00710             l.addAnchor(self, Qt.AnchorRight, self.rightSnap, Qt.AnchorLeft)
00711             l.addAnchor(self, Qt.AnchorTop, self.rightSnap, Qt.AnchorTop)
00712             l.addAnchor(self, Qt.AnchorBottom, self.rightSnap, Qt.AnchorBottom)
00713         # Otherwise connect to the right container edge
00714         else:
00715             l.addAnchor(self, Qt.AnchorRight, self.parent, Qt.AnchorRight)
00716             l.addAnchor(self, Qt.AnchorTop, self.parent, Qt.AnchorTop)
00717             l.addAnchor(self, Qt.AnchorBottom, self.parent, Qt.AnchorBottom)
00718 
00719     def dragEnterEvent(self, event):
00720         """ Decides whether or not the information being dragged can be placed here"""
00721         if not event.mimeData().hasText():
00722             event.setAccepted(False)
00723             return
00724         data = json.loads(str(event.mimeData().text()))
00725         if not set(['block', 'container', 'snap']).issubset(set(data.keys())):
00726             event.setAccepted(False)
00727             return
00728         if not data['block'] == self.parent.parentBlock.block_index:
00729             event.setAccepted(False)
00730             return
00731         if not self.parent.strType() == data['container']:
00732             event.setAccepted(False)
00733             return
00734         if self.leftSnap and data['snap'] == self.leftSnap.snap_order:
00735             event.setAccepted(False)
00736             return
00737         if self.rightSnap and data['snap'] == self.rightSnap.snap_order:
00738             event.setAccepted(False)
00739             return
00740         event.setAccepted(True)
00741         self.expandWidth()
00742         self.dragOver = True
00743         self.update()
00744 
00745     def dragLeaveEvent(self, event):
00746         self.dragOver = False
00747         self.collapseWidth()
00748         self.update()
00749 
00750     def dropEvent(self,event):
00751         """ Relocates a MySnap to this position """
00752         self.collapseWidth()
00753         self.dragOver = False
00754         data = json.loads(str(event.mimeData().text()))
00755         assert(data['block'] == self.parent.parentBlock.block_index)
00756         assert(data['container'] == self.parent.strType())
00757 
00758         srcIdx = data['snap']
00759         lowerIdx = self.leftSnap.snap_order if self.leftSnap else None
00760         upperIdx = self.rightSnap.snap_order if self.rightSnap else None
00761         self._adapter.reorder_snaps(data['block'], data['container'], srcIdx, lowerIdx, upperIdx)
00762 
00763     def paint(self, painter, option, widget):
00764         if self.dragOver:
00765             brush = QBrush()
00766             brush.setStyle(Qt.Dense4Pattern)
00767             brush.setColor(Qt.yellow)
00768             painter.fillRect(self.rect(),brush)
00769             pen = QPen()
00770             pen.setBrush(Qt.darkGray)
00771             pen.setStyle(Qt.DotLine)
00772             pen.setWidth(1)
00773             painter.setPen(pen)
00774             painter.drawRect(self.rect())
00775             rect = self.geometry()
00776         else:
00777             painter.setPen(Qt.NoPen)
00778 #             painter.setPen(Qt.magenta)
00779             painter.drawRect(self.rect())
00780 
00781     def collapseWidth(self):
00782         """ Make the width very small """
00783         self.setMinimumWidth(0.1)
00784         self.setMaximumWidth(0.1)
00785         self.setPreferredWidth(0.1)
00786 
00787     def expandWidth(self):
00788         """ Widen the spacer to afford seperation """
00789         self.setMinimumWidth(10)
00790         self.setMaximumWidth(10)
00791         self.setPreferredWidth(10)
00792 
00793 
00794 
00795 class SnapItem(SpacerContainer.Item, QtSnapItemAttributes):
00796     def __init__(self, parent, snapkey):
00797         QtSnapItemAttributes.__init__(self)
00798         block_index, container_name, snap_order = parse_snapkey(snapkey)
00799         self._snapkey = snapkey
00800         self._layout_manager = typecheck(parent, LayoutManagerWidget, "parent")
00801         self._view = parent.view()
00802         self._adapter = parent.adapter()
00803 
00804         assert(container_name in ["emitter","collector"])
00805         self.block_index = block_index
00806         self.snap_order = snap_order
00807         self.block_item = self._layout_manager.get_block_item(block_index)
00808         self.container = self.block_item.myEmitter if container_name == "emitter" else self.block_item.myCollector
00809         # SnapItems to the left and to the right - populated by the adapter
00810         self.left_snap = None
00811         self.right_snap = None
00812         # Positive and Negative BandItems this snap connects to - only exists if
00813         # the band is being used - populated by the adapter
00814         self.posBandItem = None
00815         self.negBandItem = None
00816         super(SnapItem,self).__init__(parent,self.container)
00817 
00818         # Qt Properties
00819         self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred))
00820         self.set_width(20)
00821         # These values dictate the block Container Height
00822         self.setPreferredHeight(150)
00823         self.setMaximumHeight(150)
00824 
00825         #Create two SnapBandLinks - one for each band
00826         _is_source = True if container_name == "emitter" else False
00827         self.upLink = SnapBandLink(None, is_uplink=True, is_source=_is_source)
00828         self.downLink = SnapBandLink(None,is_source=_is_source)
00829  
00830     def release(self):
00831         self.left_snap = None
00832         self.right_snap = None
00833         self.upLink.setParent(None)
00834         self.downLink.setParent(None)
00835         self.upLink = None
00836         self.downLink = None
00837         self.setVisible(False)
00838         super(SnapItem, self)._release()
00839 
00840 
00841     def itemA(self):
00842         """ We use itemA for the SnapItem to the left """
00843         return self.left_snap
00844 
00845     def itemB(self):
00846         """ We use itemB for the SnapItem to the right """
00847         # When we don't display unused snaps, we are still reporting unused snaps to 
00848         # our left and right here - only they don't visually exists which causes problems
00849         return self.right_snap
00850 
00851     def isUsed(self):
00852         """ Deprecated """
00853         return True
00854 
00855     def set_attributes(self, attrs):
00856         typecheck(attrs, SnapItemAttributes, "attrs")
00857         self.copy_attributes(attrs)
00858         self.set_width(attrs.width)
00859         self.update(self.rect())
00860 
00861     def set_width(self, width):
00862         self.width = width
00863         self.setPreferredWidth(width)
00864 
00865     def link(self):
00866         super(SnapItem, self).link()
00867         l = self.parent.layout()
00868         l.addAnchor(self, Qt.AnchorTop, self.container, Qt.AnchorTop)
00869         l.addAnchor(self, Qt.AnchorBottom, self.container, Qt.AnchorBottom)
00870 
00871         #Connect bandlinks
00872         if self.posBandItem:
00873             self.upLink.setVisible(True)
00874             self.upLink.setZValue(self.posBandItem.rank+0.5)
00875             l.addAnchor(self, Qt.AnchorTop, self.upLink, Qt.AnchorBottom)
00876             l.addAnchor(self.posBandItem, Qt.AnchorBottom, self.upLink, Qt.AnchorTop)
00877             l.addAnchor(self, Qt.AnchorLeft, self.upLink, Qt.AnchorLeft)
00878             l.addAnchor(self, Qt.AnchorRight, self.upLink, Qt.AnchorRight)
00879             self.upLink.bgcolor = self.posBandItem.bgcolor
00880             self.upLink.border_color = self.posBandItem.border_color
00881             self.upLink.label_color = self.posBandItem.label_color
00882         else:
00883             self.upLink.setVisible(False)
00884             self.upLink.setParent(None)
00885 
00886         if self.negBandItem:
00887             self.downLink.setVisible(True)
00888             self.downLink.setZValue(self.negBandItem.rank+0.5)
00889             l.addAnchor(self, Qt.AnchorBottom, self.downLink, Qt.AnchorTop)
00890             l.addAnchor(self.negBandItem, Qt.AnchorTop, self.downLink, Qt.AnchorBottom)
00891             l.addAnchor(self, Qt.AnchorLeft, self.downLink, Qt.AnchorLeft)
00892             l.addAnchor(self, Qt.AnchorRight, self.downLink, Qt.AnchorRight)
00893             self.downLink.bgcolor = self.negBandItem.bgcolor
00894             self.downLink.border_color = self.negBandItem.border_color
00895             self.downLink.label_color = self.negBandItem.label_color
00896         else:
00897             self.downLink.setVisible(False)
00898             self.downLink.setParent(None)
00899 
00900     def mousePressEvent(self, event):
00901         """ Captures the mouse press event for dragging """
00902         pass
00903 
00904     def mouseReleaseEvent(self, event):
00905         if self.posBandItem:
00906             self._adapter.bring_band_to_front(self.posBandItem.altitude)
00907         if self.negBandItem:
00908             self._adapter.bring_band_to_front(self.negBandItem.altitude)
00909 
00910     def mouseMoveEvent(self, event):
00911         if event.buttons() != Qt.LeftButton:
00912             super(SnapItem,self).mouseMoveEvent(event)
00913         else:
00914             drag = QDrag(event.widget())
00915             mimeData = QMimeData()
00916             mimeData.setText(json.dumps({'block': self.block_index,'container': self.container.strType(),'snap':self.snap_order}))
00917             drag.setMimeData(mimeData)
00918             drag.start()
00919 
00920     def paint(self, painter, option, widget):
00921         # Paint background
00922         brush = QBrush()
00923         brush.setStyle(Qt.SolidPattern)
00924         brush.setColor(self.bgcolor)
00925         painter.fillRect(self.rect(),brush)
00926         # Paint border
00927         border_pen = QPen()
00928         border_pen.setBrush(self.border_color)
00929         border_pen.setStyle(Qt.SolidLine)
00930         border_pen.setWidth(self.border_width)
00931         painter.setPen(border_pen)
00932         painter.drawRect(self.rect())
00933         rect = self.geometry()
00934         painter.setPen(self.label_color)
00935         if self.draw_debug:
00936             if self.posBandItem:
00937                 painter.drawText(6,12,str(self.posBandItem.altitude))
00938             if self.negBandItem:
00939                 painter.drawText(3,rect.height()-3,str(self.negBandItem.altitude))
00940 #         painter.drawText(2,rect.height()/2+4,self.label)
00941         painter.rotate(-90)
00942         fm = painter.fontMetrics()
00943         elided = fm.elidedText(self.label, Qt.ElideRight, rect.height())
00944         twidth = fm.width(elided)
00945         painter.drawText(-twidth-(rect.height()-twidth)/2,rect.width()-2,elided)
00946 
00947 
00948 class SnapBandLink(QGraphicsWidget, QtBandItemAttributes):
00949     def __init__(self,parent, is_uplink=False, is_source=False):
00950         super(SnapBandLink,self).__init__(parent=parent)
00951         QtBandItemAttributes.__init__(self)
00952         self.setVisible(False)
00953         self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.MinimumExpanding))
00954         self.setPreferredWidth(5)
00955         self.setMinimumWidth(5)
00956         self.setPreferredHeight(5)
00957         self.setMinimumHeight(5)
00958 
00959         self._is_uplink = is_uplink
00960         self._is_source = is_source
00961 
00962  
00963     def paint(self,painter,option,widget):
00964         brush = QBrush()
00965         brush.setStyle(Qt.SolidPattern)
00966         brush.setColor(self.bgcolor)
00967         painter.fillRect(self.rect(),brush)
00968         pen = QPen()
00969         pen.setBrush(self.border_color)
00970         pen.setStyle(Qt.DashLine)
00971         rect = self.rect()
00972         painter.setPen(pen)
00973         painter.drawRect(rect)
00974         # Create arrows
00975         arrow_scale = 0.5
00976         arrow_width = rect.width()*arrow_scale
00977         arrow_height = arrow_width * 0.8
00978         arrow_margin = (rect.width()-arrow_width)/2.0
00979 
00980         brush.setColor(self.label_color)
00981         painter.setPen(Qt.NoPen)
00982         painter.setBrush(brush)
00983         arrow = None
00984         # Determine which direction to draw arrow
00985         if (self._is_uplink and self._is_source) or (not self._is_uplink and not self._is_source):
00986             # Draw pointing up
00987             arrow = QPolygon([QPoint(0,arrow_height), QPoint(arrow_width,arrow_height), QPoint(arrow_width/2.0,0)])
00988         else:
00989             # Draw pointing down
00990             arrow = QPolygon([QPoint(0,0), QPoint(arrow_width,0), QPoint(arrow_width/2.0,arrow_height)])
00991 
00992         # Determine which side to draw arrow on
00993         if self._is_uplink:
00994             arrow.translate(rect.x()+arrow_margin,rect.y()+rect.height()-arrow_height-arrow_margin)
00995         else:
00996             arrow.translate(rect.x()+arrow_margin,rect.y()+arrow_margin)
00997         painter.drawPolygon(arrow)
00998 
00999 class LayoutManagerWidget(QGraphicsWidget):
01000     """ Holds the actual qt anchoredlayout and top level SpacerContainers """
01001     def __init__(self, view):
01002         super(LayoutManagerWidget, self).__init__(parent=None)
01003         self._view = view
01004         self.resize(0,0)
01005         
01006         # Top Level Layout Containers
01007         self.block_container = BlockContainer(self)
01008         self.bandStack = BandStack(self)
01009 
01010         # Visual Object we are tracking
01011         self._block_items = TypedDict(int,BlockItem)  # index    #TypedList(BlockItem)
01012         self._band_items = TypedDict(int,BandItem)    # altitude    #TypedList(BandItem)
01013         self._snap_items = TypedDict(str,SnapItem)  # snapkey  #TypedList(SnapItem)
01014 
01015     def add_block_item(self, index):
01016         log.debug("... Adding BlockItem %d"%index)
01017         """ create a new BlockItem """
01018         if index in self._block_items:
01019             raise DuplicateItemExistsError("Block Item with index %d already exists"%(index))
01020         item = BlockItem(self, index)
01021         self._block_items[index] = item
01022         return item
01023 
01024     def has_block_item(self, index):
01025         return True if index in self._block_items else False
01026 
01027     def set_block_item_settings(self, index, left_index, right_index):
01028         item = self._block_items[index]
01029         item.left_block = self._block_items[left_index] if left_index is not None else None
01030         item.right_block = self._block_items[right_index] if right_index is not None else None
01031 
01032     def set_block_item_attributes(self, index, attributes):
01033         self._block_items[index].set_attributes(attributes)
01034 
01035     def remove_block_item(self, index):
01036         log.debug("... Removing BlockItem %d"%index)
01037         self._block_items[index].release()
01038         self._block_items.pop(index)
01039 
01040     def get_block_item(self, index):
01041         """ Returns a BlockItem with specified index """
01042         return self._block_items[index]
01043 
01044     def add_band_item(self, altitude, rank):
01045         """ Create a new drawable object to correspond to a Band. """
01046         log.debug("... Adding BandItem with altitude %d"%altitude)
01047         if altitude in self._band_items:
01048             raise DuplicateItemExistsError("BandItem with altitude %d already exists"%(altitude))
01049         item = BandItem(self, altitude, rank)
01050         self._band_items[altitude] = item
01051         return item
01052 
01053     def has_band_item(self, altitude):
01054         return True if altitude in self._band_items else False
01055 
01056     def remove_band_item(self, altitude):
01057         """ Remove the drawable object to correspond to a band """ 
01058         log.debug("... Removing BandItem altitude %d"%altitude)
01059         self._band_items[altitude].release()
01060         self._band_items.pop(altitude)
01061 
01062     def get_band_item(self, altitude):
01063         return self._band_items[altitude]
01064     
01065     def set_band_item_settings(self, altitude, rank,
01066                                 top_band_alt, bot_band_alt,
01067                                 leftmost_snapkey, rightmost_snapkey):
01068         item = self._band_items[altitude]
01069         item.rank = rank
01070         item.top_band = self._band_items[top_band_alt] if top_band_alt is not None else None
01071         item.bot_band = self._band_items[bot_band_alt] if bot_band_alt is not None else None
01072         item.left_most_snap = self._snap_items[str(leftmost_snapkey)]
01073         item.right_most_snap = self._snap_items[str(rightmost_snapkey)]
01074 
01075     def set_band_item_attributes(self, altitude, attrs):
01076         self._band_items[altitude].set_attributes(attrs)
01077 
01078     def add_snap_item(self, snapkey):
01079         # snapkey gets passed as a QString automatically since it goes across
01080         # a signal/slot interface
01081         snapkey = str(snapkey)
01082         log.debug("... Adding SnapItem %s"%snapkey)
01083         if snapkey in self._snap_items:
01084             raise DuplicateItemExistsError("SnapItem with snapkey %s already exists"%(snapkey))
01085         item = SnapItem(self, snapkey)
01086         self._snap_items[snapkey] = item
01087         return item
01088 
01089     def remove_snap_item(self, snapkey):
01090         # snapkey gets passed as a QString automatically since it goes across
01091         # a signal/slot interface
01092         snapkey = str(snapkey)
01093         log.debug("... Removing SnapItem %s"%snapkey)
01094         self._snap_items[snapkey].release()
01095         self._snap_items.pop(snapkey)
01096 
01097     def has_snap_item(self, snapkey):
01098         return True if snapkey in self._snap_items else False
01099 
01100     def get_snap_items(self, snapkey):
01101         # snapkey gets passed as a QString automatically since it goes across
01102         # a signal/slot interface
01103         snapkey = str(snapkey)
01104         return self._snap_item[snapkey]
01105 
01106     def set_snap_item_settings(self, snapkey, left_order, right_order, pos_band_alt, neg_band_alt):
01107         # snapkey gets passed as a QString automatically since it goes across
01108         # a signal/slot interface
01109         snapkey = str(snapkey)
01110         item = self._snap_items[snapkey]
01111         if left_order is not None:
01112             left_snapkey = gen_snapkey(item.block_index,item.container.strType(),left_order)
01113             item.left_snap = self._snap_items[left_snapkey]
01114         else:
01115             item.left_snap = None
01116         if right_order is not None:
01117             right_snapkey = gen_snapkey(item.block_index,item.container.strType(),right_order)
01118             item.right_snap = self._snap_items[right_snapkey]
01119         else:
01120             item.right_snap = None
01121         item.posBandItem = self._band_items[pos_band_alt] if pos_band_alt is not None else None
01122         item.negBandItem = self._band_items[neg_band_alt] if neg_band_alt is not None else None
01123 
01124     def set_snap_item_attributes(self, snapkey, attributes):
01125         # snapkey gets passed as a QString automatically since it goes across
01126         # a signal/slot interface
01127         snapkey = str(snapkey)
01128         self._snap_items[snapkey].set_attributes(attributes)
01129 
01130     def view(self):
01131         return self._view
01132 
01133     def adapter(self):
01134         return self._view.adapter
01135 
01136     def link(self):
01137         log.debug("*** Begining Linking ***")
01138         sys.stdout.flush()
01139         # Create a new anchored layout. Until I can figure out how to remove
01140         # objects from the layout, I need to make a new one each time
01141         l = QGraphicsAnchorLayout()
01142         l.setSpacing(0.0)
01143         self.setLayout(l)
01144 
01145         # Anchor BandStack to Layout, and BlockContainer to BandStack
01146         self.layout().addAnchor(self.block_container, Qt.AnchorTop, self.layout(), Qt.AnchorTop)
01147         self.layout().addAnchor(self.block_container, Qt.AnchorLeft, self.layout(), Qt.AnchorLeft)
01148         self.layout().addAnchor(self.bandStack, Qt.AnchorLeft, self.block_container, Qt.AnchorLeft)
01149         self.layout().addAnchor(self.bandStack, Qt.AnchorRight, self.block_container, Qt.AnchorRight)
01150 
01151         # Link block items
01152         for item in self._block_items.values():
01153             item.link()
01154 
01155         # Link band items
01156         for item in self._band_items.values():
01157             item.link()
01158 
01159         # Link Snap Items
01160         for item in self._snap_items.values():
01161             item.link()
01162 
01163         log.debug("*** Finished Linking ***\n")
01164         sys.stdout.flush()
01165 
01166 #     def mousePressEvent(self, event):
01167 #         print "updating model"
01168 #         self.adapter().update_model()
01169 # 
01170 #     def paint(self, painter, option, widget):
01171 #         painter.setPen(Qt.blue)
01172 #         painter.drawRect(self.rect())
01173 
01174 
01175 class QtView(QGraphicsView, View):
01176     """ This is a Qt based stand-alone widget that provides a visual rendering 
01177     of a Topology. It provides a window into a self contained GraphicsScene in
01178     which we draw the topology. 
01179     It also implements the View interface as a passthrough to the LayoutManager.
01180     """
01181     # Qt Signals. The following signals correspond to diarc.View() API calls that
01182     # are called from outside the main qt thread. Rather then call the implementations
01183     # defined in layout_manager directly, we call them from these signals so that
01184     # the call happens from the correct thread.
01185     __update_view_signal = Signal()
01186 
01187     __add_block_item_signal = Signal(int)
01188     __remove_block_item_signal = Signal(int)
01189     __set_block_item_settings_signal = Signal(int, object, object)
01190     __set_block_item_attributes_signal = Signal(int, BlockItemAttributes)
01191 
01192     __add_band_item_signal = Signal(int, int)
01193     __remove_band_item_signal = Signal(int)
01194     __set_band_item_settings_signal = Signal(int, int, object, object, str, str)
01195     __set_band_item_attributes_signal = Signal(int, BandItemAttributes)
01196 
01197     __add_snap_item_signal = Signal(str)
01198     __remove_snap_item_signal = Signal(str)
01199     __set_snap_item_settings_signal = Signal(str, object, object, object, object)
01200     __set_snap_item_attributes_signal = Signal(str, SnapItemAttributes)
01201 
01202     def __init__(self):
01203         super(QtView, self).__init__(None)
01204         View.__init__(self)
01205 
01206         # Qt properties - Enable click-n-drag paning and initialize Scene
01207         self.setDragMode(QGraphicsView.ScrollHandDrag)
01208         self.setScene(QGraphicsScene(self))
01209 
01210         # Enable for debuging
01211         self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
01212         
01213         # Add the LayoutManagerWidget to the scene
01214         self.layout_manager = LayoutManagerWidget(self)
01215         self.scene().addItem(self.layout_manager)
01216 
01217         # Hook up the signals and slots
01218         self.__update_view_signal.connect(self.layout_manager.link)
01219         self.__add_block_item_signal.connect(self.layout_manager.add_block_item)
01220         self.__remove_block_item_signal.connect(self.layout_manager.remove_block_item)
01221         self.__set_block_item_settings_signal.connect(self.layout_manager.set_block_item_settings)
01222         self.__set_block_item_attributes_signal.connect(self.layout_manager.set_block_item_attributes)
01223         self.__add_band_item_signal.connect(self.layout_manager.add_band_item)
01224         self.__remove_band_item_signal.connect(self.layout_manager.remove_band_item)
01225         self.__set_band_item_settings_signal.connect(self.layout_manager.set_band_item_settings)
01226         self.__set_band_item_attributes_signal.connect(self.layout_manager.set_band_item_attributes)
01227         self.__add_snap_item_signal.connect(self.layout_manager.add_snap_item)
01228         self.__remove_snap_item_signal.connect(self.layout_manager.remove_snap_item)
01229         self.__set_snap_item_settings_signal.connect(self.layout_manager.set_snap_item_settings)
01230         self.__set_snap_item_attributes_signal.connect(self.layout_manager.set_snap_item_attributes)
01231         self.resize(1024,768)
01232         QColor.setAllowX11ColorNames(True)
01233         if not QColor.allowX11ColorNames():
01234             rospy.logwarn("Coloring will not work properly")
01235         self.show()
01236 
01237     def update_view(self):
01238         self.__update_view_signal.emit()
01239 
01240     def add_block_item(self, index):
01241         """ Allows the adapter to create a new BlockItem """
01242         self.__add_block_item_signal.emit(index)
01243 
01244     def has_block_item(self, index):
01245         return self.layout_manager.has_block_item(index)
01246 
01247     def remove_block_item(self, index):
01248         self.__remove_block_item_signal.emit(index)
01249 
01250     def set_block_item_settings(self, index, left_index, right_index):
01251         return self.__set_block_item_settings_signal.emit(index, left_index, right_index)
01252 
01253     def set_block_item_attributes(self, index, attributes):
01254         self.__set_block_item_attributes_signal.emit(index, attributes)
01255 
01256     def add_band_item(self, altitude, rank):
01257         """ Create a new drawable object to correspond to a Band. """
01258         self.__add_band_item_signal.emit(altitude, rank)
01259 
01260     def has_band_item(self, altitude):
01261         return self.layout_manager.has_band_item(altitude)
01262 
01263     def remove_band_item(self, altitude):
01264         """ Remove the drawable object to correspond to a band """ 
01265         self.__remove_band_item_signal.emit(altitude)
01266 
01267     def set_band_item_settings(self, altitude, rank, top_band_alt, bot_band_alt,
01268                                 leftmost_snapkey, rightmost_snapkey):
01269         self.__set_band_item_settings_signal.emit(altitude, rank, top_band_alt, bot_band_alt, leftmost_snapkey, rightmost_snapkey)
01270 
01271     def set_band_item_attributes(self, altitude, attributes):
01272         self.__set_band_item_attributes_signal.emit(altitude, attributes)
01273 
01274     def add_snap_item(self, snapkey):
01275         self.__add_snap_item_signal.emit(snapkey)
01276 
01277     def has_snap_item(self, snapkey):
01278         return self.layout_manager.has_snap_item(snapkey)
01279 
01280     def remove_snap_item(self, snapkey): 
01281         self.__remove_snap_item_signal.emit(snapkey)
01282 
01283     def set_snap_item_settings(self, snapkey, left_order, right_order, pos_band_alt, neg_band_alt):
01284         self.__set_snap_item_settings_signal.emit(snapkey, left_order, right_order, pos_band_alt, neg_band_alt)
01285 
01286     def set_snap_item_attributes(self, snapkey, attributes):
01287         self.__set_snap_item_attributes_signal.emit(snapkey, attributes)
01288 
01289     def wheelEvent(self,event):
01290         """ Implements scrollwheel zooming """
01291         self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
01292         scaleFactor = 1.15
01293         if event.delta() > 0:
01294             self.scale(scaleFactor, scaleFactor)
01295         else:
01296             self.scale(1.0/scaleFactor, 1.0/scaleFactor)
01297 
01298 
01299 
01300 
01301 
01302 
01303 
01304 class DuplicateItemExistsError(Exception):
01305     """ An Item with the specified parameters already exists """
01306     pass


rqt_graphprofiler
Author(s): Dan Brooks
autogenerated on Thu Jun 6 2019 20:29:31