def handleDrag(self, delta):
     if self.draggedHandle == None:
         return 0
     original = self.draggedStart + self.draggedDistance
     projected = original + delta
     if self.draggedAxisIsX:
         if self.draggedBoundIsLow:
             if delta < 0:
                 projected = max(projected,self.scatterBounds[0])
             else:
                 projected = min(projected,self.draggedHandle.parent.rightHandle.left(),self.scatterBounds[2])
         else:
             if delta < 0:
                 projected = max(projected,self.draggedHandle.parent.leftHandle.right(),self.scatterBounds[0])
             else:
                 projected = min(projected,self.scatterBounds[2])
     else:
         if self.draggedBoundIsLow:
             if delta < 0:
                 projected = max(projected,self.draggedHandle.parent.topHandle.bottom(),self.scatterBounds[1])
             else:
                 projected = min(projected,self.scatterBounds[3])
         else:
             if delta < 0:
                 projected = max(projected,self.scatterBounds[1])
             else:
                 projected = min(projected,self.draggedHandle.parent.bottomHandle.top(),self.scatterBounds[3])
                 
     delta = projected - original
     self.draggedDistance = projected - self.draggedStart
     if self.draggedAxisIsX:
         self.draggedHandle.label.setText(fitInSevenChars(self.screenToDataSpace(projected, self.scatterBounds[0], self.currentXaxis.minimum, self.xAxisRatio)))
     else:
         self.draggedHandle.label.setText(fitInSevenChars(self.screenToDataSpace(projected, self.scatterBounds[3], self.currentYaxis.minimum, self.yAxisRatio)))
     return delta
 def notifyAxisChange(self, applyImmediately=True):
     # x axis
     att = self.app.currentXattribute
     self.currentXaxis = self.vData.axisLookups[att]
     low = self.currentXaxis.minimum
     high = self.currentXaxis.maximum
     ax = self.svgLayer.svg.xAxis
     right = self.scatterBounds[2]
     left = self.scatterBounds[0]
     ax.label.setText(att)
     ax.lowLabel.setText(fitInSevenChars(low))
     ax.highLabel.setText(fitInSevenChars(high))
     self.xAxisRatio = float(high - low) / float(right - left)
     if self.xAxisRatio == 0:
         self.xAxisRatio = 1.0 / float(right-left)
     if low <= 0 and high >= 0:
         self.svgLayer.svg.xZeroBar.show()
         self.svgLayer.svg.xZeroBar.moveTo(self.dataToScreenSpace(0.0, left, low, self.xAxisRatio)-self.svgLayer.svg.xZeroBar.width()/2,self.svgLayer.svg.xZeroBar.top())
     else:
         self.svgLayer.svg.xZeroBar.hide()
     
     # y axis
     att = self.app.currentYattribute
     self.currentYaxis = self.vData.axisLookups[att]
     low = self.currentYaxis.minimum
     high = self.currentYaxis.maximum
     ax = self.svgLayer.svg.yAxis
     bottom = self.scatterBounds[3]
     top = self.scatterBounds[1]
     ax.label.setText(att)
     ax.lowLabel.setText(fitInSevenChars(low))
     ax.highLabel.setText(fitInSevenChars(high))
     self.yAxisRatio = float(high - low) / float(top-bottom)
     if self.yAxisRatio == 0:
         self.yAxisRatio = 1.0 / float(top-bottom)
     if low <= 0 and high >= 0:
         self.svgLayer.svg.yZeroBar.show()
         self.svgLayer.svg.yZeroBar.moveTo(self.svgLayer.svg.yZeroBar.left(),self.dataToScreenSpace(0.0, bottom, low, self.yAxisRatio)-self.svgLayer.svg.yZeroBar.height()/2)
     else:
         self.svgLayer.svg.yZeroBar.hide()
     
     if applyImmediately:
         self.notifySelection(self.app.intMan.activePoints, self.app.intMan.activeParams)
         self.allDataLayer.setup()
         self.selectedLayer.updateAxes()
         self.highlightedLayer.updateAxes()
 def updateNumeric(self, ranges):
     if self.dataAxis.hasNumeric:
         while len(self.numericRanges) > len(ranges):
             self.numericRanges[-1].delete()
             del self.numericRanges[-1]
         
         while len(ranges) > len(self.numericRanges):
             self.numericRanges.append(self.numericRanges[-1].clone())
         
         for i,(low,high) in enumerate(ranges):
             v = self.numericRanges[i]
             
             # are parts (or all) of the selection hidden?
             topPixel = self.dataToScreen(high)[0]
             bottomPixel = self.dataToScreen(low)[0]
             
             if topPixel < self.numericPixelHigh or topPixel - v.topHandle.height() > self.numericPixelLow:
                 v.topHandle.hide()
             else:
                 v.topHandle.label.setText(fitInSevenChars(high))
                 v.topHandle.moveTo(v.topHandle.left(),topPixel-v.topHandle.height())
                 v.topHandle.show()
             topPixel = max(topPixel,self.numericPixelHigh)
             topPixel = min(topPixel,self.numericPixelLow)
             
             if bottomPixel > self.numericPixelLow+1 or bottomPixel + v.bottomHandle.height() < self.numericPixelHigh:
                 v.bottomHandle.hide()
             else:
                 v.bottomHandle.label.setText(fitInSevenChars(low))
                 v.bottomHandle.moveTo(v.bottomHandle.left(),bottomPixel)
                 v.bottomHandle.show()
             bottomPixel = min(self.numericPixelLow,bottomPixel)
             bottomPixel = max(self.numericPixelHigh,bottomPixel)
             
             if bottomPixel >= topPixel:
                 v.bar.setSize(v.bar.width(),1)
             
             v.bar.moveTo(v.bar.left(),topPixel)
             v.bar.setSize(v.bar.width(),bottomPixel-topPixel)
             v.bar.show()
 def handleDrag(self, element, delta):
     if self.draggedHandle == None:
         self.startDrag(element)
     original = self.draggedStart + self.draggedDistance
     projected = original + delta
     if self.draggedBoundIsLow:
         if delta < 0:
             projected = max(projected,self.draggedHandle.parent.topHandle.bottom(),self.numericPixelHigh)
         else:
             projected = min(projected,self.numericPixelLow)
     else:
         if delta < 0:
             projected = max(projected,self.numericPixelHigh)
         else:
             projected = min(projected,self.draggedHandle.parent.bottomHandle.top(),self.numericPixelLow)
                 
     delta = projected - original
     self.draggedDistance = projected - self.draggedStart
     
     self.draggedHandle.label.setText(fitInSevenChars(self.screenToData(projected)))
     return delta
 def __init__(self, name, vData, visAxis, visible, parent):
     self.name = name
     self.vData = vData
     self.dataAxis = vData.axisLookups[self.name]
     self.visAxis = visAxis
     self.visible = visible
     self.parent = parent
     
     # handle
     # this is an ugly hack to get around SVG (and QtSvg)'s issues with text
     m = self.name.rfind('(')
     if m == -1:
         filename = ""
         attName = " ".join(self.name.split())
     else:
         filename = self.name[m:].strip()
         attName = " ".join(self.name[:m].strip().split())
     
     row1 = attName[:15]
     row2 = attName[15:]
     if len(row2) > 15:
         row2 = "..." + row2[-12:]
     row3 = filename
     if len(row3) > 15:
         row3 = row3[:12] + "..."
     
     self.visAxis.handle.label1.setText(row1)
     self.visAxis.handle.label2.setText(row2)
     self.visAxis.handle.label3.setText(row3)
     
     # numeric
     self.dataToPixelRatio = 1.0
     self.pixelToDataRatio = 1.0
     self.numericDataLow = self.dataAxis.minimum
     self.numericDataHigh = self.dataAxis.maximum
     self.numericPixelLow = self.visAxis.numeric.scrollDownBar.top()
     self.numericPixelHigh = self.visAxis.numeric.scrollUpBar.bottom()
     self.numericRanges = []
     if not self.dataAxis.hasNumeric:
         #TODO: hide/show numeric area via context menu....
         self.visAxis.numeric.delete()
         self.visAxis.categorical.scrollUpBar.translate(0,self.visAxis.spacer.top()-self.visAxis.categorical.scrollUpBar.top())
     else:
         if self.numericDataLow == self.numericDataHigh: # don't allow a numeric range of zero
             self.numericDataHigh += 1
         self.pixelToDataRatio = float(self.numericDataHigh-self.numericDataLow)/float(self.numericPixelHigh-self.numericPixelLow)
         self.dataToPixelRatio = 1.0/self.pixelToDataRatio
         self.visAxis.numeric.scrollUpBar.label.setText(fitInSevenChars(self.dataAxis.maximum))
         self.visAxis.numeric.scrollDownBar.label.setText(fitInSevenChars(self.dataAxis.minimum))
         self.numericRanges.append(self.visAxis.numeric.selectionGroup.selectionRange)
     
     self.draggedHandle = None
     self.draggedBoundIsLow = None
     self.draggedDistance = None
     self.draggedStart = None
     
     # categorical
     self.rootCatNode = None
     self.tailCatNode = None
     self.catLattice = {-1:set()}    # lattice number to labelHandler; -1 and 1 more than the number of possible spaces are for offscreen, hidden nodes
     self.cats = {}  # label to lattice number
     self.visPool = {'Allele Masked':None,'Missing':None,'Text Items':[]}
     
     # figure out some globals
     self.labelTop = self.visAxis.categorical.scrollUpBar.bottom()
     self.labelBottom = self.visAxis.categorical.scrollDownBar.top()
     self.itemHeight = self.visAxis.categorical.itemGroup.alleleMasked.height()  # allele masked is the tallest one
     
     # sort the items by set membership size, except put Allele Masked, Missing last. Also populate the visPool with the right number of elements
     temp = []
     
     for label in self.dataAxis.categoricalKeys:
         if label == 'Allele Masked' or label == 'Missing':
             continue
         temp.append((self.dataAxis.lookup.count(label),label))
         poolItem = self.visAxis.categorical.itemGroup.textItem.clone()
         self.visPool['Text Items'].append(poolItem)
     temp = sorted(temp)
     self.visAxis.categorical.itemGroup.textItem.delete()
     
     numMasked = self.dataAxis.lookup.count('Allele Masked')
     if numMasked > 0:
         temp.append((numMasked,'Allele Masked'))
         self.visPool['Allele Masked'] = self.visAxis.categorical.itemGroup.alleleMasked
     else:
         self.visAxis.categorical.itemGroup.alleleMasked.delete()
     
     numMissing = self.dataAxis.lookup.count('Missing')
     if numMissing > 0:
         temp.append((numMissing,'Missing'))
         self.visPool['Missing'] = self.visAxis.categorical.itemGroup.missing
     else:
         self.visAxis.categorical.itemGroup.missing.delete()
     
     # Figure out some more globals now that we know how many/which items we have
     self.numVisibleCats = len(temp)
     self.allCatsVisible = self.maximizeLatticeSpace()
     
     # create nodes from the end of the list to the beginning - we know the bottom scroll arrow will start hidden
     self.visAxis.categorical.scrollDownBar.hide()
     if self.allCatsVisible:
         self.visAxis.categorical.scrollUpBar.hide()
     self.catLattice[self.latticeLength] = set()
     
     latticeNumber = self.latticeLength-1
     if self.numVisibleCats == 0:
         self.rootCatNode = None
     else:
         self.rootCatNode = labelHandler(temp[-1][1], latticeNumber)
         self.cats[temp[-1][1]] = latticeNumber
         self.assignVisElement(self.rootCatNode, None)
         
         lastNode = self.rootCatNode
         latticeNumber -= 1
         for size,label in reversed(temp[:-1]):
             newNode = labelHandler(label,latticeNumber)
             self.cats[label] = latticeNumber
             if latticeNumber > -1:
                 self.assignVisElement(newNode, lastNode)
                 self.catLattice[latticeNumber] = newNode
                 latticeNumber -= 1
                 if latticeNumber == 0:
                     self.tailCatNode = newNode
             else:
                 self.catLattice[-1].add(newNode)
             lastNode.p = newNode
             newNode.n = lastNode
             lastNode = newNode
     # hide everything that's unused in the visPool
     for v in self.visPool['Text Items']:
         v.hide()
 def notifySelection(self, activePoints, activeParams):
     self.selectedLayer.update(activePoints)
     self.selectedLayer.setDirty()
     # pull out the sizes of things once before we manipulate everything - this should help minimize re-rendering
     xAtt = self.app.currentXattribute
     numericLeftPixel = self.scatterBounds[0]
     numericRightPixel = self.scatterBounds[2]
     
     rightHandleSize = self.xRanges[0].rightHandle.width()
     rightHandleTop = self.xRanges[0].rightHandle.top()
     leftHandleSize = self.xRanges[0].leftHandle.width()
     leftHandleTop = self.xRanges[0].leftHandle.top()
     xBarSize = self.xRanges[0].bar.height()
     xBarTop = self.xRanges[0].bar.top()
     
     yAtt = self.app.currentYattribute
     numericBottomPixel = self.scatterBounds[3]
     numericTopPixel = self.scatterBounds[1]
     topHandleSize = self.yRanges[0].topHandle.height()
     topHandleLeft = self.yRanges[0].topHandle.left()
     bottomHandleSize = self.yRanges[0].bottomHandle.height()
     bottomHandleLeft = self.yRanges[0].bottomHandle.left()
     yBarSize = self.yRanges[0].bar.width()
     yBarLeft = self.yRanges[0].bar.left()
     
     # remove, duplicate the number of svg selection groups to fit the data
     # x axis
     i = len(self.xRanges) - 1
     while len(self.xRanges) > len(activeParams[xAtt][0]):
         self.xRanges[i].delete()
         del self.xRanges[i]
         i -= 1
     
     while len(self.xRanges) < len(activeParams[xAtt][0]):
         self.xRanges.append(self.xRanges[0].clone())
     # y axis
     i = len(self.yRanges) - 1
     while len(self.yRanges) > len(activeParams[yAtt][0]):
         self.yRanges[i].delete()
         del self.yRanges[i]
         i -= 1
     
     while len(self.yRanges) < len(activeParams[yAtt][0]):
         self.yRanges.append(self.yRanges[0].clone())
     
     # adjust each selection group to the data
     # x axis
     for i,r in enumerate(activeParams[xAtt][0]):
         l = r[0]
         h = r[1]
         
         v = self.xRanges[i]
         
         # are parts (or all) of the selection hidden?
         rightPixel = numericRightPixel - float(self.currentXaxis.maximum-h)/self.xAxisRatio
         leftPixel = numericLeftPixel + float(l-self.currentXaxis.minimum)/self.xAxisRatio
         
         if rightPixel + rightHandleSize < numericLeftPixel or rightPixel > numericRightPixel:
             v.rightHandle.hide()
         else:
             v.rightHandle.label.setText(fitInSevenChars(h))
             v.rightHandle.moveTo(rightPixel,rightHandleTop)
             v.rightHandle.show()
         rightPixel = max(rightPixel,numericLeftPixel)
         rightPixel = min(rightPixel,numericRightPixel)
         
         if leftPixel < numericLeftPixel or leftPixel - leftHandleSize > numericRightPixel:
             v.leftHandle.hide()
         else:
             v.leftHandle.label.setText(fitInSevenChars(l))
             v.leftHandle.moveTo(leftPixel - leftHandleSize, leftHandleTop)
             v.leftHandle.show()
         leftPixel = min(numericRightPixel,leftPixel)
         leftPixel = max(numericLeftPixel,leftPixel)
         
         if leftPixel >= rightPixel:
             v.bar.setSize(1,xBarSize)
         
         v.bar.moveTo(leftPixel,xBarTop)
         v.bar.setSize(rightPixel-leftPixel,xBarSize)
         v.bar.show()
     # y axis
     for i,r in enumerate(activeParams[yAtt][0]):
         l = r[0]
         h = r[1]
         
         v = self.yRanges[i]
         
         # are parts (or all) of the selection hidden?
         topPixel = self.dataToScreenSpace(h, self.scatterBounds[3], self.currentYaxis.minimum, self.yAxisRatio)
         bottomPixel = self.dataToScreenSpace(l, self.scatterBounds[3], self.currentYaxis.minimum, self.yAxisRatio)
         
         if topPixel - topHandleSize > numericBottomPixel or topPixel < numericTopPixel:
             v.topHandle.hide()
         else:
             v.topHandle.label.setText(fitInSevenChars(h))
             v.topHandle.moveTo(topHandleLeft,topPixel - topHandleSize)
             v.topHandle.show()
         topPixel = max(topPixel,numericTopPixel)
         topPixel = min(topPixel,numericBottomPixel)
         
         if bottomPixel > numericBottomPixel or bottomPixel + bottomHandleSize < numericTopPixel:
             v.bottomHandle.hide()
         else:
             v.bottomHandle.label.setText(fitInSevenChars(l))
             v.bottomHandle.moveTo(bottomHandleLeft,bottomPixel)
             v.bottomHandle.show()
         bottomPixel = min(bottomPixel,numericBottomPixel)
         bottomPixel = max(bottomPixel,numericTopPixel)
         
         if topPixel >= bottomPixel:
             v.bar.setSize(yBarSize,1)
         
         v.bar.moveTo(yBarLeft,topPixel)
         v.bar.setSize(yBarSize,bottomPixel-topPixel)
         v.bar.show()