Changeset 6978:8bc20880353f in orange


Ignore:
Timestamp:
09/20/10 11:05:55 (4 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
9e7a416d53f19d21636611bd1c89e7296278b5e7
Message:
  • restructured SOM code
  • added component plane and histogram legend
File:
1 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/Unsupervised/OWSOMVisualizer.py

    r6814 r6978  
    1616BaseColor=QColor(0, 0, 200) 
    1717 
     18from orngMisc import ColorPalette 
     19from functools import partial 
     20 
    1821class GraphicsSOMItem(QGraphicsPolygonItem): 
    1922    startAngle=0 
     
    2225        QGraphicsPolygonItem.__init__(self, *args) 
    2326        self.node=None 
    24         self.hasNode=False 
    25         self.isSelected=False 
    2627        self.labelText="" 
    2728        self.textColor=Qt.black 
    2829        self.defaultPen = QPen(Qt.black) 
    2930        self.outlinePoints=None 
    30         self.histObj=[] 
     31        self.histogramConstructor = None 
     32        self.histogramItem = None 
    3133        self.setSize(10) 
    3234        self.setZValue(0) 
     35#        self.setFlags(QGraphicsItem.ItemIsSelectable) 
    3336 
    3437    def areaPoints(self): 
     
    4144        self.setPolygon(QPolygonF(self.outlinePoints)) 
    4245 
    43     def setPie(self, attrIndex, size): 
    44         for e in self.histObj: 
    45             self.scene().removeItem(e) 
    46         self.histObj = [] 
    47         if len(self.node.examples) < 0: 
    48             return 
    49         dist = orange.Distribution(attrIndex, self.node.examples) 
    50         colors = OWColorPalette.ColorPaletteHSV(len(dist)) 
    51         distSum = max(sum(dist), 1) 
    52         startAngle = 0 
    53         for i in range(len(dist)): 
    54             angle = 360/distSum*dist[i]*16 
    55             c = QGraphicsEllipseItem(-size/2, -size/2, size, size, self, self.scene()) 
    56             c.setStartAngle(startAngle) 
    57             c.setSpanAngle(angle) 
    58             c.setBrush(QBrush(colors[i])) 
    59             c.show() 
    60             startAngle += angle 
    61             self.histObj.append(c)   
    62  
    63     def setHist(self, size): 
    64         for e in self.histObj: 
    65             self.scene().removeItem(e) 
    66         self.histObj = [] 
    67         c = QGraphicsEllipseItem(-size/2, -size/2, size, size, self, self.scene()) 
    68         c.setBrush(QBrush(DefColor)) 
    69         c.setPen(QPen(Qt.white)) 
    70         c.show() 
    71         self.histObj.append(c) 
    72  
    73     def setLabel(self, label): 
    74         self.labelText = label 
    75  
    7646    def setColor(self, color): 
    7747        self.color = color 
     
    8353        self.node = node 
    8454        self.hasNode = True 
    85         self.buildToolTip() 
    86  
    87     def setSelected(self, bool=True): 
    88         self.isSelected = bool 
    89         self.setPen(QPen(Qt.red, 2) if bool else self.defaultPen) 
    90         self.setZValue(1 if bool else 0) 
     55        self.setFlags(QGraphicsItem.ItemIsSelectable if node else 0) 
     56        self.updateToolTips() 
    9157 
    9258    def advancement(self): 
    9359        pass 
    9460 
    95     def buildToolTip(self): 
    96         if self.node and self.scene().showToolTip: 
     61    def updateToolTips(self, showToolTips=True, includeCodebook=True): 
     62        if self.node and showToolTips: 
    9763            node = self.node 
    9864            text = "Items: %i" % len(self.node.examples) 
    99             if self.scene().includeCodebook: 
     65            if includeCodebook: 
    10066                text += "<hr><b>Codebook vector:</b><br>" + "<br>".join(\ 
    10167                    [a.variable.name + ": " + str(a) for a in node.referenceExample \ 
     
    11379        else: 
    11480            self.setToolTip("") 
    115         
    116 ##    def drawShape(self, painter): 
    117 ##        QCanvasPolygon.drawShape(self, painter) 
    118 ##        if self.canvas().showGrid or self.isSelected: 
    119 ##            color=self.isSelected and Qt.blue or Qt.black 
    120 ##            p=QPen(color) 
    121 ##            if self.isSelected: 
    122 ##                p.setWidth(3) 
    123 ##            painter.setPen(p) 
    124 ##            painter.drawPolyline(self.outlinePoints) 
    125 ##            painter.drawLine(self.outlinePoints.point(0)[0],self.outlinePoints.point(0)[1], 
    126 ##                             self.outlinePoints.point(self.segments-1)[0],self.outlinePoints.point(self.segments-1)[1]) 
    127          
     81         
     82    def setComponentPlane(self, component): 
     83        colorSchema = self.colorSchema(component) 
     84        if component is not None: 
     85            val = self.node.referenceExample[component] 
     86            color = colorSchema(val) 
     87        else: 
     88            color = QColor(Qt.white) 
     89        self.setBrush(QBrush(color)) 
     90         
     91    @property 
     92    def colorSchema(self): 
     93        return self.parentItem().colorSchema 
     94     
     95    @property 
     96    def histogramColorSchema(self): 
     97        return self.parentItem().histogramColorSchema 
     98     
     99    def setHistogramConstructor(self, constructor): 
     100        if getattr(self, "histogramItem", None) is not None: 
     101            self.scene().removeItem(self.histogramItem) 
     102            self.histogramItem = None 
     103        self.histogramConstructor = constructor 
     104        self.updateHistogram() 
     105         
     106    def updateHistogram(self): 
     107        if self.histogramItem is not None: 
     108            self.scene().removeItem(self.histogramItem) 
     109            self.histogramItem = None 
     110        if self.histogramConstructor and self.node and self.node.mappedExamples: 
     111            self.histogramItem = self.histogramConstructor(self.node.mappedExamples, self) 
     112            self.histogramItem.setParentItem(self) 
     113            # center the item 
     114            rect = self.histogramItem.boundingRect() 
     115             
     116            self.histogramItem.setPos(-rect.center()) 
     117             
     118    def paint(self, painter, option, widget=0): 
     119        painter.setBrush(self.brush()) 
     120        if self.isSelected(): 
     121            painter.setPen(QPen(Qt.red, 2)) 
     122        else: 
     123            painter.setPen(self.pen()) 
     124        painter.drawPolygon(self.polygon()) 
     125         
     126    def itemChange(self, change, value): 
     127        if change == QGraphicsItem.ItemSelectedHasChanged: 
     128            self.setZValue(self.zValue() + (1 if value.toPyObject() else -1)) 
     129        return QGraphicsPolygonItem.itemChange(self, change, value) 
     130                         
    128131class GraphicsSOMHexagon(GraphicsSOMItem): 
    129132    startAngle = 0 
     
    144147        return (x,y) 
    145148     
    146 baseColor = QColor(20,20,20)   
    147  
    148 class SOMScene(QGraphicsScene): 
    149     def __init__(self, master, *args): 
    150         QGraphicsScene.__init__(self, *args) 
    151         self.master = master 
    152         self.drawMode = 1 
    153         self.showGrid = True 
    154         self.component = 0 
    155         self.objSize = 25 
    156         self.canvasObj = [] 
    157         self.selectionList = [] 
    158         self.selectionRect = None 
    159         self.showToolTip = True 
    160         self.includeCodebook = True 
    161         self.somMap = None 
    162  
    163     def drawHistogram(self): 
    164         if self.parent().inputSet: 
    165             maxVal = max([len(n.mappedExamples) for n in self.somMap.map] + [1]) 
    166         else: 
    167             maxVal = max([len(n.examples) for n in self.somMap.map] + [1]) 
    168         if self.drawMode == 1: 
    169             maxSize = 4*int(self.somMap.map_shape[0]*self.objSize/(2*self.somMap.map_shape[0] - 1)*0.7) 
    170         else: 
    171             maxSize = 4*int(self.objSize*0.7) 
    172         for n in self.canvasObj: 
    173             if n.hasNode: 
    174                 if self.parent().inputSet: 
    175                     size = float(len(n.node.mappedExamples))/maxVal*maxSize 
    176                 else: 
    177                     size = float(len(n.node.examples))/maxVal*maxSize 
    178                 if self.parent().drawPies(): 
    179                     n.setPie(self.parent().attribute, size) 
    180                 else: 
    181                     n.setHist(size) 
    182                      
    183         self.updateHistogramColors()             
    184  
    185     def updateHistogramColors(self): 
    186         if self.parent().drawPies(): 
    187             return 
    188         attr = self.somMap.examples.domain.variables[self.parent().attribute] 
    189         for n in self.canvasObj: 
    190             if n.hasNode: 
    191                 if attr.varType == orange.VarTypes.Discrete: 
    192                     if self.parent().inputSet: 
    193                         dist = orange.Distribution(attr, n.node.mappedExamples) 
    194                     else: 
    195                         dist = orange.Distribution(attr, n.node.examples) 
    196                     colors = OWColorPalette.ColorPaletteHSV(len(dist)) 
    197                     maxProb = max(dist) 
    198                     majValInd = filter(lambda i:dist[i] == maxProb, range(len(dist)))[0] 
    199                     if self.parent().discHistMode == 1: 
    200                         n.histObj[0].setBrush(QBrush(colors[majValInd])) 
    201                     elif self.parent().discHistMode == 2: 
    202                         light = 180 - 80*float(dist[majValInd])/max(sum(dist), 1) 
    203                         n.histObj[0].setBrush(QBrush(colors[majValInd].light(light))) 
    204                 else: 
    205                     if self.parent().inputSet: 
    206                         dist=orange.Distribution(attr, n.node.mappedExamples) 
    207                         fullDist=orange.Distribution(attr, self.parent().examples) 
    208                     else: 
    209                         dist=orange.Distribution(attr, n.node.examples) 
    210                         fullDist=orange.Distribution(attr, self.somMap.examples) 
    211                     if len(dist)==0: 
    212                         continue 
    213                      
    214                     if self.parent().contHistMode==0: 
    215                         n.histObj[0].setBrush(QBrush(DefColor)) 
    216                     if self.parent().contHistMode==1: 
    217                         std=(dist.average()-fullDist.average())/max(fullDist.dev(),1) 
    218                         std=min(max(std,-1),1) 
    219                         #print std 
    220                         n.histObj[0].setBrush(QBrush(QColor(70*(std+1)+50, 70*(std+1)+50, 0)))                            
    221                     if self.parent().contHistMode==2: 
    222                         light = 300-200*dist.var()/fullDist.var() 
    223                         n.histObj[0].setBrush(QBrush(QColor(0,0,20).light(light))) 
    224  
    225     def updateToolTips(self): 
    226         for item in self.canvasObj: 
    227             item.buildToolTip() 
    228      
    229     def setSom(self, somMap=None): 
    230         self.oldSom=self.somMap 
    231         self.somMap=somMap 
    232         self.clear() 
    233         if not self.somMap: 
    234             return 
    235          
    236         self.somNodeMap={} 
    237         for n in somMap.map: 
    238 ##            self.somNodeMap[(n.x,n.y)]=n 
    239             self.somNodeMap[tuple(n.pos)]=n 
    240  
    241         if self.drawMode==1: 
    242 ##            self.uMat=orngSOM.getUMat(somMap) 
    243             self.uMat = somMap.map.getUMat() 
    244             if somMap.topology==orngSOM.HexagonalTopology: 
    245                 self.drawUMatHex() 
    246             else: 
    247                 self.drawUMatRect() 
    248         else: 
    249             if somMap.topology == orngSOM.HexagonalTopology: 
    250                 self.drawHex() 
    251             else: 
    252                 self.drawRect() 
    253             if self.drawMode!=0: 
    254                 minVal=min([n.vector[self.component] for n in self.somMap.map]) 
    255                 maxVal=max([n.vector[self.component] for n in self.somMap.map]) 
    256                 for o in self.canvasObj: 
    257                     val=255-max(min(255*(o.node.vector[self.component]-minVal)/(maxVal-minVal),245),10) 
    258                     o.setColor(QColor(val,val,val)) 
    259         if (self.parent().inputSet==0 or self.parent().examples) and self.parent().histogram: 
    260             self.drawHistogram() 
    261         self.updateLabels() 
    262          
    263     def redrawSom(self):    #for redrawing without clearing the selection  
    264         if not self.somMap: 
    265             return 
    266         oldSelection = self.parent().scene.selectionList 
    267         self.setSom(self.somMap) 
    268         nodeList = [n.node for n in oldSelection] 
    269         newSelection = [] 
    270         for o in self.canvasObj: 
    271             if o.node in nodeList: 
    272                 o.setSelected(True) 
    273                 newSelection.append(o) 
    274         self.parent().sceneView.selectionList = newSelection 
    275         self.update() 
    276      
    277     def drawHex(self): 
    278         #size=self.objSize*2-1 
    279         #size=int((self.objSize-9)/10.0*20)*2-1 
     149class PieChart(QGraphicsEllipseItem): 
     150    def __init__(self, attr, examples, *args, **kwargs): 
     151        QGraphicsEllipseItem.__init__(self, *args) 
     152        self.distribution = orange.Distribution(attr, examples) 
     153        self.setRect(0.0, 0.0, 20.0, 20.0) 
     154         
     155    def paint(self, painter, option, widget=0): 
     156        start, stop = self.startAngle(), self.startAngle() + self.spanAngle() 
     157        span = stop - start 
     158        distsum = sum(self.distribution) 
     159        angle = start 
     160        dspan = span / distsum 
     161        colorSchema = self.colorSchema 
     162        for i, count in enumerate(self.distribution):  
     163            color = colorSchema(i) 
     164            painter.setBrush(QBrush(color)) 
     165            arcSpan = count * dspan 
     166            painter.drawPie(self.rect(), angle, arcSpan) 
     167            angle = angle + arcSpan 
     168             
     169    @property 
     170    def colorSchema(self): 
     171        try: 
     172            self.no_attr 
     173            return self.parentItem().histogramColorSchema 
     174        except AttributeError: 
     175            return lambda val: OWColorPalette.ColorPaletteHSV(len(self.distribution))[val] 
     176         
     177    @classmethod 
     178    def legendItemConstructor(cls, attr, examples, *args, **kwargs): 
     179        return DiscreteLegendItem(attr, examples, *args, **kwargs) 
     180         
     181class MajorityChart(PieChart): 
     182    def __init__(self, *args, **kwargs): 
     183        PieChart.__init__(self, *args, **kwargs) 
     184        self.majorityValue = self.distribution.modus() 
     185        index = int(numpy.argmax(list(self.distribution))) 
     186        self.setBrush(QBrush(self.colorSchema(index))) 
     187         
     188    def paint(self, painter, option, widget=0): 
     189        QGraphicsEllipseItem.paint(self, painter, option, widget) 
     190         
     191class MajorityProbChart(MajorityChart): 
     192    def __init__(self, *args, **kwargs): 
     193        MajorityChart.__init__(self, *args, **kwargs) 
     194        index = int(numpy.argmax(list(self.distribution))) 
     195        val = self.distribution[self.majorityValue] 
     196        prob = float(val) / sum(self.distribution) 
     197#        colorSchema = self.colorSchema(self.distribution.variable) 
     198        color = self.colorSchema(index) 
     199        color = color.lighter(200 - 100 * prob) 
     200        self.setBrush(QColor(color)) 
     201         
     202class ContChart(QGraphicsEllipseItem): 
     203    def __init__(self, attr, examples, *args, **kwargs): 
     204        QGraphicsEllipseItem.__init__(self, *args) 
     205        self.distribution = orange.Distribution(attr, examples) 
     206        self.setRect(0, 0, 20, 20) 
     207        self.setBrush(QBrush(DefColor)) 
     208         
     209    @property 
     210    def colorSchema(self): 
     211        try: 
     212            return self.parentItem().histogramColorSchema 
     213        except AttributeError, ex: 
     214            raise 
     215     
     216    @classmethod 
     217    def legendItemConstructor(cls, attr, examples, *args, **kwargs): 
     218        return LegendItem(attr, examples, *args, **kwargs) 
     219         
     220class AverageValue(ContChart): 
     221    def __init__(self, *args, **kwargs): 
     222        ContChart.__init__(self, *args, **kwargs) 
     223        try: 
     224            self.average = self.distribution.average() 
     225        except orange.KernelException: 
     226            self.average = None 
     227        colorSchema = self.colorSchema(self.distribution.variable) 
     228        color = lambda col: col if isinstance(col, QColor) else QColor(*col) 
     229        color = color(colorSchema(self.average)) if self.average is not None else Qt.white 
     230        self.setBrush(QBrush(color)) 
     231         
     232    @classmethod 
     233    def legendItemConstructor(cls, attr, examples, *args, **kwargs): 
     234        item = LegendItem(attr, examples, *args, **kwargs) 
     235        if examples: 
     236            dist = orange.Distribution(attr, examples) 
     237            minval, maxval = min(dist.keys()), max(dist.keys()) 
     238            item.setScale((minval, maxval)) 
     239        return item 
     240     
     241class SOMMapItem(QAbstractGraphicsShapeItem): 
     242    """ A SOM graphics object  
     243    """ 
     244    objSize = 15 
     245    def __init__(self, SOMMap=None, w=None, h=None, parent=None, scene=None): 
     246        QAbstractGraphicsShapeItem.__init__(self, parent, None) 
     247        self.histogramData = None 
     248        if map is not None: 
     249            self.setSOMMap(SOMMap) 
     250             
     251        if scene is not None: 
     252            scene.addItem(self) 
     253             
     254    def setSOMMap(self, map): 
     255        if map is not None: 
     256            if map.topology == orngSOM.HexagonalTopology: 
     257                self._setHexagonalMap(map) 
     258            elif map.topology == orngSOM.RectangularTopology: 
     259                self._setRectangularMap(map) 
     260        else: 
     261            self.map = None 
     262        self.prepareGeometryChange() 
     263         
     264    def _setHexagonalMap(self, map): 
     265        self.map = map 
     266        xdim, ydim = map.map_shape 
    280267        size = 2*self.objSize - 1 
    281268        x, y = size, size*2 
    282         for n in self.somMap.map: 
     269        for n in self.map.map: 
    283270            offset = 1 - abs(int(n.pos[0])%2 - 2) 
    284             h=GraphicsSOMHexagon(None, self) 
     271            h=GraphicsSOMHexagon(self) 
    285272            h.setSize(size) 
    286273            h.setNode(n) 
    287274            (xa, ya) = h.advancement() 
    288 ##            h.move(x+n.x*xa, y+n.y*ya+offset*ya/2) 
    289275            h.setPos(x + n.pos[0]*xa, y + n.pos[1]*ya + offset*ya/2) 
    290276            h.show() 
    291             self.canvasObj.append(h) 
    292         self.setSceneRect(self.itemsBoundingRect()) 
    293         self.update() 
    294      
    295     def drawRect(self): 
     277         
     278    def _setRectangularMap(self, map): 
     279        self.map = map 
    296280        size=self.objSize*2-1 
    297281        x,y=size, size 
    298 ##        self.resize(1,1)    # crashes at update without this line !!! 
    299         for n in self.somMap.map: 
    300             r=GraphicsSOMRectangle(None, self) 
     282##            self.resize(1,1)    # crashes at update without this line !!! 
     283        for n in self.map.map: 
     284            r=GraphicsSOMRectangle(self) 
    301285            r.setSize(size) 
    302286            r.setNode(n) 
    303287            (xa,ya) = r.advancement() 
    304 ##            r.move(x+n.x*xa, y+n.y*ya) 
    305288            r.setPos(x + n.pos[0]*xa, y + n.pos[1]*ya) 
    306289            r.show() 
    307             self.canvasObj.append(r) 
    308         self.setSceneRect(self.itemsBoundingRect()) 
    309         self.update() 
    310      
    311     def drawUMatHex(self): 
    312         #size=2*(int(self.objSize*1.15)/2)-1 
    313         size=2*int(self.somMap.map_shape[0]*self.objSize/(2*self.somMap.map_shape[0]-1))-1 
     290             
     291    def boundingRect(self): 
     292        return self.childrenBoundingRect() 
     293         
     294    def paint(self, painter, options, widget=0): 
     295        pass 
     296     
     297    def setComponentPlane(self, component): 
     298        for node in self.nodes(): 
     299            node.setComponentPlane(component) 
     300             
     301    def setHistogram(self, attr): 
     302        for node in self.nodes(): 
     303            node.setHistogram(attr) 
     304         
     305    def nodes(self): 
     306        for item in self.childItems(): 
     307            if isinstance(item, GraphicsSOMItem): 
     308                yield item 
     309                 
     310    def setColorSchema(self, schema): 
     311        self._colorSchema = schema 
     312         
     313    @property 
     314    def colorSchema(self): 
     315        if hasattr(self, "_colorSchema"): 
     316            return self._colorSchema 
     317        else: 
     318            def colorSchema(attr): 
     319                if attr is None: 
     320                    return lambda val: QColor(Qt.white) 
     321                elif type(attr) == int: 
     322                    attr = self.map.examples.domain[attr] 
     323                 
     324                if attr.varType == orange.VarTypes.Discrete and False: 
     325                    return lambda val: OWColorPalette.ColorPaletteHSV(self.map.examples.domain[attr].values)[val] 
     326                else: 
     327                    index = self.map.examples.domain.index(attr) 
     328                    vals = [n.vector[index] for n in self.map.map] 
     329                    minval, maxval = min(vals), max(vals) 
     330                    return lambda val: OWColorPalette.ColorPaletteBW()[ 1 - (val - minval) / (maxval - minval or 1)]   
     331            return colorSchema 
     332         
     333    def setHistogramColorSchema(self, schema): 
     334        self._histogramColorSchema = schema 
     335         
     336    @property 
     337    def histogramColorSchema(self): 
     338        if hasattr(self, "_histogramColorSchema"): 
     339            def colorSchema(attr): 
     340                if attr is None: 
     341                    return lambda val: QColor(Qt.white) 
     342                elif type(attr) == int: 
     343                    attr = self.map.examples.domain[attr] 
     344                     
     345                if attr.varType == orange.VarTypes.Discrete: 
     346                    return schema 
     347                else: 
     348                    index = self.map.examples.domain.index(attr) 
     349                    arr, c, w = self.histogramData.toNumpyMA() 
     350                    vals = arr[:,index] 
     351                    minval, maxval = min(vals), max(vals) 
     352                    return lambda val: self._histogramColorSchema(1 - (val - minval) / (maxval - minval or 1)) 
     353            return colorSchema 
     354        else: 
     355            def colorSchema(attr): 
     356                if attr is None: 
     357                    return lambda val: QColor(Qt.white) 
     358                elif type(attr) == int: 
     359                    attr = self.map.examples.domain[attr] 
     360                 
     361                if attr.varType == orange.VarTypes.Discrete: 
     362                    return lambda val: OWColorPalette.ColorPaletteHSV(self.map.examples.domain[attr].values)[val] 
     363                else: 
     364                    index = self.map.examples.domain.index(attr) 
     365                    vals = [n.vector[index] for n in self.map.map] 
     366                    minval, maxval = min(vals), max(vals) 
     367                    return lambda val: OWColorPalette.ColorPaletteBW()[1 - (val - minval) / (maxval - minval or 1)]   
     368            return colorSchema 
     369         
     370    def componentRange(self, attr = None): 
     371        vectors = self.map.map.vectors() 
     372        range = zip(numpy.min(vectors, axis=0), numpy.max(vectors, axis=0)) 
     373        if attr is not None: 
     374            return range[attr] 
     375        else: 
     376            return range 
     377         
     378    def setNodePen(self, pen): 
     379        for node in self.nodes(): 
     380            node.setPen(pen) 
     381             
     382    def setHistogramData(self, data): 
     383        self.histogramData = data 
     384        for node in self.map: 
     385            node.mappedExamples = orange.ExampleTable(data.domain) 
     386        for example in data: 
     387            bmn = self.map.getBestMatchingNode(example) 
     388            bmn.mappedExamples.append(example) 
     389             
     390        self.updateHistogram() 
     391 
     392    def setHistogramConstructor(self, constructor): 
     393        for item in self.nodes(): 
     394            item.setHistogramConstructor(constructor) 
     395        self.updateHistogramSize() 
     396             
     397    def updateHistogram(self): 
     398        for item in self.nodes(): 
     399            item.updateHistogram() 
     400        self.updateHistogramSize() 
     401         
     402    def updateHistogramSize(self): 
     403        if not self.histogramData: 
     404            return 
     405        maxmapped = max([len(getattr(node.node, "mappedExamples", [])) for node in self.nodes()]) 
     406        for node in self.nodes(): 
     407            if node.node is not None and node.node.mappedExamples and node.histogramItem is not None: 
     408                mapped = len(node.node.mappedExamples) 
     409                size = node.boundingRect().width() * 0.75 
     410                size = size  * mapped / maxmapped 
     411                rect = QRectF(0.0, 0.0, size, size) 
     412                rect.moveCenter(node.histogramItem.rect().center()) 
     413                node.histogramItem.setRect(rect) 
     414                 
     415    def updateToolTips(self, *args, **kwargs): 
     416        for node in self.nodes(): 
     417            node.updateToolTips(*args, **kwargs) 
     418           
     419class SOMUMatrixItem(SOMMapItem): 
     420    """ A SOM U-Matrix graphics object 
     421    """ 
     422    def setSOMMap(self, map): 
     423        self.map = map 
     424        self.somNodeMap = dict([(tuple(n.pos), n) for n in self.map]) 
     425        if self.map.topology==orngSOM.HexagonalTopology: 
     426            self._setHexagonalUMat(map) 
     427        elif self.map.topology==orngSOM.RectangularTopology: 
     428            self._setRectangularUMat(map) 
     429        self.prepareGeometryChange() 
     430         
     431    def _setHexagonalUMat(self, map): 
     432        self.map = map  
     433        self.uMat = orngSOM.getUMat(map) 
     434        size=2*int(self.map.map_shape[0]*self.objSize/(2*self.map.map_shape[0]-1))-1 
    314435        x,y=size, size 
    315436        maxDist=max(reduce(numpy.maximum, [a for a in self.uMat])) 
    316437        minDist=max(reduce(numpy.minimum, [a for a in self.uMat])) 
    317438        for i in range(len(self.uMat)): 
    318             offset=2-abs(i%4-2) 
     439            offset = 2 - abs(i % 4 - 2) 
    319440            for j in range(len(self.uMat[i])): 
    320                 h=GraphicsSOMHexagon(None, self) 
     441                h=GraphicsSOMHexagon(self) 
    321442                h.setSize(size) 
    322443                (xa,ya)=h.advancement() 
     
    327448                val=255-min(max(255*(self.uMat[i][j]-minDist)/(maxDist-minDist),10),245) 
    328449                h.setColor(QColor(val, val, val)) 
    329                 self.canvasObj.append(h) 
    330         self.setSceneRect(self.itemsBoundingRect()) 
    331         self.update() 
    332          
    333     def drawUMatRect(self): 
    334         #size=self.objSize-1 
    335         size=2*int(self.somMap.map_shape[0]*self.objSize/(2*self.somMap.map_shape[0]-1))-1 
     450                 
     451    def _setRectangularUMat(self, map): 
     452        self.map = map 
     453        self.uMat = orngSOM.getUMat(map) 
     454        size=2*int(self.map.map_shape[0]*self.objSize/(2*self.map.map_shape[0]-1))-1 
    336455        x,y=size, size 
    337456         
     
    340459        for i in range(len(self.uMat)): 
    341460            for j in range(len(self.uMat[i])): 
    342                 r=GraphicsSOMRectangle(None, self) 
     461                r=GraphicsSOMRectangle(self) 
    343462                r.setSize(size) 
    344463                if i%2==0 and j%2==0: 
     
    349468                val=255-min(max(255*(self.uMat[i][j]-minDist)/(maxDist-minDist),10),245) 
    350469                r.setColor(QColor(val, val, val)) 
    351                 self.canvasObj.append(r) 
    352         self.setSceneRect(self.itemsBoundingRect()) 
     470 
     471class LayoutItemWrapper(QGraphicsWidget): 
     472    def __init__(self, item, parent=None): 
     473        self.item = item 
     474        QGraphicsWidget.__init__(self, parent) 
     475        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 
     476         
     477    def setGeometry(self, rect): 
     478        self.item.setPos(rect.topLeft()) 
     479        return QGraphicsWidget.setGeometry(self, rect) 
     480     
     481    def sizeHint(self, which, constraint): 
     482        return self.item.boundingRect().size() 
     483         
     484class AxisItem(QGraphicsWidget): 
     485    orientation = Qt.Horizontal 
     486    tickAlign = Qt.AlignBottom 
     487    textAlign = Qt.AlignHCenter | Qt.AlignBottom 
     488    axisScale = (0.0, 1.0) 
     489    def __init__(self, parent=None): 
     490        QGraphicsWidget.__init__(self, parent)  
     491        self.tickCount = 5 
     492         
     493    def setOrientation(self, orientation): 
     494        self.prepareGeometryChange() 
     495        self.orientation = orientation 
     496        if self.orientation == Qt.Horizontal: 
     497            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) 
     498        else: 
     499            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) 
     500        self.updateGeometry() 
     501         
     502    def ticks(self): 
     503        minval, maxval = self.axisScale 
     504        ticks = ["%.2f" % val for val in numpy.linspace(minval, maxval, self.tickCount)] 
     505        return ticks 
     506     
     507    def paint(self, painter, option, widget=0): 
     508        painter.setFont(self.font()) 
     509        size = self.geometry().size() 
     510        metrics = QFontMetrics(painter.font()) 
     511        minval, maxval = self.axisScale 
     512        tickCount = 5 
     513         
     514        if self.orientation == Qt.Horizontal: 
     515            spanx, spany = 0.0, size.width() 
     516            xadv, yadv =  spanx/ tickCount, 0.0 
     517            tick_w, tick_h = 0.0, 5.0 
     518            tickOffset = QPointF(0.0, 0.0) 
     519        else: 
     520            spanx, spany = 0.0, size.height() 
     521            xadv, yadv = 0.0, spany / tickCount 
     522            tick_w, tick_h = 5.0, 0.0 
     523            tickFunc = lambda : (y / spany) 
     524            tickOffset = QPointF(tick_w + 1.0, metrics.ascent()/2) 
     525             
     526        ticks = self.ticks() 
     527             
     528        xstart, ystart = 0.0, 0.0 
     529        painter.drawLine(xstart, ystart, xstart + tickCount*xadv, ystart + tickCount*yadv) 
     530         
     531        linspacex = numpy.linspace(0.0, spanx, tickCount) 
     532        linspacey = numpy.linspace(0.0, spany, tickCount) 
     533         
     534        for x, y, tick in zip(linspacex, linspacey, ticks): 
     535            painter.drawLine(x, y, x + tick_w, y + tick_h) 
     536            painter.drawText(QPointF(x, y) + tickOffset, tick) 
     537         
     538    def setGeometry(self, rect): 
     539        self.prepareGeometryChange() 
     540        return QGraphicsWidget.setGeometry(self, rect) 
     541         
     542    def sizeHint(self, which, *args): 
     543        minval, maxval = self.axisScale 
     544        ticks = self.ticks() 
     545        metrics = QFontMetrics(self.font()) 
     546        if self.orientation == Qt.Horizontal: 
     547            h = metrics.height() + 5 
     548            w = 100  
     549        else: 
     550            h = 100 
     551            w = max([metrics.width(t) for t in ticks]) + 5 
     552        return QSizeF(w, h) 
     553     
     554    def boundingRect(self): 
     555        metrics = QFontMetrics(self.font()) 
     556        return QRectF(QPointF(0.0, 0.0), self.sizeHint(None)).adjusted(0, -metrics.ascent(), 5, metrics.ascent())    
     557     
     558    def setAxisScale(self, min, max): 
     559        self.axisScale = (min, max) 
     560        self.updateGeometry() 
     561         
     562    def setAxisTicks(self, ticks): 
     563        if isinstance(ticks, dict): 
     564            self.ticks = ticks 
     565        self.updateGeometry() 
     566             
     567    def tickLayout(self): 
     568        min, max = getattr(self, "axisScale", (0.0, 1.0)) 
     569        ticks = self.ticks 
     570        span = max - min 
     571        span_log = math.log10(span) 
     572        log_sign = -1 if log_sign < 0.0 else 1 
     573        span_log = math.floor(span_log) 
     574        majorTicks = [(x, 5.0, tick(i, span_log)) for i in range(5)] 
     575        minorTicks = [(x, 3.0, tick(i, span_log + log_sign))  for i in range(10)] 
     576        return [(i, major, label) for i, tick, label in majorTicks] 
     577         
     578class LegendRect(QGraphicsWidget): 
     579    orientation = Qt.Horizontal 
     580    def __init__(self, parent=None): 
     581        QGraphicsWidget.__init__(self, parent) 
     582        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) 
     583        self.setColorSchema(lambda val: OWColorPalette.ColorPaletteBW()[1 - val]) 
     584         
     585    def setOrientation(self, orientation): 
     586        self.orientation = orientation 
     587        if orientation == Qt.Horizontal: 
     588            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) 
     589        else: 
     590            self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding) 
     591 
     592    def paint(self, painter, option, widget=0): 
     593        size = self.geometry().size() 
     594        painter.setBrush(QBrush(self.gradient)) 
     595        painter.drawRect(0.0, 0.0, size.width(), size.height()) 
     596         
     597    def setColorSchema(self, schema): 
     598        self.colorSchema = schema 
     599        self.gradient = QLinearGradient(0.0, 0.0, 0.0, 1.0) 
     600        space = numpy.linspace(0.0, 1.0, 10) 
     601        color = lambda col: col if isinstance(col, QColor) else QColor(*col) 
     602        self.gradient.setStops([(x, color(self.colorSchema(x))) for x in space]) 
     603        if hasattr(self.gradient, "setCoordinateMode"): 
     604            self.gradient.setCoordinateMode(QGradient.ObjectBoundingMode) 
     605         
     606    def sizeHint(self, which, *args): 
     607        if self.orientation == Qt.Horizontal: 
     608            return QSizeF(100, 20) 
     609        else: 
     610            return QSizeF(20, 100) 
     611     
     612    def setGeometry(self, rect): 
     613        QGraphicsWidget.setGeometry(self, rect)    
     614         
     615class LegendItem(QGraphicsWidget): 
     616    schema = ColorPalette([(255, 255, 255), (0, 0, 0)]) 
     617    range = (0.0, 1.0) 
     618    orientation = Qt.Horizontal 
     619    textAlign = Qt.AlignVCenter | Qt.AlignTop 
     620    textFont = QFont() 
     621    def __init__(self, attr, examples=None, parent=None, **kwargs): 
     622        QGraphicsWidget.__init__(self, parent) 
     623        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) 
     624        self.setLayout(QGraphicsLinearLayout(Qt.Vertical)) 
     625        self.layout().setSpacing(0) 
     626        self.rectItem = LegendRect(self) 
     627        self.rectItem.setOrientation(self.orientation) 
     628        self.layout().addItem(self.rectItem) 
     629        self.axisItem = AxisItem(self) 
     630        self.axisItem.setOrientation(self.orientation) 
     631        self.layout().addItem(self.axisItem) 
     632         
     633    def setOrientation(self, orientation=Qt.Horizontal): 
     634        self.orientation = orientation 
     635        self.rectItem.setOrientation(orientation) 
     636        self.axisItem.setOrientation(orientation) 
     637        if orientation == Qt.Horizontal: 
     638            self.layout().setOrientation(Qt.Vertical) 
     639            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) 
     640        else: 
     641            self.layout().setOrientation(Qt.Horizontal) 
     642            self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding) 
     643        self.updateGeometry() 
     644         
     645    def setTextAlign(self, align): 
     646        self.textAlign = align 
     647     
     648    def setTextFont(self, font): 
     649        self.textFont = font 
     650         
     651    def setColorSchema(self, schema): 
     652        self.schema = schema 
     653        self.rectItem.setColorSchema(schema) 
     654         
     655    def setTicks(self, ticks): 
     656        self.ticks = ticks 
     657 
     658    def setScale(self, scale): 
     659        self.scale = scale 
     660        self.axisItem.setAxisScale(*scale) 
     661         
     662    def setGeometry(self, rect): 
     663        QGraphicsWidget.setGeometry(self, rect) 
     664     
     665class DiscreteLegendItem(QGraphicsWidget): 
     666    class _ItemPair(QGraphicsItemGroup): 
     667        def __init__(self, item1, item2, parent=None): 
     668            QGraphicsItemGroup.__init__(self, parent) 
     669            self.item1, self.item2 = item1, item2 
     670            self.addToGroup(item1) 
     671            self.addToGroup(item2) 
     672            self.item1.setPos(0, item1.boundingRect().height()/2) 
     673            self.item2.setPos(self.item1.boundingRect().width(), 0.0) 
     674             
     675    orientation = Qt.Vertical 
     676    def __init__(self, attr, examples=None, colorSchema=None, parentItem=None, scene=None, itemShape=QGraphicsEllipseItem): 
     677        QGraphicsWidget.__init__(self, parentItem) 
     678        self.attr = attr 
     679        self.colorSchema = (lambda val: OWColorPalette.ColorPaletteHSV(len(attr.values))[val]) if colorSchema is None else colorSchema 
     680        self.itemShape = itemShape 
     681        self.setLayout(QGraphicsLinearLayout(self.orientation)) 
     682         
     683        self.legendItems = [] 
     684        for i, value in enumerate(attr.values): 
     685            self.addLegendItem(attr, value, self.colorSchema(i)) 
     686         
     687    def addLegendItem(self, attr, value, color=None, size=10): 
     688        item = self.itemShape(self) 
     689        item.setBrush(QBrush(self.colorSchema(len(self.legendItems)) if color is None else color)) 
     690        item.setRect(QRectF(QPointF(0, 0), QSizeF(size, size))) 
     691         
     692        text = QGraphicsTextItem(value, self) 
     693        pair = self._ItemPair(item, text, self) 
     694#        pair.setToolTip(value) 
     695        self.layout().addItem(LayoutItemWrapper(pair)) 
     696         
     697    def setOrientation(self, orientation): 
     698        self.orientation = orientation 
     699        self.layout().setOrientation(orientation) 
     700         
     701    def setGeometry(self, rect): 
     702        QGraphicsWidget.setGeometry(self, rect) 
     703 
     704 
     705class SOMGraphicsWidget(QGraphicsWidget): 
     706    """ Graphics widget containing a SOM map and optional legends 
     707    """ 
     708    def __init__(self, parent=None): 
     709        QGraphicsWidget.__init__(self, parent) 
     710        self.setLayout(QGraphicsLinearLayout()) 
     711        self.legendLayout = QGraphicsLinearLayout(Qt.Vertical) 
     712        self.layout().addItem(self.legendLayout) 
     713#        self.setGraphicsEffect(QGraphicsDropShadowEffect(offset=QPointF(1,2), blurRadius=5)) 
     714         
     715    def clear(self): 
     716        for i in range(self.layout().count()): 
     717            self.removeAt(i) 
     718             
     719    def setSOM(self, som): 
     720        self.som = som 
     721        if getattr(self, "somItem", None) is not None: 
     722            self.layout().removeAt(0) 
     723            self.scene().removeItem(self.somItem) 
     724        self.somItem = SOMMapItem(som, parent=self) 
     725        self.layout().insertItem(0, LayoutItemWrapper(self.somItem)) 
     726         
     727    def setComponentPlane(self, attr): 
     728        self.componentPlane = attr 
     729        self.somItem.setComponentPlane(attr) 
     730        if self.legendLayout.count() > 0: 
     731            item = self.legendLayout.itemAt(0) 
     732            item.scene().removeItem(item) 
     733            self.legendLayout.removeAt(0) 
     734        if attr is not None: 
     735            self.legendItem = LegendItem(attr, parent=self) 
     736            self.legendItem.setOrientation(Qt.Vertical) 
     737            self.legendLayout.addItem(self.legendItem) 
     738            self.legendItem.setScale(self.somItem.componentRange(attr)) 
     739         
     740    def setHistogramData(self, data): 
     741        self.histogramData = data 
     742        self.somItem.setHistogramData(data) 
     743         
     744    def setHistogramConstructor(self, constructor): 
     745        self.histogramConstructor = constructor  
     746        self.somItem.setHistogramConstructor(constructor) 
     747        if getattr(self, "histLegendItem", None) is not None: 
     748            self.scene().removeItem(self.histLegendItem) 
     749            self.histLegendItem = None 
     750        if constructor and getattr(constructor, "legendItemConstructor", None) is not None: 
     751            self.histLegendItem = constructor.legendItemConstructor(self.histogramData) 
     752            self.histLegendItem.setOrientation(Qt.Vertical)               
     753             
     754            self.legendLayout.insertItem(1, self.histLegendItem) 
     755             
     756    def setHistogram(self, attr, type_): 
     757        def const(*args, **kwargs): 
     758            return type_(attr, self.data, *args, **kwargs) 
     759         
     760        self.histogramConstructor = const 
     761        self.histogramConstructorType = type_ 
     762        self.histogramAttr = attr 
     763        self.setHistogramConstructor(self.histogramConstructor) 
     764             
     765    def setColorSchema(self, schema): 
     766        self.colorSchema = schema 
    353767        self.update() 
    354768         
    355     def updateLabels(self): 
    356         for o in self.canvasObj: 
    357             if o.hasNode and len(o.node.examples): 
    358                 if self.parent().labelNodes and o.node.classifier: 
    359                     o.setLabel(str(o.node.classifier.defaultValue)) 
    360                 else: 
    361                     o.setLabel("") 
    362             else: 
    363                 o.setLabel("") 
    364         self.updateAll() 
    365  
    366     def updateAll(self): 
    367 ##        self.setAllChanged() 
     769    def setHistogramColorSchema(self, schema): 
     770        self.histogramColorSchema = schema 
     771        self.somItem.setHistogramColorSchema(schema) 
    368772        self.update() 
    369  
    370     def updateGrid(self): 
    371         pen = QPen(Qt.black) if self.showGrid else QPen(Qt.NoPen) 
    372         for item in self.canvasObj: 
    373             item.defaultPen = pen 
    374             item.setSelected(item.isSelected) 
    375          
    376     def clear(self): 
    377         for o in self.canvasObj: 
    378             self.removeItem(o) #o.setCanvas(None) 
    379         self.canvasObj=[] 
    380              
     773         
     774    def paint(self, painter, option, widget=0): 
     775        painter.drawRect(self.boundingRect()) 
     776         
     777class SOMGraphicsUMatrix(SOMGraphicsWidget): 
     778    def setSOM(self, som): 
     779        self.som = som 
     780        if getattr(self, "somItem", None) is not None: 
     781            self.scene().removeItem(self.somItem) 
     782            self.somItem = None 
     783        self.somItem = SOMUMatrixItem(som, parent=self) 
     784#        item = self.layout().itemAt(0) 
     785#        if item: 
     786#            self.layout().removeItem(item) 
     787#            item.scene().removeItem(item) 
     788        self.layout().insertItem(0, LayoutItemWrapper(self.somItem)) 
     789         
     790    def setComponentPlane(self, *args): 
     791        raise NotImplemented 
     792     
     793baseColor = QColor(20,20,20) 
     794 
     795class SceneSelectionManager(QObject): 
     796    def __init__(self, scene): 
     797        QObject.__init__(self, scene) 
     798        self.scene = scene 
     799        self.selection = [] 
     800         
     801    def start(self, event): 
     802        pos = event.scenePos() 
     803        if event.modifiers() & Qt.ControlModifier: 
     804            self.selection.append((pos, pos + QPointF(1, 1))) 
     805        else: 
     806            self.selection = [(pos, pos)] 
     807        self.emit(SIGNAL("selectionGeometryChanged()")) 
     808         
     809    def update(self, event): 
     810        pos = event.scenePos() + QPointF(2.0, 2.0) 
     811        self.selection[-1] = self.selection[-1][:-1] + (pos,) 
     812        self.emit(SIGNAL("selectionGeometryChanged()")) 
     813     
     814    def end(self, event): 
     815        self.update(event) 
     816         
     817    def testSelection(self, data): 
     818        data = numpy.asarray(data) 
     819        region = QPainterPath() 
     820        for p1, p2 in self.selection: 
     821            region.addRect(QRectF(p1, p2).normalized()) 
     822        def test(point): 
     823            return region.contains(QPointF(point[0], point[1])) 
     824        test = numpy.apply_along_axis(test, 1, data) 
     825        return test 
     826     
     827    def selectionArea(self): 
     828        region = QPainterPath() 
     829        for p1, p2 in self.selection: 
     830            region.addRect(QRectF(p1, p2).normalized()) 
     831        return region 
     832     
     833    def lastSelectionRect(self): 
     834        if self.selection: 
     835            return QRectF(*self.selection[-1]).normalized() 
     836        else: 
     837            return None 
     838             
     839class SOMScene(QGraphicsScene): 
     840    def setSom(self, map): 
     841        self.clear() 
     842         
     843        self.map = map 
     844         
     845        self.emit(SIGNAL("som_changed()")) 
     846         
     847        self.selectionManager = SceneSelectionManager(self) 
     848        self.connect(self.selectionManager, SIGNAL("selectionGeometryChanged()"), self.onSelectionAreaChange) 
     849         
     850    def somWidgets(self): 
     851        for item in self.items(): 
     852            if isinstance(item, SOMGraphicsWidget): 
     853                yield item 
     854         
     855    def setHistogramData(self, data): 
     856        self.data = data 
     857        for item in self.somWidgets(): 
     858            item.setHistogramData(data) 
     859                 
     860    def setHistogramConstructor(self, constructor): 
     861        for item in self.somWidgets(): 
     862            item.setHistogramConstructor(constructor) 
     863             
     864    def setHistogramColorSchema(self, schema): 
     865        for item in self.somWidgets(): 
     866            item.setHistogramColorSchema(schema) 
     867             
     868    def setGridMode(self, mode): 
     869        return 
     870        for item in self.somWidgets(): 
     871            item.setGridMode(mode) 
     872             
     873    def setColorSchema(self, schema): 
     874        for item in self.somWidgets(): 
     875            item.setColorSchema(schema) 
     876 
     877    def setComponentPlane(self, attr): 
     878        self.clear() 
     879        self.somWidget = SOMGraphicsWidget(None) 
     880        self.somWidget.setSOM(self.map) 
     881        self.somWidget.setComponentPlane(attr) 
     882        self.addItem(self.somWidget) 
     883         
     884    def setUMatrix(self): 
     885        self.clear() 
     886         
     887        self.somWidget = SOMGraphicsUMatrix(None) 
     888        self.somWidget.setSOM(self.map) 
     889         
     890        self.addItem(self.somWidget) 
     891         
     892    def setNodePen(self, pen): 
     893        for item in self.somWidgets(): 
     894            item.somItem.setNodePen(pen) 
     895             
     896    def mousePressEvent(self, event): 
     897        if event.button() == Qt.LeftButton: 
     898            self.selectionManager.start(event) 
     899            if getattr(self, "selectionRectItem", None) is not None: 
     900                self.removeItem(self.selectionRectItem) 
     901            self.selectionRectItem = self.addRect(self.selectionManager.lastSelectionRect()) 
     902            self.selectionRectItem.setRect(self.selectionManager.lastSelectionRect()) 
     903            self.selectionRectItem.show() 
     904         
    381905    def mouseMoveEvent(self, event): 
    382         pos = event.scenePos() 
    383         if self.selectionRect: 
    384             rect = self.selectionRect.rect() 
    385             self.selectionRect.setRect(QRectF(rect.x(), rect.y(), pos.x() - rect.x(), pos.y() - rect.y())) 
    386          
    387     def mousePressEvent(self, event): 
    388         pos = event.scenePos() 
    389         self.selectionRect = r = QGraphicsRectItem(pos.x(), pos.y(), 1, 1, None, self) 
    390         r.show() 
    391         r.setZValue(19) 
    392         self.oldSelection=self.selectionList 
    393         if not self.master.ctrlPressed: 
    394             self.clearSelection() 
    395          
     906        if event.buttons() & Qt.LeftButton: 
     907            self.selectionManager.update(event) 
     908            self.selectionRectItem.setRect(self.selectionManager.lastSelectionRect()) 
     909     
    396910    def mouseReleaseEvent(self, event): 
    397         self.updateSelection() 
    398         self.removeItem(self.selectionRect) 
    399         self.selectionRect=None 
    400         self.master.updateSelection([a.node for a in self.selectionList]) 
    401         self.update() 
    402      
    403     def updateSelection(self): 
    404         obj = self.items(self.selectionRect.rect()) 
    405         obj = [a for a in obj if isinstance(a, GraphicsSOMItem) and a.hasNode] 
    406         if self.master.ctrlPressed: 
    407             set1 = set(obj) 
    408             set2 = set(self.oldSelection) 
    409             intersection = set1.intersection(set2) 
    410             union = set1.union(set2) 
    411             for e in intersection: 
    412                 self.removeSelection(e) 
    413             for e in set1.difference(set2): 
    414                 self.addSelection(e) 
    415         else: 
    416             self.clearSelection() 
    417             for e in obj: 
    418                 self.addSelection(e)           
    419  
    420     def addSelection(self, obj): 
    421         obj.setSelected(True) 
    422         self.selectionList.append(obj) 
    423             
    424     def removeSelection(self, obj): 
    425         obj.setSelected(False) 
    426         self.selectionList = [a for a in self.selectionList if a.isSelected] 
    427      
    428     def clearSelection(self): 
    429         for o in self.selectionList: 
    430             o.setSelected(False) 
    431         self.selectionList=[] 
    432          
    433     def invertSelection(self): 
    434         for n in self.canvasObj: 
    435             if n.hasNode: 
    436                 if not n.isSelected: 
    437                     self.addSelection(n) 
    438                 else: 
    439                     self.removeSelection(n) 
    440         self.master.updateSelection([a.node for a in self.selectionList]) 
    441         self.update() 
    442  
    443  
     911        if event.button() & Qt.LeftButton: 
     912            self.selectionManager.end(event) 
     913            self.selectionRectItem.hide() 
     914            self.removeItem(self.selectionRectItem) 
     915            self.selectionRectItem = None 
     916     
     917    def onSelectionAreaChange(self): 
     918        self.setSelectionArea(self.selectionManager.selectionArea()) 
     919         
     920    def updateToolTips(self, *args, **kwargs): 
     921        for item in self.somWidgets(): 
     922            item.somItem.updateToolTips(*args, **kwargs) 
     923     
    444924class OWSOMVisualizer(OWWidget): 
    445     settingsList = ["scene.drawMode","scene.objSize","commitOnChange", "backgroundMode", "backgroundCheck", "scene.includeCodebook", "scene.showToolTip"] 
     925    settingsList = ["drawMode","objSize","commitOnChange", "backgroundMode", "backgroundCheck", "includeCodebook", "showToolTips"] 
    446926    contextHandlers = {"":DomainContextHandler("", [ContextField("attribute", DomainContextHandler.Optional), 
    447927                                                  ContextField("discHistMode", DomainContextHandler.Optional), 
     
    460940        self.outputs = [("Examples", ExampleTable)] 
    461941         
     942        self.drawMode = 0 
     943        self.objSize = 10 
     944        self.component = 0 
    462945        self.labelNodes = 0 
    463946        self.commitOnChange = 0 
    464947        self.backgroundCheck = 1 
    465948        self.backgroundMode = 0 
     949        self.includeCodebook = 0 
     950        self.showToolTips = 0 
    466951        self.histogram = 1 
    467952        self.attribute = 0 
     
    470955        self.contHistMode = 0 
    471956        self.inputSet = 0 
     957        self.showNodeOutlines = 0 
    472958 
    473959        self.somMap = None 
     
    476962         
    477963         
    478 ##        layout = QVBoxLayout(self.mainArea) #,QVBoxLayout.TopToBottom,0) 
    479         self.scene = SOMScene(self, self) 
     964        self.scene = SOMScene(self) 
    480965        self.sceneView = QGraphicsView(self.scene, self.mainArea) 
    481966        self.sceneView.viewport().setMouseTracking(True) 
    482         self.sceneView.setRenderHints(QPainter.Antialiasing) 
     967        self.sceneView.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) 
    483968 
    484969        self.mainArea.layout().addWidget(self.sceneView) 
     970        self.connect(self.scene, SIGNAL("selectionChanged()"), self.commitIf) 
    485971         
    486972        self.loadSettings() 
    487         call = lambda:self.scene.redrawSom() 
     973#        call = lambda:self.scene.redrawSom() 
    488974 
    489975        histTab = mainTab = self.controlArea 
     
    493979 
    494980        self.backgroundBox = OWGUI.widgetBox(mainTab, "Background") 
    495         #OWGUI.checkBox(self.backgroundBox, self, "backgroundCheck","Show background", callback=self.setBackground) 
    496         b = OWGUI.radioButtonsInBox(self.backgroundBox, self, "scene.drawMode", self.drawModes, callback=self.setBackground) 
     981        b = OWGUI.radioButtonsInBox(self.backgroundBox, self, "drawMode", self.drawModes, callback=self.setBackground) 
    497982        b.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    498         self.componentCombo=OWGUI.comboBox(OWGUI.indentedBox(b), self,"scene.component", callback=self.setBackground) 
    499         self.componentCombo.setEnabled(self.scene.drawMode==2) 
    500         OWGUI.checkBox(self.backgroundBox, self, "scene.showGrid", "Show grid", callback=self.scene.updateGrid) 
    501         #b=OWGUI.widgetBox(mainTab, "Histogram") 
    502         #OWGUI.separator(mainTab) 
    503          
    504         b = OWGUI.widgetBox(mainTab, "Histogram") ##QVButtonGroup("Histogram", mainTab) 
    505 #        b.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
     983        self.componentCombo=OWGUI.comboBox(OWGUI.indentedBox(b), self,"component", callback=self.setBackground) 
     984        self.componentCombo.setEnabled(self.drawMode==2) 
     985        OWGUI.checkBox(self.backgroundBox, self, "showNodeOutlines", "Show grid", callback=self.updateGrid) 
     986         
     987        b = OWGUI.widgetBox(mainTab, "Histogram")  
    506988        OWGUI.checkBox(b, self, "histogram", "Show histogram", callback=self.setHistogram) 
    507989        OWGUI.radioButtonsInBox(OWGUI.indentedBox(b), self, "inputSet", ["Use training set", "Use input subset"], callback=self.setHistogram) 
    508         #OWGUI.separator(mainTab) 
    509          
    510         b1= OWGUI.widgetBox(mainTab) ##QVBox(mainTab) 
    511 #        b1.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    512         b=OWGUI.hSlider(b1, self, "scene.objSize","Plot size", 10,100,step=10,ticks=10, callback=call) 
    513 #        b.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    514         #OWGUI.checkBox(b1, self, "labelNodes", "Node Labeling", callback=self.canvas.updateLabels) 
    515         #OWGUI.separator(b1) 
     990         
     991        b1= OWGUI.widgetBox(mainTab)  
     992        b=OWGUI.hSlider(b1, self, "objSize","Plot size", 1, 100,step=10,ticks=10, callback=self.setZoom) 
    516993 
    517994        b1 = OWGUI.widgetBox(b1, "Tooltip Info") 
    518         OWGUI.checkBox(b1, self, "scene.showToolTip","Show tooltip", callback=self.scene.updateToolTips) 
    519         OWGUI.checkBox(b1, self, "scene.includeCodebook", "Include codebook vector", callback=self.scene.updateToolTips) 
     995        OWGUI.checkBox(b1, self, "showToolTips","Show tooltip", callback=self.updateToolTips) 
     996        OWGUI.checkBox(b1, self, "includeCodebook", "Include codebook vector", callback=self.updateToolTips) 
    520997        b1.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    521         #OWGUI.separator(mainTab) 
    522998         
    523999        self.histogramBox = OWGUI.widgetBox(histTab, "Coloring") 
    524         self.attributeCombo = OWGUI.comboBox(self.histogramBox, self, "attribute", callback=self.setHistogram) 
    525  
    526         self.discTab = OWGUI.radioButtonsInBox(OWGUI.indentedBox(self.histogramBox), self, "discHistMode", ["Pie chart", "Majority value", "Majority value prob."], box=1, callback=self.setHistogram) 
     1000        self.attributeCombo = OWGUI.comboBox(self.histogramBox, self, "attribute", callback=self.onHistogramAttrSelection) 
     1001        self.coloringStackedLayout = QStackedLayout() 
     1002        indentedBox = OWGUI.indentedBox(self.histogramBox, orientation=self.coloringStackedLayout) 
     1003        self.discTab = OWGUI.widgetBox(indentedBox) 
     1004        OWGUI.radioButtonsInBox(self.discTab, self, "discHistMode", ["Pie chart", "Majority value", "Majority value prob."], box=0, callback=self.onHistogramAttrSelection) 
    5271005        self.discTab.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    528         #self.targetValueCombo=OWGUI.comboBox(b, self, "targetValue", callback=self.setHistogram) 
    529 ##        QVBox(discTab) 
    530 ##        b=QVButtonGroup(contTab) 
    531 ##        b.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    532         self.contTab = OWGUI.radioButtonsInBox(OWGUI.indentedBox(self.histogramBox), self, "contHistMode", ["Default", "Average value"], box=1, callback=self.setHistogram) 
     1006 
     1007        self.contTab = OWGUI.widgetBox(indentedBox) 
     1008        OWGUI.radioButtonsInBox(self.contTab, self, "contHistMode", ["Default", "Average value"], callback=self.onHistogramAttrSelection) 
     1009        self.contTab.layout().addStretch(10) 
    5331010        self.contTab.setSizePolicy(QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)) 
    534 ##        QVBox(contTab) 
    535  
    536 ##        OWGUI.rubber(mainTab) 
    5371011  
    5381012        b = OWGUI.widgetBox(self.controlArea, "Selection") 
    539         OWGUI.button(b, self, "&Invert selection", callback=self.scene.invertSelection) 
     1013        OWGUI.button(b, self, "&Invert selection", callback=self.invertSelection) 
    5401014        button = OWGUI.button(b, self, "&Commit", callback=self.commit) 
    5411015        check = OWGUI.checkBox(b, self, "commitOnChange", "Commit on change") 
     
    5431017 
    5441018        OWGUI.rubber(self.controlArea) 
    545 #        OWGUI.button(self.controlArea, self, "&Save Graph", callback=self.saveGraph, debuggingEnabled = 0) 
    5461019        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveGraph) 
    5471020         
    5481021        self.selectionList = [] 
    5491022        self.ctrlPressed = False 
    550 ##        self.setFocusPolicy(QWidget.StrongFocus) 
    5511023        self.resize(800,500) 
    5521024 
     
    5611033         
    5621034    def setMode(self): 
    563         self.componentCombo.setEnabled(self.scene.drawMode == 2) 
    564         self.scene.redrawSom() 
     1035        self.componentCombo.setEnabled(self.drawMode == 2) 
     1036        if not self.somMap: 
     1037            return 
     1038        if self.drawMode in [0 ,2]: 
     1039            self.scene.setComponentPlane(self.component if self.drawMode == 2 else None) 
     1040        elif self.drawMode == 1: 
     1041            self.scene.setUMatrix() 
     1042        if self.histogram: 
     1043            self.setHistogram() 
     1044        self.updateToolTips() 
     1045        self.updateGrid() 
     1046        self.setZoom() 
    5651047 
    5661048    def setBackground(self): 
     
    5691051    def setDiscCont(self): 
    5701052        if self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete: 
    571             self.discTab.show() 
    572             self.contTab.hide() 
    573         else: 
    574             self.discTab.hide() 
    575             self.contTab.show() 
     1053            self.coloringStackedLayout.setCurrentWidget(self.discTab) 
     1054        else: 
     1055            self.coloringStackedLayout.setCurrentWidget(self.contTab) 
     1056         
     1057    def onHistogramAttrSelection(self): 
     1058        if self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete: 
     1059            self.coloringStackedLayout.setCurrentWidget(self.discTab) 
     1060        else: 
     1061            self.coloringStackedLayout.setCurrentWidget(self.contTab) 
     1062             
     1063        self.setHistogram() 
    5761064         
    5771065    def setHistogram(self): 
    578         if self.somMap: 
    579             self.setDiscCont() 
    580             self.scene.redrawSom() 
     1066        if self.somMap and self.histogram: 
     1067            if self.inputSet and self.examples is not None and self.examples.domain == self.somMap.examples.domain: 
     1068                self.scene.setHistogramData(self.examples) 
     1069            else: 
     1070                self.scene.setHistogramData(self.somMap.examples) 
     1071                 
     1072            attr = self.somMap.examples.domain.variables[self.attribute] 
     1073            if attr.varType == orange.VarTypes.Discrete: 
     1074                hist = [PieChart, MajorityChart, MajorityProbChart] 
     1075                hist = hist[self.discHistMode] 
     1076                visible, schema = True, None  
     1077            else: 
     1078                hist = [ContChart, AverageValue] 
     1079                hist = hist[self.contHistMode] 
     1080                visible = self.contHistMode == 1 
     1081                schema = ColorPalette([(255, 0, 0), (0, 255, 0)]) if self.contHistMode == 1 else None 
     1082                 
     1083            def partial__init__(self, *args, **kwargs): 
     1084                hist.__init__(self, attr, *args) 
     1085                 
     1086            def partialLegendItem(cls, *args, **kwargs): 
     1087                return hist.legendItemConstructor(attr, *args, **kwargs) 
     1088            hist_ = type(hist.__name__ + "Partial", (hist,), {"__init__":partial__init__, "legendItemConstructor":classmethod(partialLegendItem)}) 
     1089            if schema: 
     1090                self.scene.setHistogramColorSchema(schema) 
     1091            self.scene.setHistogramConstructor(hist_) 
     1092             
     1093            if visible and schema: 
     1094                self.scene.somWidget.histLegendItem.setColorSchema(schema) 
     1095            elif not visible: 
     1096                self.scene.somWidget.histLegendItem.hide() 
     1097        else: 
     1098            self.scene.setHistogramConstructor(None) 
     1099             
     1100    def update(self): 
     1101        self.setMode() 
    5811102 
    5821103    def drawPies(self): 
    5831104        return self.discHistMode == 0 and self.somMap.examples.domain.variables[self.attribute].varType == orange.VarTypes.Discrete 
     1105     
     1106    def setZoom(self): 
     1107        self.sceneView.setTransform(QTransform().scale(float(self.objSize)/SOMMapItem.objSize, float(self.objSize)/SOMMapItem.objSize)) 
     1108     
     1109    def updateGrid(self): 
     1110        self.scene.setNodePen(QPen(Qt.black, 1) if self.showNodeOutlines else QPen(Qt.NoPen)) 
     1111     
     1112    def updateToolTips(self): 
     1113        self.scene.updateToolTips(showToolTips=self.showToolTips, includeCodebook=self.includeCodebook) 
    5841114         
    5851115    def setSomMap(self, somMap=None): 
     
    6111141        self.setDiscCont() 
    6121142        self.scene.setSom(somMap) 
     1143        self.update() 
    6131144        
    6141145    def data(self, data=None): 
     
    6221153            if self.inputSet == 1: 
    6231154                self.setHistogram() 
     1155        self.update() 
    6241156     
    6251157    def clear(self): 
    6261158        self.scene.clearSelection() 
    6271159        self.componentCombo.clear() 
     1160        self.attributeCombo.clear() 
    6281161        self.scene.component = 0 
    6291162        self.scene.setSom(None) 
     1163        self.update() 
    6301164        self.send("Examples", None) 
    6311165         
     1166    def invertSelection(self): 
     1167        for widget in self.scene.somWidgets(): 
     1168            for node in widget.somItem.nodes(): 
     1169                node.setSelected(not node.isSelected()) 
     1170     
    6321171    def updateSelection(self, nodeList): 
    6331172        self.selectionList = nodeList 
     
    6441183            return 
    6451184        ex = orange.ExampleTable(self.somMap.examples.domain) 
    646         for n in self.selectionList: 
    647             if self.inputSet == 0 and n.examples: 
    648                 ex.extend(n.examples) 
    649             elif self.inputSet == 1 and getattr(n, "mappedExamples", False): 
    650                 ex.extend(n.mappedExamples) 
     1185#        for n in self.selectionList: 
     1186#            if self.inputSet == 0 and n.examples: 
     1187#                ex.extend(n.examples) 
     1188#            elif self.inputSet == 1 and getattr(n, "mappedExamples", False): 
     1189#                ex.extend(n.mappedExamples) 
     1190        for n in self.scene.selectedItems(): 
     1191            if isinstance(n, GraphicsSOMItem) and n.node and hasattr(n.node, "mappedExamples"): 
     1192                ex.extend(n.node.mappedExamples) 
    6511193        if len(ex): 
    6521194            self.send("Examples",ex) 
     
    6601202        sizeDlg.exec_() 
    6611203         
    662     def keyPressEvent(self, event): 
    663         if event.key() == Qt.Key_Control: 
    664             self.ctrlPressed = True 
    665         else: 
    666             OWWidget.keyPressEvent(self, event) 
    667        
    668     def keyReleaseEvent(self, event): 
    669         if event.key() == Qt.Key_Control: 
    670             self.ctrlPressed = False 
    671         else: 
    672             OWWidget.keyReleaseEvent(self, event) 
    673          
    674  
    675          
    6761204if __name__=="__main__": 
    6771205    ap = QApplication(sys.argv) 
    678     data = orange.ExampleTable("../../doc/datasets/brown-selected.tab") 
    679 ##    l=orngSOM.SOMLearner(batch_train=False) 
     1206    data = orange.ExampleTable("../../doc/datasets/iris.tab") 
     1207#    l=orngSOM.SOMLearner(batch_train=False) 
    6801208    l = orngSOM.SOMLearner(batch_train=True, initialize=orngSOM.InitializeLinear) 
    681 ##    l = orngSOM.SOMLearner(batch_train=True, initialize=orngSOM.InitializeRandom) 
     1209#    l = orngSOM.SOMLearner(batch_train=True, initialize=orngSOM.InitializeRandom) 
    6821210    l = l(data) 
    6831211    l.data = data 
     1212#    import cPickle 
     1213#    cPickle.dump(l, open("iris.som", "wb")) 
     1214#    l = cPickle.load(open("iris.som", "rb")) 
     1215     
    6841216    w = OWSOMVisualizer() 
    6851217##    ap.setMainWidget(w) 
    6861218    w.setSomClassifier(l) 
    687     w.data(orange.ExampleTable(data[:50])) 
     1219    w.data(orange.ExampleTable(data[:])) 
    6881220    w.show() 
    6891221    ap.exec_() 
    6901222    w.saveSettings() 
     1223     
     1224     
Note: See TracChangeset for help on using the changeset viewer.