Changeset 1401:efb3ad9c1851 in orange-bioinformatics


Ignore:
Timestamp:
05/20/11 16:30:59 (3 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
6d39ae4938502b76c60a0eb1073f7789a28ebad1
Message:

Almost completely rewrote HeatMap to add support for heat-map splits.
Now uses Qt's GraphicsWidget/GraphicsLayout framework to layout components.
Using OWClustering.DendrogramWidget.

New group creation in GenotypeDistances widget.

Location:
widgets
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • widgets/OWGenotypeDistances.py

    r1386 r1401  
    408408            else: 
    409409                return self.data.domain[attr_index] 
    410              
     410         
    411411        for keys, indices in partitions: 
    412412            attrs = [get_attr(attr_index, i) for i, attr_index in enumerate(indices)] 
     413            for attr in attrs: 
     414                attr.attributes.update(zip(separate_keys, keys)) 
    413415            domain = Orange.data.Domain(attrs, None) 
    414416            domain.add_metas(self.data.domain.get_metas().items()) 
     
    539541        if self.split_groups: 
    540542            all_attrs = [] 
    541             for group, domain in self.split_groups: 
     543            for group, domain in self.split_groups:  
    542544                attrs = [] 
    543545                group_name = " | ".join("{0}={1}".format(*item) for item in \ 
     
    545547                for attr in domain.attributes: 
    546548                    newattr = clone_attr(attr) 
    547                     newattr.attributes["$(GROUP)"] = group_name # Need a better way to pass the groups to downstream widgets. 
     549                    newattr.attributes["<GENOTYPE GROUP>"] = group_name # Need a better way to pass the groups to downstream widgets. 
    548550                    attrs.append(newattr) 
    549551                     
     
    560562        self.send("Distances", self.matrix) 
    561563        self.changed_flag = False 
    562      
    563  
    564564 
    565565if __name__ == "__main__": 
  • widgets/OWHeatMap.py

    r1372 r1401  
    1313from OWDlgs import OWChooseImageSizeDlg 
    1414from ColorPalette import signedPalette 
    15 from OWClustering import HierarchicalClusterItem 
     15from OWClustering import HierarchicalClusterItem, DendrogramWidget, DendrogramItem 
    1616import OWColorPalette 
    17 try: 
    18     from OWDataFiles import DataFiles 
    19 except Exception: 
    20     class DataFiles(object): 
    21         pass 
     17 
     18from collections import defaultdict 
     19import itertools 
     20import orngClustering 
     21 
     22DEBUG = False 
    2223 
    2324#BUG: OWHeatMap does not support heatmaps which need a image which is larger than maxint X maxint pixels! 
     
    2728warnings.filterwarnings("ignore", "'strain'", orange.AttributeWarning) 
    2829 
    29 # from OWChipANOVA import ANOVAResults 
     30def split_domain(domain, split_label): 
     31    """ Split the domain based on values of `split_label` value. 
     32    """ 
     33    groups = defaultdict(list) 
     34    for attr in domain.attributes: 
     35        groups[attr.attributes.get(split_label)].append(attr) 
     36         
     37    attr_values = [attr.attributes.get(split_label) for attr in domain.attributes] 
     38     
     39    domains = [] 
     40    for value, attrs in groups.items(): 
     41        group_domain = orange.Domain(attrs, domain.class_var) 
     42        group_domain.add_metas(domain.get_metas()) 
     43        domains.append((value, group_domain)) 
     44         
     45    if domains: 
     46        assert(all(len(dom) == len(domains[0][1]) for _, dom in domains)) 
     47         
     48    return sorted(domains, key=lambda t: attr_values.index(t[0])) 
     49     
     50def vstack_by_subdomain(data, sub_domains): 
     51    domain = sub_domains[0] 
     52    newtable = orange.ExampleTable(domain) 
     53     
     54    for sub_dom in sub_domains: 
     55        for ex in data: 
     56            vals = [ex[a].native() for a in sub_dom] 
     57            newtable.append(orange.Example(domain, vals)) 
     58     
     59    return newtable 
     60     
     61     
     62def select_by_class(data, class_): 
     63    indices = select_by_class_indices(data, class_) 
     64    return data.select(indices) 
     65 
     66def select_by_class_indices(data, class_): 
     67    return [1 if class_ == ex.getclass() else 0 for ex in data] 
     68 
     69def group_by_unordered(iterable, key): 
     70    groups = defaultdict(list) 
     71    for item in iterable: 
     72        groups[key(item)].append(item) 
     73    return groups.items() 
     74 
     75def hierarchical_cluster_ordering(data, group_domains=None, opt_order=False, progress_callback=None): 
     76    classVar = data.domain.classVar 
     77    if classVar and isinstance(classVar, orange.EnumVariable): 
     78        class_data = [select_by_class_indices(data, val) for val in data.domain.classVar.values] 
     79    else: 
     80        class_data = [[1] * len(data)] 
     81         
     82    parts = len(class_data) + 1  
     83     
     84    def pp_callback(part): 
     85        def callback(value): 
     86            return progress_callback(value / parts + 100.0 * part / parts) 
     87        if progress_callback: 
     88            callback(100.0 * part / parts) 
     89            return callback 
     90        else: 
     91            return progress_callback 
     92         
     93    if group_domains is not None and len(group_domains) > 1: 
     94        stacked = vstack_by_subdomain(data, group_domains) 
     95    else: 
     96        stacked = data     
     97         
     98    attr_cluster = orngClustering.hierarchicalClustering_attributes(stacked, order=opt_order, progressCallback=pp_callback(0)) 
     99    attr_ordering = list(attr_cluster.mapping) 
     100         
     101    def indices_map(indices): 
     102        map = zip(range(len(indices)), indices) 
     103        map = [i for i, test in map if test] 
     104        return dict(enumerate(map)) 
     105     
     106    data_ordering = [] 
     107    data_clusters = [] 
     108    for i, indices in enumerate(class_data): 
     109        sub_data = data.select(indices) 
     110        cluster = orngClustering.hierarchicalClustering(sub_data, order=opt_order, progressCallback=pp_callback(i + 1)) 
     111        ind_map = indices_map(indices) 
     112        data_ordering.append([ind_map[m] for m in cluster.mapping]) 
     113        data_clusters.append(cluster) 
     114         
     115    return attr_ordering, attr_cluster, data_ordering, data_clusters   
     116         
    30117 
    31118############################################################################## 
     
    53140        context.checksum = examples.checksum() 
    54141        return context, isNew 
     142     
     143from OWGenotypeDistances import SetContextHandler 
    55144 
    56145class OWHeatMap(OWWidget): 
    57146    contextHandlers = {"": DomainContextHandler("", ["CellWidth", "CellHeight"]), 
    58                        "Selection": ExampleTableContextHandler("Selection")} 
     147                       "Selection": ExampleTableContextHandler("Selection", contextDataVersion=2)} 
    59148     
    60149    settingsList = ["CellWidth", "CellHeight", "SpaceX", "Merge", 
     
    63152                    "ShowAverageStripe", "ShowGroupLabel", 
    64153                    "MaintainArrayHeight", 
    65                     "BShowballoon", "BShowColumnID", "BShowSpotIndex", 
     154                    "GShowToolTip", "BShowColumnID", "BShowSpotIndex", 
    66155                    "BShowAnnotation", 'BShowGeneExpression', 
    67156                    "BSpotVar", "ShowGeneAnnotations", 
     
    72161 
    73162    def __init__(self, parent=None, signalManager = None): 
    74 #        self.callbackDeposit = [] # deposit for OWGUI callback functions 
    75163        OWWidget.__init__(self, parent, signalManager, 'HeatMap', TRUE) 
    76164         
    77         self.inputs = [("Structured Data", DataFiles, self.chipdata, Single + NonDefault), ("Examples", ExampleTable, self.dataset, Default + Multiple)] 
    78         self.outputs = [("Structured Data", DataFiles, Single + NonDefault), ("Examples", ExampleTable, Default)] 
     165        self.inputs = [("Examples", ExampleTable, self.set_dataset)] 
     166        self.outputs = [("Examples", ExampleTable, Default)] 
    79167 
    80168        #set default settings 
     
    83171        self.Merge = 1; self.savedMerge = self.Merge 
    84172        self.Gamma = 1 
    85         self.CutLow = 0; self.CutHigh = 0; self.CutEnabled = 0 
     173        self.CutLow = self.CutHigh = self.CutEnabled = 0 
    86174        self.ShowAnnotation = 0 
    87175        self.LegendOnTop = 0           # legend stripe on top (bottom)? 
    88176        self.LegendOnBottom = 1 
     177        self.ShowLegend = 1 
    89178        self.ShowGroupLabel = 1        # show class names in case of classified data? 
    90179        self.ShowAverageStripe = 0     # show the stripe with the evarage 
    91180        self.MaintainArrayHeight = 0   # adjust cell height while changing the merge factor 
    92         self.BShowballoon = 1          # balloon help 
     181        self.GShowToolTip = 1          # balloon help 
    93182        self.ShowGeneAnnotations = 1   # show annotations for genes 
    94183        self.ShowColumnLabels = 1 
     
    107196        self.colorSettings =None 
    108197        self.selectedSchemaIndex = 0 
     198        self.auto_commit = True 
     199        self.selection_changed_flag = False 
    109200 
    110201        self.palette = self.ColorPalettes[0] 
     
    117208        # GUI definition 
    118209        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveFig) 
    119         self.tabs = OWGUI.tabWidget(self.controlArea) #QTabWidget(self.controlArea, 'tabWidget') 
     210        self.tabs = OWGUI.tabWidget(self.controlArea) 
    120211 
    121212        # SETTINGS TAB 
    122         settingsTab = OWGUI.createTabPage(self.tabs, "Settings") #QVGroupBox(self) 
    123         box = OWGUI.widgetBox(settingsTab, "Cell Size (Pixels)", addSpace=True) #QVButtonGroup("Cell Size (Pixels)", settingsTab) 
    124         OWGUI.qwtHSlider(box, self, "CellWidth", label='Width: ', labelWidth=38, minValue=1, maxValue=self.maxHSize, step=1, precision=0, callback=self.drawHeatMap) 
    125         self.sliderVSize = OWGUI.qwtHSlider(box, self, "CellHeight", label='Height: ', labelWidth=38, minValue=1, maxValue=self.maxVSize, step=1, precision=0, callback=self.createHeatMap) 
    126         OWGUI.qwtHSlider(box, self, "SpaceX", label='Space: ', labelWidth=38, minValue=0, maxValue=50, step=2, precision=0, callback=self.drawHeatMap) 
    127         OWGUI.qwtHSlider(settingsTab, self, "Gamma", box="Gamma", minValue=0.1, maxValue=1, step=0.1, callback=self.drawHeatMap) 
     213        settingsTab = OWGUI.createTabPage(self.tabs, "Settings") 
     214        box = OWGUI.widgetBox(settingsTab, "Cell Size (Pixels)", addSpace=True) 
     215        OWGUI.qwtHSlider(box, self, "CellWidth", label='Width: ', 
     216                         labelWidth=38, minValue=1, maxValue=self.maxHSize, 
     217                         step=1, precision=0, callback=self.update_cell_size) 
     218         
     219#        OWGUI.hSlider(box, self, "CellWidth", label="Width:", minValue=1, 
     220#                      maxValue=self.maxHSize, step=1, ticks=5, 
     221#                      callback=self.update_cell_size, 
     222#                      tooltip="Width of each heatmap cell.") 
     223         
     224        self.sliderVSize = OWGUI.qwtHSlider(box, self, "CellHeight", 
     225                                label='Height: ', labelWidth=38, 
     226                                minValue=1, maxValue=self.maxVSize, 
     227                                step=1, precision=0, 
     228                                callback=self.update_cell_size) 
     229#        self.sliderVSize = OWGUI.hSlider(box, self, "CellHeight", 
     230#                      label='Height:',  
     231#                      minValue=1, maxValue=50, ticks=5, 
     232#                      callback = self.update_cell_size) 
     233         
     234        OWGUI.qwtHSlider(box, self, "SpaceX", label='Space: ',  
     235                         labelWidth=38, minValue=0, maxValue=50, 
     236                         step=2, precision=0, 
     237                         callback=self.update_grid_spacing) 
     238#                         callback=self.drawHeatMap) 
     239         
     240        OWGUI.qwtHSlider(settingsTab, self, "Gamma", box="Gamma", 
     241                         minValue=0.1, maxValue=1, step=0.1, 
     242                         callback=self.update_color_schema) 
     243#                         callback=self.drawHeatMap) 
     244         
    128245        OWGUI.separator(settingsTab) 
    129246 
     
    145262        OWGUI.separator(settingsTab) 
    146263 
    147 ##        OWGUI.checkBox(settingsTab, self, "SortGenes", "Sort genes", box="Sort", callback=self.constructHeatmap) 
    148         OWGUI.comboBox(settingsTab, self, "SortGenes", "Sort genes", items=["No sorting", "Sort genes", "Clustering", "Clustering with leaf ordering"], callback=self.constructHeatmap) 
     264        OWGUI.comboBox(settingsTab, self, "SortGenes", "Sort genes", 
     265                       items=["No sorting", "Sort genes", "Clustering", 
     266                              "Clustering with leaf ordering"], 
     267                               callback=self.update_sorting) 
    149268        OWGUI.rubber(settingsTab) 
    150269         
    151270        # FILTER TAB 
    152         tab = OWGUI.createTabPage(self.tabs, "Filter") #QVGroupBox(self) 
    153         box = OWGUI.widgetBox(tab, "Threshold Values", addSpace=True) #QVButtonGroup("Threshold Values", tab) 
    154         OWGUI.checkBox(box, self, 'CutEnabled', "Enabled", callback=self.setCutEnabled) 
    155         self.sliderCutLow = OWGUI.qwtHSlider(box, self, 'CutLow', label='Low:', labelWidth=33, minValue=-100, maxValue=0, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.drawHeatMap) 
    156         self.sliderCutHigh = OWGUI.qwtHSlider(box, self, 'CutHigh', label='High:', labelWidth=33, minValue=0, maxValue=100, step=0.1, precision=1, ticks=0, maxWidth=80, callback=self.drawHeatMap) 
     271        tab = OWGUI.createTabPage(self.tabs, "Filter") 
     272        box = OWGUI.widgetBox(tab, "Threshold Values", addSpace=True) 
     273        OWGUI.checkBox(box, self, 'CutEnabled', "Enabled", 
     274                       callback=self.update_thresholds) 
     275         
     276        self.sliderCutLow = OWGUI.qwtHSlider(box, self, 'CutLow', label='Low:',  
     277                            labelWidth=40, minValue=-100, maxValue=0, step=0.1, 
     278                            precision=1, ticks=0, maxWidth=80, 
     279                            callback=self.update_thresholds) 
     280         
     281        self.sliderCutHigh = OWGUI.qwtHSlider(box, self, 'CutHigh', label='High:',  
     282                            labelWidth=40, minValue=0, maxValue=100, step=0.1, 
     283                            precision=1, ticks=0, maxWidth=80, 
     284                            callback=self.update_thresholds) 
     285         
    157286        if not self.CutEnabled: 
    158287            self.sliderCutLow.box.setDisabled(1) 
    159288            self.sliderCutHigh.box.setDisabled(1) 
    160289 
    161         box = OWGUI.widgetBox(tab, "Merge", addSpace=True) #QVButtonGroup("Merge", tab) 
     290        box = OWGUI.widgetBox(tab, "Merge", addSpace=True) 
    162291##        OWGUI.qwtHSlider(box, self, "Merge", label='Rows:', labelWidth=33, minValue=1, maxValue=500, step=1, callback=self.mergeChanged, precision=0, ticks=0) 
    163         OWGUI.spin(box, self, "Merge", min=1, max=500, step=1, label='Rows:', callback=self.mergeChanged, callbackOnReturn=True) 
     292        OWGUI.spin(box, self, "Merge", min=1, max=500, step=1, label='Rows:', 
     293#                   callback=self.mergeChanged, 
     294                   callback=self.on_merge_changed, 
     295                   callbackOnReturn=True) 
    164296        OWGUI.checkBox(box, self, 'MaintainArrayHeight', "Maintain array height") 
    165297        OWGUI.rubber(tab) 
    166298 
    167299        # INFO TAB 
    168         tab = OWGUI.createTabPage(self.tabs, "Info") #QVGroupBox(self) 
    169  
    170         box = OWGUI.widgetBox(tab,'Annotation && Legends') #QVButtonGroup("Annotation && Legends", tab) 
    171         OWGUI.checkBox(box, self, 'LegendOnTop', 'Show legend', callback=self.drawHeatMap) 
    172         OWGUI.checkBox(box, self, 'ShowAverageStripe', 'Stripes with averages', callback=self.drawHeatMap) 
    173         self.geneAnnotationsCB = OWGUI.checkBox(box, self, 'ShowGeneAnnotations', 'Gene annotations', callback=self.drawHeatMap) 
    174          
    175         self.annotationCombo = OWGUI.comboBox(box, self, "BAnnotationIndx", items=[], callback=lambda x='BAnnotationVar', y='BAnnotationIndx': self.setMetaID(x, y)) 
     300        tab = OWGUI.createTabPage(self.tabs, "Info") 
     301 
     302        box = OWGUI.widgetBox(tab,'Annotation && Legends') 
     303        OWGUI.checkBox(box, self, 'ShowLegend', 'Show legend',  
     304                       callback=self.update_legend) 
     305        OWGUI.checkBox(box, self, 'ShowAverageStripe', 'Stripes with averages',  
     306                       callback=self.update_averages_stripe) 
     307        self.geneAnnotationsCB = OWGUI.checkBox(box, self, 'ShowGeneAnnotations', 'Gene annotations',  
     308                                                callback=self.update_annotations) 
     309         
     310        self.annotationCombo = OWGUI.comboBox(box, self, "BAnnotationIndx", items=[], 
     311                                              callback=self.update_annotations)  
     312                                              #callback=lambda x='BAnnotationVar', y='BAnnotationIndx': self.setMetaID(x, y)) 
    176313 
    177314        box = OWGUI.widgetBox(tab, 'Column Labels') 
    178         columnLabelCB = OWGUI.checkBox(box, self, "ShowColumnLabels", "Display column labels", callback=self.drawHeatMap) 
    179         comboBox = OWGUI.comboBox(OWGUI.indentedBox(box), self, "ColumnLabelPosition", "Position", items=["Top", "Bottom"], callback=self.drawHeatMap) 
     315        columnLabelCB = OWGUI.checkBox(box, self, "ShowColumnLabels", "Display column labels", 
     316                                       callback=self.update_column_annotations) 
     317        posbox = OWGUI.widgetBox(OWGUI.indentedBox(box), "Position", flat=True) 
     318        comboBox = OWGUI.comboBox(posbox, self, "ColumnLabelPosition",  
     319                                  items=["Top", "Bottom"],  
     320                                  callback=self.update_column_annotations) 
    180321        columnLabelCB.disables.append(comboBox.box) 
    181322        columnLabelCB.makeConsistent() 
    182323         
    183         box = OWGUI.widgetBox(tab, "Ballon") #QVButtonGroup("Balloon", tab) 
    184         OWGUI.checkBox(box, self, 'BShowballoon', "Show balloon", \ 
    185             callback=lambda: self.balloonInfoBox.setDisabled(not self.BShowballoon)) 
    186         box = OWGUI.widgetBox(tab, "Ballon info") #QVButtonGroup("Balloon Info", tab) 
     324        box = OWGUI.widgetBox(tab, "Tool Tips") 
     325        cb = OWGUI.checkBox(box, self, 'GShowToolTip', "Show tool tips") 
     326        box = OWGUI.widgetBox(OWGUI.indentedBox(box), "Tool Tip Info") 
     327        box.setFlat(True) 
    187328        OWGUI.checkBox(box, self, 'BShowColumnID', "Column ID") 
    188329        self.spotIndxCB = OWGUI.checkBox(box, self, 'BShowSpotIndex', "Spot Index", \ 
     
    192333        OWGUI.checkBox(box, self, 'BShowGeneExpression', "Gene expression") 
    193334        OWGUI.checkBox(box, self, 'BShowAnnotation', "Annotation") 
    194         self.balloonInfoBox = box 
     335        self.toolTipInfoBox = box 
     336        cb.disables.append(box) 
     337        cb.makeConsistent() 
    195338        OWGUI.rubber(tab) 
    196339 
    197         # FILES TAB 
    198         self.filesTab = OWGUI.createTabPage(self.tabs, "Files") 
    199         box = OWGUI.widgetBox(self.filesTab, "Data Files") 
    200         self.fileLB = QListWidget(box) 
    201         box.layout().addWidget(self.fileLB) 
    202 ##        self.fileLB.setMaximumWidth(10) 
    203         self.connect(self.fileLB, SIGNAL("highlighted(int)"), self.fileSelectionChanged) 
    204         self.connect(self.fileLB, SIGNAL("selected(int)"), self.setFileReferenceBySelection) 
    205 ##        self.tabs.insertTab(self.filesTab, "Files") 
    206         self.tabs.setTabEnabled(self.tabs.indexOf(self.filesTab), 0) 
    207         hbox = OWGUI.widgetBox(box, orientation="horizontal") #QHBox(box) 
    208         self.fileUp = OWGUI.button(hbox, self, 'Up', \ 
    209             callback=lambda i=-1: self.fileOrderChange(i), disabled=1) 
    210         self.fileRef = OWGUI.button(hbox, self, 'Ref', self.setFileReference, disabled=1) 
    211         self.fileDown = OWGUI.button(hbox, self, 'Down', \ 
    212             callback=lambda i=1: self.fileOrderChange(i), disabled=1) 
    213         for btn in [self.fileUp, self.fileRef, self.fileDown]: 
    214             btn.setMaximumWidth(45) 
    215  
    216         OWGUI.checkBox(self.filesTab, self, 'ShowDataFileNames', 'Show data file names', 
    217            callback=self.drawHeatMap) 
    218         OWGUI.radioButtonsInBox(self.filesTab, self, 'SelectionType', \ 
    219            ['Single data set', 'Multiple data sets'], box='Selection', callback=self.removeSelection) 
    220         OWGUI.rubber(self.filesTab) 
    221  
    222         self.resize(800,400) 
    223  
    224         # canvas with microarray 
    225         self.scene = HeatMapGraphicsScene() 
    226         self.sceneView = MyGraphicsView(self.scene, self.mainArea) 
    227         self.selection = SelectData(self, self.scene) 
     340        # SPLIT TAB 
     341        self.splitTab = OWGUI.createTabPage(self.tabs, "Split Data") 
     342        box = OWGUI.widgetBox(self.splitTab, "Split By") 
     343        self.splitLB = QListWidget(box) 
     344        box.layout().addWidget(self.splitLB) 
     345        self.connect(self.splitLB, SIGNAL("itemSelectionChanged()"), self.split_changed) 
     346 
     347        # Scene with microarray 
     348        self.heatmap_scene = self.scene = HeatmapScene() 
     349        self.selection_manager = HeatmapSelectionManager(self) 
     350        self.connect(self.selection_manager, SIGNAL("selection_changed()"), self.on_selection_changed) 
     351        self.connect(self.selection_manager, SIGNAL("selection_finished()"), self.on_selection_finished) 
     352        self.heatmap_scene.set_selection_manager(self.selection_manager) 
     353        item = QGraphicsRectItem(0, 0, 10, 10, None, self.heatmap_scene) 
     354        self.heatmap_scene.itemsBoundingRect() 
     355        self.heatmap_scene.removeItem(item) 
     356         
     357        self.sceneView = QGraphicsView(self.scene) 
    228358        self.currentHighlightedCluster = None 
    229359        self.selectedClusters = [] 
    230360        self.mainArea.layout().addWidget(self.sceneView) 
     361        self.heatmap_scene.widget = None 
     362        self.heatmap_widget_grid = [[]] 
     363        self.attr_annotation_widgets = [] 
     364        self.attr_dendrogram_widgets = [] 
     365        self.gene_annotation_widgets = [] 
     366        self.gene_dendrogram_widgets = [] 
     367         
     368        self.heatmaps = [] 
     369         
     370        self.selection_rects = [] 
     371         
     372        self.attr_cluster = None 
     373        self.data_clusters = [] 
     374        self.sorted_data = None 
     375         
     376        self.resize(800,400) 
    231377 
    232378    def createColorStripe(self, palette): 
     
    234380        bmp = chr(252)*dx*2 + reduce(lambda x,y:x+y, \ 
    235381           [chr(i*250/dx) for i in range(dx)] * (dy-4)) + chr(252)*dx*2  
    236 ##        image = QImage(bmp, dx, dy, 8, self.ColorPalettes[palette], 256, QImage.LittleEndian) 
    237         image = QImage(bmp, dx, dy, QImage.Format_Indexed8)# self.ColorPalettes[palette], 256, QImage.LittleEndian) 
     382         
     383        image = QImage(bmp, dx, dy, QImage.Format_Indexed8) 
    238384        image.setColorTable(signedPalette(self.ColorPalettes[palette])) 
    239385 
     
    258404        self.CurrentPalette = 0 
    259405         
     406    def getGammaCorrectedPalette(self): 
     407        return [QColor(*self.contPalette.getRGB(float(i)/250, gamma=self.Gamma)).rgb() for i in range(250)] + self.palette[-6:] 
     408 
     409    def setColor(self, index, dialog=None, update=True): 
     410        self.selectedSchemaIndex = index 
     411        if not dialog: 
     412            dialog = self.createColorDialog() 
     413 
     414        self.colorCombo.setPalettes("palette", dialog) 
     415        self.colorCombo.setCurrentIndex(self.selectedSchemaIndex) 
     416        self.contPalette = palette = dialog.getExtendedContinuousPalette("palette") 
     417        unknown = dialog.getColor("unknown").rgb() 
     418        underflow = dialog.getColor("underflow").rgb() 
     419        overflow = dialog.getColor("overflow").rgb() 
     420        self.palette = [QColor(*palette.getRGB(float(i)/250, gamma=self.Gamma)).rgb() for i in range(250)] + [qRgb(255, 255, 255)]*3 +[underflow, overflow, unknown] 
     421 
     422        if update: 
     423            self.update_color_schema() 
     424#            self.drawHeatMap() 
     425         
     426    def openColorDialog(self): 
     427        dialog = self.createColorDialog() 
     428        if dialog.exec_(): 
     429            self.colorSettings = dialog.getColorSchemas() 
     430            self.selectedSchemaIndex = dialog.selectedSchemaIndex 
     431            self.colorCombo.setCurrentIndex(self.selectedSchemaIndex) 
     432            self.setColor(self.selectedSchemaIndex, dialog) 
     433 
     434    def createColorDialog(self): 
     435        c = OWColorPalette.ColorPaletteDlg(self, "Color Palette") 
     436        c.createExtendedContinuousPalette("palette", "Continuous Palette", initialColor1=QColor(Qt.blue), initialColor2=QColor(255, 255, 0).rgb(), extendedPassThroughColors = ((Qt.red, 1), (Qt.darkYellow, 1), (Qt.black, 1), (Qt.magenta, 1), (Qt.green, 1))) 
     437        box = c.createBox("otherColors", "Other Colors") 
     438        c.createColorButton(box, "unknown", "Unknown", Qt.gray) 
     439        box.layout().addSpacing(5) 
     440        c.createColorButton(box, "overflow", "Overflow", Qt.black) 
     441        box.layout().addSpacing(5) 
     442        c.createColorButton(box, "underflow", "Underflow", Qt.white) 
     443        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex) 
     444        return c 
     445     
    260446    # any time the data changes, the two combo boxes showing meta attributes 
    261447    # have to be adjusted 
     
    287473    def setMetaID(self, val, valIndx): 
    288474        setattr(self, val, self.meta[getattr(self, valIndx)]) 
    289         if val=='BAnnotationVar': 
    290             self.drawHeatMap() 
     475#        if val=='BAnnotationVar': 
     476#            self.drawHeatMap() 
    291477 
    292478    def setMetaCombos(self): 
    293         self.meta = [m.name for m in self.data[0].domain.getmetas().values()] 
     479        self.meta = [m.name for m in self.data.domain.getmetas().values()] 
    294480        self.BSpotVar, self.BSpotIndx = self.setMetaCombo(self.spotCombo, self.BSpotVar, \ 
    295481            enabled=self.BShowSpotIndex, default='RMI') 
    296482        self.BAnnotationVar, self.BAnnotationIndx = self.setMetaCombo(self.annotationCombo, \ 
    297483            self.BAnnotationVar, enabled=self.BShowAnnotation, default='xannotation') 
    298  
     484         
     485    def set_meta_combos(self): 
     486        self.spotCombo.clear() 
     487        self.annotationCombo.clear() 
     488         
     489        self.meta = self.data.domain.getmetas().values() 
     490        names = [m.name for m in self.meta] 
     491         
     492        self.spotCombo.addItems(names) 
     493        self.annotationCombo.addItems(names) 
     494        enabled = bool(self.meta) 
     495         
     496        self.spotIndxCB.setEnabled(enabled) 
     497        self.geneAnnotationsCB.setEnabled(enabled) 
     498         
     499        self.spotCombo.setEnabled(enabled and self.BShowSpotIndex) 
     500        self.annotationCombo.setEnabled(enabled and self.BShowAnnotation) 
     501         
     502        self.BSpotIndx = 0 
     503        self.BSpotVar = self.meta[0] if self.meta else None 
     504        self.BAnnotationIndx = 0 
     505        self.BAnnotationVar = self.meta[0] if self.meta else None 
     506 
     507    def get_candidate_splits(self): 
     508        """ Return candidate labels on which we can split the data.  
     509        """ 
     510        if self.data is not None: 
     511            groups = defaultdict(list) 
     512            for attr in self.data.domain.attributes: 
     513                for item in attr.attributes.items(): 
     514                    groups[item].append(attr) 
     515                 
     516            by_keys = defaultdict(list) 
     517            for (key, value), attrs in groups.items(): 
     518                by_keys[key].append(attrs) 
     519             
     520            # Find the keys for which all values have the same number of attributes. 
     521            candidates = [] 
     522            for key, groups in by_keys.items(): 
     523                count = len(groups[0]) 
     524                if all(len(attrs) == count for attrs in groups) and len(groups) > 1 and count > 1: 
     525                    candidates.append(key) 
     526                     
     527            return candidates 
     528        else: 
     529            return [] 
     530             
     531    def set_split_labels(self): 
     532        """ Set the list view in Split tab. 
     533        """ 
     534        self.splitLB.addItems(self.get_candidate_splits()) 
     535         
     536    def selected_split_label(self): 
     537        item = self.splitLB.currentItem() 
     538        return str(item.text()) if item else None 
     539         
     540    def clear(self): 
     541        self.data = None 
     542        self.spotCombo.clear() 
     543        self.annotationCombo.clear() 
     544        self.splitLB.clear() 
     545        self.meta = [] 
     546         
     547        self.clear_scene() 
     548         
     549    def clear_scene(self): 
     550        self.selection_manager.set_heatmap_widgets([[]]) 
     551        self.heatmap_scene.clear() 
     552        self.heatmap_scene.widget = None 
     553        self.heatmap_widget_grid = [[]] 
     554        self.attr_annotation_widgets = [] 
     555        self.attr_dendrogram_widgets = [] 
     556        self.gene_annotation_widgets = [] 
     557        self.gene_dendrogram_widgets = [] 
     558         
     559        self.selection_rects = [] 
     560         
     561         
    299562    def saveFig(self): 
    300563        sizeDlg = OWChooseImageSizeDlg(self.scene, parent=self) 
     
    303566    ########################################################################## 
    304567    # handling of input/output signals 
    305  
    306     def dataset(self, data, id, blockUpdate=0): 
     568         
     569    def set_dataset(self, data=None, id=None): 
    307570        self.closeContext("Selection") 
    308         ids = [d.id for d in self.data] 
    309         if not data: 
    310             if id in ids: 
    311                 k = ids.index(id) 
    312                 del self.data[k] 
    313                 self.fileLB.takeItem(k) 
    314                 if self.refFile == k: 
    315                     self.refFile = 0 
    316                     if len(self.data): 
    317                         self.fileLB.changeItem(self.createListItem(self.data[0].name, self.refFile), \ 
    318                                                self.refFile) 
    319         else: 
    320             # check if the same length 
    321             if data.domain.classVar: 
    322                 domain = self.checkDomain(data) 
    323                 if domain: 
    324                     data = orange.ExampleTable(domain, data) 
    325             data.setattr("id", id) 
    326             if id in ids: 
    327                 indx = ids.index(id) 
    328                 self.data[indx] = data 
    329 ##                self.fileLB.changeItem(self.createListItem(data.name, indx), indx) 
    330                 self.fileLB.takeItem(indx) 
    331                 self.fileLB.insertItem(indx, self.createListItem(data.name, indx)) 
    332             else: 
    333                 self.fileLB.addItem(self.createListItem(data.name, len(self.data))) 
    334                 self.data.append(data) 
    335  
    336             if len(self.data) > 1: 
    337                 self.tabs.setTabEnabled(self.tabs.indexOf(self.filesTab), True) 
    338             else: 
    339                 self.tabs.setTabEnabled(self.tabs.indexOf(self.filesTab), False) 
    340             self.setMetaCombos() # set the two combo widgets according to the data 
    341  
     571        self.clear() 
     572        self.data = data 
     573        if data is not None: 
     574#            self.setMetaCombos() 
     575            self.set_meta_combos() 
     576            self.set_split_labels() 
     577             
    342578        self.unorderedData = None 
    343579        self.groupClusters = None 
    344  
    345     def chipdata(self, data): 
    346         self.data = [] # XXX should only remove the data from the same source, use id in this rutine 
    347         self.fileLB.clear() 
    348         self.refFile = 0 
    349         if not data: 
    350             for i in self.scene.items(): 
    351                 self.scene.removeItem(i) 
    352             self.scene.update() 
    353             return 
    354         indx = 0 
    355         for (strainname, ds) in data: 
    356             for d in ds: 
    357                 self.dataset(d, indx, blockUpdate=1) 
    358                 indx += 1 
    359 #        self.createHeatMap() 
    360  
    361         pb = OWGUI.ProgressBar(self, iterations=len(self.data)) 
    362         self.constructHeatmap(callback=pb.advance) 
    363         self.scene.update() 
    364         pb.finish() 
    365580         
    366581    def handleNewSignals(self): 
    367582        self.send('Examples', None) 
    368         self.send('Structured Data', None) 
    369         self.constructHeatmap() 
    370 #        self.scene.update() 
    371583        if self.data: 
    372             self.openContext("Selection", self.data[0]) 
    373          
    374          
    375  
    376     def orderClustering(self, data): 
    377         import orngClustering 
    378         self.progressBarInit() 
    379         progressCallback = lambda value, caller=None: self.progressBarSet(value) 
    380          
    381         clusterRoots = [] 
    382         orderedData = [] 
    383         mapping = [] 
    384          
    385         valuesCount = len(data.domain.classVar.values) if data.domain.classVar else 0 
    386         attrRoot = orngClustering.hierarchicalClustering_attributes(data, progressCallback=lambda value: progressCallback((value)/(valuesCount or 1 + 1)), order=self.SortGenes==3) 
    387         orderedDomain = orange.Domain([data.domain.attributes[i] for i in attrRoot], data.domain.classVar) 
    388         orderedDomain.addmetas(data.domain.getmetas()) 
    389          
    390         data = orange.ExampleTable(orderedDomain, data) 
    391           
    392         if data.domain.classVar and data.domain.classVar.values: 
    393             valuesCount = len(data.domain.classVar.values) + 1 
    394             for i, val in enumerate(data.domain.classVar.values): 
    395                 self.progressBarSet(100.0*(i + 1)/valuesCount) 
    396                 tmpData = orange.ExampleTable([ex for ex in data if ex.getclass()==val]) 
    397                 root = orngClustering.hierarchicalClustering(tmpData, progressCallback=lambda value: progressCallback((100.0*(i + 1) + value)/valuesCount), order=self.SortGenes==3) 
    398                 orderedData.extend([tmpData[i] for i in root.mapping]) 
    399                 mapping.extend([i+len(mapping) for i in root.mapping]) 
    400                 clusterRoots.append(root) 
    401              
    402         else: 
    403             root = orngClustering.hierarchicalClustering(data, progressCallback=progressCallback, order=self.SortGenes==3) 
    404             orderedData.extend([data[i] for i in root.mapping]) 
    405             mapping = list(root.mapping) 
    406             clusterRoots.append(root) 
    407  
    408         self.progressBarFinished() 
    409         return orange.ExampleTable(orderedData), clusterRoots, mapping, attrRoot 
    410          
    411     def constructHeatmap(self, callback=None): 
    412         if len(self.data): 
    413             self.heatmapconstructor = [None] * len(self.data) 
    414             self.unorderedData = self.data if not self.unorderedData else self.unorderedData 
    415             self.groupClusters = [] 
    416             self.attrCluster = None 
    417             self.mapping = None 
    418             sortData = lambda data, mapping, domain=None: orange.ExampleTable(domain or data.domain, [data[i] for i in mapping]) 
    419             if self.SortGenes: 
    420                 if self.SortGenes > 1: ## cluster sort 
    421                     refData, self.groupClusters , self.mapping, self.attrCluster = self.orderClustering(self.unorderedData[self.refFile]) 
    422                     sortedData = sortData(self.data[self.refFile], self.mapping, refData.domain) 
    423                     self.heatmapconstructor[self.refFile] = \ 
    424                         orangene.HeatmapConstructor(sortedData, None) 
    425                     self.heatmapconstructor[self.refFile].setattr("_sortedData", sortedData) 
    426                 else: 
    427                     self.heatmapconstructor[self.refFile] = \ 
    428                         orangene.HeatmapConstructor(self.data[self.refFile]) 
     584            self.update_heatmaps() 
     585        else: 
     586            self.clear() 
     587         
     588        if self.data: 
     589            self.openContext("Selection", self.data) 
     590             
     591    def construct_heatmaps(self, data, split_label=None): 
     592        if split_label is not None: 
     593            groups = split_domain(data.domain, split_label) 
     594        else: 
     595            groups = [("", data.domain)] 
     596             
     597        group_domains = [dom for _, dom in groups] 
     598         
     599        if self.SortGenes > 1: 
     600            self.progressBarInit() 
     601            attr_ordering, attr_cluster, data_ordering, data_clusters = \ 
     602                    hierarchical_cluster_ordering(data, group_domains, 
     603                                                  opt_order=self.SortGenes == 3, 
     604                                                  progress_callback=self.progressBarSet) 
     605            sorted_data = [data[i] for i in itertools.chain(*data_ordering)] 
     606            self.progressBarFinished() 
     607             
     608        else: 
     609            attr_ordering = range(len(group_domains[0][1].attributes)) 
     610            attr_cluster = None 
     611            data_ordering = [] 
     612            data_clusters = [None] 
     613            sorted_data = data 
     614             
     615        self.heatmapconstructor = [] 
     616        self._group_data = [] 
     617         
     618        for name, group_domain in groups: 
     619            if attr_ordering != sorted(attr_ordering): 
     620                domain = orange.Domain([group_domain[i] for i in attr_ordering], group_domain.classVar) 
     621                domain.addmetas(group_domain.getmetas()) 
     622                group_domain = domain 
     623                 
     624            group_data = orange.ExampleTable(group_domain, sorted_data) 
     625            self._group_data.append((group_data, group_domain)) # Crashes at accessing the heatmap.examples[0] without this  
     626            if self.SortGenes == 1: 
     627                hc = orangene.HeatmapConstructor(group_data) 
    429628            else: 
    430                 self.heatmapconstructor[self.refFile] = \ 
    431                     orangene.HeatmapConstructor(self.data[self.refFile], None) 
    432             if callback: callback() 
    433  
    434             for i in range(len(self.data)): 
    435                 if i <> self.refFile: 
    436                     if self.mapping: 
    437                         self.heatmapconstructor[i] = orangene.HeatmapConstructor(sortData(self.data[i],self.mapping, refData.domain), 
    438                             self.heatmapconstructor[self.refFile]) 
    439                     else:                         
    440                         self.heatmapconstructor[i] = orangene.HeatmapConstructor(self.data[i], 
    441                             self.heatmapconstructor[self.refFile]) 
    442                     if callback: callback() 
    443         else: 
    444             self.heatmapconstructor = [] 
    445         self.createHeatMap() 
    446  
    447     # remove unused values from the class of the data set 
    448     def checkDomain(self, data, selection = None): 
    449         # Reduce the number of class values, if class is defined 
    450         cl = clo = data.domain.classVar 
    451         if cl: 
    452             if selection: 
    453                 cl = orange.RemoveUnusedValues(cl, selection, removeOneValued = 1) 
     629                hc = orangene.HeatmapConstructor(group_data, None) 
     630             
     631            self.heatmapconstructor.append(hc) 
     632             
     633        self.attr_cluster = attr_cluster 
     634        self.data_clusters = data_clusters 
     635        self.sorted_data = sorted_data 
     636        self.group_domains = groups 
     637             
     638    def create_heatmaps(self, constructors): 
     639        self.lowerBound = 1000 
     640        self.upperBound = -1000 
     641        squeeze = 1.0 / self.Merge 
     642        self.heatmaps = [] 
     643        for hmc in constructors: 
     644            hm, lb, ub = hmc(squeeze) 
     645             
     646            self.lowerBound = min(self.lowerBound, lb) 
     647            self.upperBound = max(self.upperBound, ub) 
     648                 
     649            self.heatmaps.append(hm) 
     650             
     651        for cluster, heatmap in zip(self.data_clusters, self.heatmaps[0]): 
     652            if cluster is not None: 
     653                cluster._heatmap = heatmap 
     654             
     655        self.sliderCutLow.setRange(self.lowerBound, 0, 0.1) 
     656        self.sliderCutHigh.setRange(1e-10, self.upperBound, 0.1) 
     657        self.CutLow = max(self.CutLow, self.lowerBound) 
     658        self.CutHigh = min(self.CutHigh, self.upperBound) 
     659        self.sliderCutLow.setValue(self.CutLow) 
     660        self.sliderCutHigh.setValue(self.CutHigh) 
     661             
     662    def point_size_hint(self, height): 
     663        font = QFont(self.font()) 
     664        font.setPointSize(height) 
     665        fix = 0 
     666        while QFontMetrics(font).lineSpacing() > height and height - fix > 1: 
     667            fix += 1 
     668            font.setPointSize(height - fix) 
     669        return height - fix 
     670     
     671    def construct_heatmaps_scene(self, heatmaps, data, attr_cluster=None, data_clusters=None): 
     672        self.heatmap_scene.clear() 
     673        widget = GridWidget() 
     674        self.heatmap_scene.addItem(widget) 
     675        layout = QGraphicsGridLayout() 
     676        layout.setSpacing(self.SpaceX) 
     677        widget.setLayout(layout) 
     678         
     679        classVar = data.domain.classVar 
     680        if classVar and isinstance(classVar, orange.EnumVariable): 
     681            classes = classVar.values 
     682        else: 
     683            classes = [None] 
     684         
     685        if self.CutEnabled: 
     686            cut_low, cut_high = self.CutLow, self.CutHigh 
     687        else: 
     688            cut_low, cut_high = self.lowerBound, self.upperBound 
     689             
     690        palette = self.getGammaCorrectedPalette() if self.Gamma !=0 else self.palette 
     691         
     692        class_dendrograms = [] 
     693        attr_dendrograms = [] 
     694        heatmap_widgets = [] 
     695        attr_annotation_widgets = [] 
     696        gene_annotation_widgets = [] 
     697        attr_annotation_widgets_top = [] 
     698        attr_annotation_widgets_bottom = [] 
     699         
     700        # Dendrograms on the left side 
     701        if data_clusters and any(data_clusters): 
     702            for i, cluster in enumerate(data_clusters): 
     703                class_dendrogram = DendrogramWidget(cluster, parent=widget, orientation=Qt.Vertical) 
     704                class_dendrogram.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 
     705                left, top, right, bottom = class_dendrogram.layout().getContentsMargins() 
     706                class_dendrogram.layout().setContentsMargins(left, self.CellHeight / 2.0 / self.Merge, 0.0, self.CellHeight / 2.0 / self.Merge) 
     707                class_dendrogram.setMinimumWidth(100) 
     708                class_dendrogram.setMaximumWidth(100) 
     709                 
     710                layout.addItem(class_dendrogram, i*2 + 5, 0) 
     711                class_dendrograms.append(class_dendrogram) 
     712             
     713        # Class labels     
     714        for i, class_ in enumerate(classes): 
     715            if class_ is not None: 
     716                item = GtI(class_, widget) 
     717                item = GraphicsSimpleTextLayoutItem(item, parent=widget) 
     718                item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 
     719                layout.addItem(item, i*2 + 4, 2) 
     720                layout.setRowSpacing(i*2 + 4, 2) 
     721                layout.setAlignment(item, Qt.AlignLeft | Qt.AlignVCenter) 
     722                 
     723        font = QFont() 
     724        font.setPointSize(self.point_size_hint(self.CellHeight)) 
     725         
     726        class_row_labels = [map(str, hm.exampleIndices) for hm in heatmaps[0]] 
     727        group_column_labels = [[a.name for a in hm[0].examples.domain.attributes] for hm in heatmaps] 
     728         
     729        # Gene annotations on the right side 
     730        for i, labels in enumerate(class_row_labels): 
     731            list = GraphicsSimpleTextList(labels, parent=widget, orientation=Qt.Vertical) 
     732            list.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) 
     733            list.setFont(font) 
     734            list.setContentsMargins(0.0, 0.0, 0.0, 0.0) 
     735            list.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) 
     736             
     737            layout.addItem(list, i*2 + 5, len(self.heatmaps) + 2) 
     738            layout.setAlignment(list, Qt.AlignLeft) 
     739            gene_annotation_widgets.append(list) 
     740             
     741        font = QFont() 
     742        font.setPointSizeF(self.point_size_hint(self.CellWidth)) 
     743         
     744        if self.ShowAverageStripe: 
     745            stripe_offset = c_averageStripeWidth + 2 
     746        else: 
     747            stripe_offset = 0 
     748             
     749        for column, (hm, labels, group) in enumerate(zip(heatmaps, group_column_labels, self.group_domains)): 
     750            column_heatmap_widgets = [] 
     751             
     752            # Top group label 
     753            if len(heatmaps) > 1: 
     754                item = GtI(group[0], widget) 
     755                item = GraphicsSimpleTextLayoutItem(item, parent=widget) 
     756                layout.addItem(item, 1, column + 2) 
     757                layout.setRowSpacing(1, 2) 
     758                layout.setRowMaximumHeight(1, item.geometry().height()) 
     759                layout.setAlignment(item, Qt.AlignLeft | Qt.AlignVCenter) 
    454760            else: 
    455                 cl = orange.RemoveUnusedValues(cl, data, removeOneValued = 1) 
    456  
    457         # Construct a new domain only if the class has changed 
    458         # (ie to lesser number of values or to one value (alias None)) 
    459         if cl != clo: 
    460             domain = orange.Domain(data.domain.attributes, cl) 
    461             metas = data.domain.getmetas() 
    462             for key in metas: 
    463                 domain.addmeta(key, metas[key]) 
    464             return domain 
    465         else: 
    466             return None 
    467  
    468     # send out the data for selected rows, rows = [(group, from, to), ...] 
    469     def prepareData(self, rows, indx): 
    470         ex = [] 
    471         for (g,s,e) in rows: 
    472             hm = self.heatmaps[indx][g] 
    473             ex += hm.examples[hm.exampleIndices[s] : hm.exampleIndices[e+1]] 
    474  
    475         # Reduce the number of class values, if class is defined 
    476         newdomain = self.checkDomain(self.data[indx], selection=ex) 
    477         if not newdomain: 
    478             newdomain = self.data[indx].domain 
    479         selectedData = orange.ExampleTable(newdomain, ex) 
    480         return selectedData 
    481  
    482     def sendOne(self, data, indx): 
    483         self.send("Examples", data) 
    484      
    485     def sendData(self, rows, indxs): 
    486         indxs.sort() 
    487  
    488         newdata = [None] * (max(indxs) + 1) 
    489         for i in indxs: 
    490             newdata[i] = self.prepareData(rows, i) 
    491             newdata[i].name = self.data[i].name 
    492             if not hasattr(self.data[i], "strain"): 
    493                 newdata[i].strain = "NoName (%d)" % i 
     761                layout.setRowMaximumHeight(1, 0) 
     762                layout.setRowSpacing(1, 0) 
     763                 
     764            # Top dendrogram 
     765            if attr_cluster is not None: 
     766                attr_dendrogram = DendrogramWidget(attr_cluster, parent=widget, orientation=Qt.Horizontal) 
     767                attr_dendrogram.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 
     768                attr_dendrogram.translate(0.0, attr_dendrogram.size().height()) 
     769                attr_dendrogram.scale(1.0, -1.0) 
     770                 
     771                left, top, right, bottom = attr_dendrogram.layout().getContentsMargins() 
     772                attr_dendrogram.layout().setContentsMargins(stripe_offset + self.CellWidth / 2.0, 0.0, self.CellWidth / 2.0, bottom) 
     773                attr_dendrogram.setMinimumHeight(100) 
     774                attr_dendrogram.setMaximumHeight(100) 
     775                 
     776                layout.addItem(attr_dendrogram, 2, column + 2) 
     777                layout.setRowMaximumHeight(2, 100) 
     778                attr_dendrograms.append(attr_dendrogram) 
     779             
     780             
     781            # Heatmap widget for each class  
     782            for i, (class_, chm) in enumerate(zip(classes, hm)):  
     783                hm_widget = GraphicsHeatmapWidget(heatmap=chm, parent=widget) 
     784                hm_widget.set_cell_size(int(self.CellWidth), int(self.CellHeight)) 
     785                hm_widget.set_cuts(cut_low, cut_high) 
     786                hm_widget.set_color_table(palette) 
     787                hm_widget.set_show_averages(self.ShowAverageStripe) 
     788                hm_widget.cell_tool_tip = lambda row, col, hm=hm_widget: self.cell_tool_tip(hm, row, col) 
     789                layout.addItem(hm_widget, i*2 + 5, column + 2) 
     790                column_heatmap_widgets.append(hm_widget) 
     791            heatmap_widgets.append(column_heatmap_widgets) 
     792             
     793            # Top attr annotations 
     794            list = GraphicsSimpleTextList(labels, parent=widget, orientation=Qt.Horizontal) 
     795            list.setAlignment(Qt.AlignBottom | Qt.AlignLeft) 
     796             
     797            list.setFont(font) 
     798            list.layout().setContentsMargins(stripe_offset, 0, 0, 0) 
     799            list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) 
     800             
     801            layout.addItem(list, 3, column + 2, Qt.AlignBottom | Qt.AlignLeft) 
     802            attr_annotation_widgets.append(list) 
     803            attr_annotation_widgets_top.append(list) 
     804             
     805            # Bottom attr annotations 
     806            list = GraphicsSimpleTextList(labels, parent=widget, orientation=Qt.Horizontal) 
     807            list.setAlignment(Qt.AlignTop | Qt.AlignHCenter) 
     808             
     809            list.setFont(font) 
     810            list.layout().setContentsMargins(stripe_offset, 0, 0, 0) 
     811            list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) 
     812             
     813            layout.addItem(list, len(hm)*2 + 5, column + 2) 
     814            attr_annotation_widgets.append(list) 
     815            attr_annotation_widgets_bottom.append(list) 
     816             
     817            # Legend 
     818            if column == 0: 
     819                item = GraphicsLegendWidget(self.heatmapconstructor[0], self.lowerBound, self.upperBound, parent=widget) 
     820                item.set_color_table(palette) 
     821                item.setVisible(self.ShowLegend) 
     822                layout.addItem(item, 0, 2, 1, len(self.heatmaps) + 1) 
     823                layout.setRowSpacing(0, 2) 
     824#                layout.setRowMaximumHeight(0, item.geometry().height()) 
     825             
     826        self.heatmap_scene.addItem(widget) 
     827        self.heatmap_scene.widget = widget 
     828        self.heatmap_widget_grid = heatmap_widgets 
     829        self.gene_annotation_widgets = gene_annotation_widgets 
     830        self.attr_annotation_widgets = attr_annotation_widgets 
     831        self.attr_annotation_widgets_top = attr_annotation_widgets_top 
     832        self.attr_annotation_widgets_bottom = attr_annotation_widgets_bottom 
     833        self.attr_dendrogram_widgets = attr_dendrograms 
     834         
     835        self.update_annotations() 
     836        self.update_column_annotations() 
     837         
     838        self.fix_grid_layout() 
     839         
     840        self.selection_manager.set_heatmap_widgets(heatmap_widgets) 
     841         
     842    def fix_grid_layout(self): 
     843        """ Fix grid layout when cell size changes or average 
     844        stripes are shown/hiddens. 
     845        """ 
     846        if self.heatmap_scene.widget: 
     847            layout = self.heatmap_scene.widget.layout() 
     848            layout.invalidate() 
     849            layout.activate() 
     850             
     851            for i, hw in enumerate(self.heatmap_widget_grid[0]): 
     852                max_h = hw.size().height() 
     853                layout.setRowMaximumHeight(i*2 + 5, max_h) 
     854                 
     855            for i, hw in enumerate(self.heatmap_widget_grid): 
     856                max_w = hw[0].size().width() 
     857                layout.setColumnMaximumWidth(i + 2, max_w) 
     858                if self.attr_dendrogram_widgets: 
     859                    dendrogram = self.attr_dendrogram_widgets[i] 
     860#                    dendrogram.resize(max_w, -1) 
     861                    dendrogram.setMaximumWidth(max_w) 
     862                self.attr_annotation_widgets_top[i].setMaximumWidth(max_w) 
     863                self.attr_annotation_widgets_bottom[i].setMaximumWidth(max_w) 
     864                 
     865#            for i, (hw, dend) in enumerate(zip(self.heatmap_widget_grid, self.attr_dendrogram_widgets)): 
     866#                max_w = hw[0].size().width() 
     867             
     868#            self.update_widget_margins()     
     869            self.heatmap_scene.widget.resize(self.heatmap_scene.widget.sizeHint(Qt.PreferredSize)) 
     870             
     871            self.on_selection_changed() 
     872            self.update_scene_rect() 
     873         
     874    def update_scene_rect(self): 
     875        rect = QRectF() 
     876        for item in self.heatmap_scene.items(): 
     877            rect |= item.sceneBoundingRect() 
     878        self.heatmap_scene.setSceneRect(rect) 
     879             
     880    def heatmap_widgets(self): 
     881        """ Iterate over heatmap widgets. 
     882        """ 
     883        for item in self.heatmap_scene.items(): 
     884            if isinstance(item, GraphicsHeatmapWidget): 
     885                yield item 
     886                 
     887    def label_widgets(self): 
     888        """ Iterate over GraphicsSimpleTextList widgets. 
     889        """ 
     890        for item in self.heatmap_scene.items(): 
     891            if isinstance(item, GraphicsSimpleTextList): 
     892                yield item 
     893                 
     894    def dendrogram_widgets(self): 
     895        """ Iterate over dendrogram widgets 
     896        """ 
     897        for item in self.heatmap_scene.items(): 
     898            if isinstance(item, DendrogramWidget): 
     899                yield item 
     900                 
     901    def legend_widgets(self): 
     902        for item in self.heatmap_scene.items(): 
     903            if isinstance(item, GraphicsLegendWidget): 
     904                yield item 
     905                 
     906    def update_cell_size(self): 
     907        """ Update cell sizes (by user request - height/width sliders) 
     908        """  
     909        for heatmap in self.heatmap_widgets(): 
     910            heatmap.set_cell_size(self.CellWidth, self.CellHeight) 
     911             
     912        hor_font = QFont(self.font()) 
     913        hor_font.setPointSize(self.point_size_hint(self.CellWidth)) 
     914        vert_font = QFont(self.font()) 
     915        vert_font.setPointSize(self.point_size_hint(self.CellHeight)) 
     916         
     917        # Also update the annotation items font. 
     918        for labels in self.label_widgets(): 
     919            if labels.orientation == Qt.Vertical: 
     920                labels.setFont(vert_font) 
    494921            else: 
    495                 newdata[i].strain = self.data[i].strain 
    496              
    497             self.sendOne(newdata[i], i) 
    498              
    499         groups = {} 
    500         for i in indxs: 
    501             groups[newdata[i].strain] = [] 
    502         for i in indxs: 
    503             groups[newdata[i].strain].append(i) 
    504         strains = groups.keys() 
    505         strains.sort() 
    506         datafiles = [] 
    507         for s in strains: 
    508             datafiles.append( (s, [newdata[i] for i in groups[s]]) ) 
    509         datafiles 
    510         self.send("Structured Data", datafiles) 
    511          
    512     ########################################################################## 
    513     # callback functions 
    514  
    515     def getGammaCorrectedPalette(self): 
    516         return [QColor(*self.contPalette.getRGB(float(i)/250, gamma=self.Gamma)).rgb() for i in range(250)] + self.palette[-6:] 
    517  
    518     def setColor(self, index, dialog=None, update=True): 
    519         self.selectedSchemaIndex = index 
    520         if not dialog: 
    521             dialog = self.createColorDialog() 
    522  
    523         self.colorCombo.setPalettes("palette", dialog) 
    524         self.colorCombo.setCurrentIndex(self.selectedSchemaIndex) 
    525         self.contPalette = palette = dialog.getExtendedContinuousPalette("palette") 
    526         unknown = dialog.getColor("unknown").rgb() 
    527         underflow = dialog.getColor("underflow").rgb() 
    528         overflow = dialog.getColor("overflow").rgb() 
    529         self.palette = [QColor(*palette.getRGB(float(i)/250, gamma=self.Gamma)).rgb() for i in range(250)] + [qRgb(255, 255, 255)]*3 +[underflow, overflow, unknown] 
    530  
    531         if update:         
    532             self.drawHeatMap() 
    533          
    534     def openColorDialog(self): 
    535         dialog = self.createColorDialog() 
    536         if dialog.exec_(): 
    537             self.colorSettings = dialog.getColorSchemas() 
    538             self.selectedSchemaIndex = dialog.selectedSchemaIndex 
    539             self.colorCombo.setCurrentIndex(self.selectedSchemaIndex) 
    540             self.setColor(self.selectedSchemaIndex, dialog) 
    541  
    542     def createColorDialog(self): 
    543         c = OWColorPalette.ColorPaletteDlg(self, "Color Palette") 
    544         c.createExtendedContinuousPalette("palette", "Continuous Palette", initialColor1=QColor(Qt.blue), initialColor2=QColor(255, 255, 0).rgb(), extendedPassThroughColors = ((Qt.red, 1), (Qt.darkYellow, 1), (Qt.black, 1), (Qt.magenta, 1), (Qt.green, 1))) 
    545         box = c.createBox("otherColors", "Other Colors") 
    546         c.createColorButton(box, "unknown", "Unknown", Qt.gray) 
    547         box.layout().addSpacing(5) 
    548         c.createColorButton(box, "overflow", "Overflow", Qt.black) 
    549         box.layout().addSpacing(5) 
    550         c.createColorButton(box, "underflow", "Underflow", Qt.white) 
    551         c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex) 
    552         return c 
    553  
    554  
    555     def setCutEnabled(self): 
     922                labels.setFont(hor_font) 
     923 
     924        self.update_widget_margins() 
     925        ## To hide the annotations if cell sizes to small. 
     926        self.update_annotations() 
     927        self.update_column_annotations() 
     928             
     929        self.fix_grid_layout() 
     930         
     931    def update_widget_margins(self): 
     932        """ Update dendrogram and text list widgets margins to incude the 
     933        space for average stripe. 
     934        """ 
     935        if self.ShowAverageStripe: 
     936            stripe_offset = c_averageStripeWidth + 2 
     937        else: 
     938            stripe_offset = 0 
     939        right = self.CellWidth / 2.0 
     940         
     941        top = self.CellHeight / 2.0 / self.Merge 
     942        bottom = self.CellHeight / 2.0 / self.Merge 
     943         
     944        for dendrogram in self.dendrogram_widgets(): 
     945            layout = dendrogram.layout()  
     946            if dendrogram.orientation == Qt.Horizontal: 
     947#                index = self.attr_dendrogram_widgets.index(dendrogram) 
     948#                heatmap = self.heatmap_widget_grid[index][0] 
     949#                h_w = heatmap.size().width() 
     950#                d_w = dendrogram.size().width() 
     951#                right_ = d_w - stripe_offset - self.CellWidth - h_w 
     952                _, top_h, _, bottom_h = layout.getContentsMargins() 
     953                layout.setContentsMargins(stripe_offset + self.CellWidth / 2.0, top_h, right, bottom_h) 
     954            else: 
     955                left_v, _, right_v, _ = layout.getContentsMargins() 
     956                layout.setContentsMargins(left_v, top, right_v, bottom) 
     957                 
     958        for widget in self.label_widgets(): 
     959            layout = widget.layout() 
     960            if widget.orientation == Qt.Horizontal: 
     961                left_h, top, right, bottom = layout.getContentsMargins() 
     962                layout.setContentsMargins(stripe_offset, top, right, bottom) 
     963         
     964    def update_averages_stripe(self): 
     965        """ Update the visibility of the averages stripe. 
     966        """ 
     967        if self.data: 
     968            for widget in self.heatmap_widgets(): 
     969                widget.set_show_averages(self.ShowAverageStripe) 
     970                 
     971            self.update_widget_margins() 
     972            self.fix_grid_layout() 
     973             
     974    def update_grid_spacing(self): 
     975        """ Update layout spacing. 
     976        """ 
     977        if self.scene.widget: 
     978            layout = self.scene.widget.layout() 
     979            layout.setSpacing(self.SpaceX) 
     980            self.fix_grid_layout() 
     981         
     982    def update_color_schema(self): 
     983        palette = self.getGammaCorrectedPalette() if self.Gamma !=0 else self.palette 
     984        for heatmap in self.heatmap_widgets(): 
     985            heatmap.set_color_table(palette) 
     986             
     987        for legend in self.legend_widgets(): 
     988            legend.set_color_table(palette) 
     989             
     990    def update_thresholds(self): 
    556991        self.sliderCutLow.box.setDisabled(not self.CutEnabled) 
    557992        self.sliderCutHigh.box.setDisabled(not self.CutEnabled) 
    558         self.drawHeatMap() 
    559  
    560     def mergeChanged(self): 
     993             
     994        if self.data: 
     995            if self.CutEnabled: 
     996                low, high = self.CutLow, self.CutHigh 
     997            else: 
     998                low, high = self.lowerBound, self.upperBound 
     999            for heatmap in self.heatmap_widgets(): 
     1000                heatmap.set_cuts(low, high) 
     1001     
     1002    def update_sorting(self): 
     1003        if self.data: 
     1004            self.update_heatmaps() 
     1005             
     1006    def update_legend(self): 
     1007        for item in self.heatmap_scene.items(): 
     1008            if isinstance(item, GraphicsLegendWidget): 
     1009                item.setVisible(self.ShowLegend) 
     1010         
     1011    def update_annotations(self): 
     1012        if self.data: 
     1013            if self.meta: 
     1014                attr = self.meta[self.BAnnotationIndx] 
     1015            else: 
     1016                attr = None 
     1017             
     1018            show = self.ShowGeneAnnotations and attr and self.Merge == 1 
     1019            show = show and self.CellHeight > 3 
     1020            for list_widget, hm in zip(self.gene_annotation_widgets, self.heatmap_widget_grid[0]): 
     1021                list_widget.setVisible(bool(show)) 
     1022                if show: 
     1023                    hm = hm.heatmap 
     1024                    examples = hm.examples 
     1025                    indices = hm.exampleIndices[:-1] 
     1026                    labels = [str(examples[i][attr]) for i in indices] 
     1027                    list_widget.set_labels(labels) 
     1028 
     1029    def update_column_annotations(self): 
     1030        if self.data: 
     1031            show = self.CellWidth > 3 
     1032            show_top = self.ShowColumnLabels and self.ColumnLabelPosition == 0 and show 
     1033            show_bottom = self.ShowColumnLabels and self.ColumnLabelPosition == 1 and show 
     1034             
     1035            for list_widget in self.attr_annotation_widgets_top: 
     1036                list_widget.setVisible(show_top) 
     1037                 
     1038            layout = self.heatmap_scene.widget.layout() 
     1039            layout.setRowMaximumHeight(3,  -1 if show_top else 0) 
     1040            layout.setRowSpacing(3, -1 if show_top else 0) 
     1041                 
     1042            for list_widget in self.attr_annotation_widgets_bottom: 
     1043                list_widget.setVisible(show_bottom) 
     1044                 
     1045            layout.setRowMaximumHeight(len(self.heatmap_widget_grid[0]) + 4, -1 if show_top else 0) 
     1046                 
     1047            self.fix_grid_layout() 
     1048             
     1049    def update_heatmaps(self): 
     1050        if self.data: 
     1051            self.construct_heatmaps(self.data, self.selected_split_label()) 
     1052            self.create_heatmaps(self.heatmapconstructor) 
     1053            self.clear_scene() 
     1054            self.construct_heatmaps_scene(self.heatmaps, self.data, 
     1055                                          attr_cluster=self.attr_cluster, 
     1056                                          data_clusters=self.data_clusters) 
     1057        else: 
     1058            self.clear() 
     1059         
     1060    def update_heatmaps_stage2(self): 
     1061        if self.data: 
     1062            self.create_heatmaps(self.heatmapconstructor) 
     1063            self.clear_scene() 
     1064            self.construct_heatmaps_scene(self.heatmaps, self.data, 
     1065                                          attr_cluster=self.attr_cluster, 
     1066                                          data_clusters=self.data_clusters) 
     1067         
     1068    def cell_tool_tip(self, heatmap_widget, row, column): 
     1069        if not self.GShowToolTip: 
     1070            return "" 
     1071        hm = heatmap_widget.heatmap 
     1072        examples = hm.examples[hm.exampleIndices[row] : hm.exampleIndices[row+1]] 
     1073        domain = hm.examples.domain 
     1074        if hm.getCellIntensity(row, column) != None: 
     1075            head = "%6.4f" % hm.getCellIntensity(row, column) 
     1076        else: 
     1077            head = "Missing Data" 
     1078        if self.BShowColumnID: 
     1079            head += "\n" + domain.attributes[column].name 
     1080        # tool tip, construct body 
     1081        body = "" 
     1082        if (self.BShowSpotIndex and self.BSpotVar) or \ 
     1083                (self.BShowAnnotation and self.BAnnotationVar) or \ 
     1084                 self.BShowGeneExpression: 
     1085            for (i, e) in enumerate(examples): 
     1086                if i > 5: 
     1087                    body += "\n... (%d more)" % (len(examples) - 5) 
     1088                    break 
     1089                else: 
     1090                    s = [] 
     1091                    if self.BShowSpotIndex and self.BSpotVar: 
     1092                        s.append(str(e[self.BSpotVar])) 
     1093                    if self.BShowGeneExpression: 
     1094                        s.append(str(e[column])) 
     1095                    if self.BShowAnnotation and self.BAnnotationVar: 
     1096                        s.append(str(e[self.BAnnotationVar])) 
     1097             
     1098                body += "\n" 
     1099                body += " | ".join(s) 
     1100        return head + body 
     1101     
     1102    def on_merge_changed(self): 
    5611103        self.oldMerge = self.savedMerge 
    562         if self.MaintainArrayHeight and self.oldMerge <> self.Merge: 
    563             k = self.Merge / self.oldMerge 
    564             l = max(1, min(self.CellHeight * k, self.maxVSize)) 
    565             if l <> self.CellHeight: 
     1104        if self.MaintainArrayHeight and self.oldMerge != self.Merge: 
     1105            k = float(self.Merge) / self.oldMerge 
     1106            l = max(1, min(int(self.CellHeight * k), self.maxVSize)) 
     1107            if l != self.CellHeight: 
    5661108                self.CellHeight = l 
    5671109                self.sliderVSize.setValue(self.CellHeight) 
    5681110 
    569         self.createHeatMap() 
     1111        self.update_heatmaps_stage2() 
    5701112        self.savedMerge = self.Merge 
    571  
    572     def fileOrderChange(self, chg): 
    573         if chg==-1 and self.selectedFile>0: 
    574             switchFiles(self.selectedFile, self.selectedFile-1) 
    575         if chg==1  and self.selectedFile < len(self.data - 1): 
    576             switchFiles(self.selectedFile, self.selectedFile+1) 
    577          
    578  
    579     # ######################################################################## 
    580     # drawing 
    581  
    582     def drawLegend(self, x, y, width, height, palette): 
    583         legend = self.heatmapconstructor[0].getLegend(width, height, 1.0) #self.Gamma) 
    584  
    585         lo = self.CutEnabled and self.CutLow   or self.lowerBound 
    586         hi = self.CutEnabled and self.CutHigh  or self.upperBound 
    587  
    588         t = QGraphicsSimpleTextItem("%3.1f" % lo, None, self.scene) #QCanvasText("%3.1f" % lo, self.canvas) 
    589         t.setPos(x, y) #setX(x); t.setY(y) 
    590         t.show() 
    591         t = QGraphicsSimpleTextItem("%3.1f" % hi, None, self.scene) #QCanvasText("%3.1f" % hi, self.canvas) 
    592         t.setPos(x+width-t.boundingRect().width(), y) #setX(x+width-t.boundingRect().width()); t.setY(y) 
    593         t.show() 
    594         y += t.boundingRect().height()+1 
    595         self.legendItem = ImageItem(legend, self.scene, width, height, palette, x=x, y=y) 
    596         return y + c_legendHeight + c_spaceY 
    597  
    598     def drawFileName(self, label, x, y, width): 
    599         t = QGraphicsSimpleTextItem(label, None, self.scene) 
    600         t.setPos(x, y) #setX(x); t.setY(y) 
    601         t.show() 
    602         line = QGraphicsLineItem(None, self.scene) 
    603         line.setPoints(0, 0, width, 0) 
    604         y += t.boundingRect().height() 
    605         line.setPos(x, y) #setX(x); line.setY(y) 
    606         line.show() 
    607         return y + 5 
    608  
    609     def drawGroupLabel(self, label, x, y, width): 
    610         t = QGraphicsSimpleTextItem(label, None, self.scene) 
    611         t.setPos(x, y) #(x); t.setY(y) 
    612         t.show() 
    613         return y + t.boundingRect().height() + 1 
    614  
    615     def drawGeneAnnotation(self, x, y, group): 
    616         font = QFont() 
    617  
    618         font.setPixelSize(max(self.CellHeight - 1, 1)) 
    619  
    620         # annotate 
    621         hm = self.heatmaps[0][group] 
    622         if self.BAnnotationVar: 
    623             for (row, indices) in enumerate(hm.exampleIndices[:-1]): 
    624                 t = QGraphicsSimpleTextItem(str(hm.examples[hm.exampleIndices[row]][self.BAnnotationVar]), None, self.scene) 
    625                 t.setFont(font) 
    626                 t.setPos(x, y) 
    627                 t.show() 
    628                 y += self.CellHeight 
    629  
    630     def drawColumnLabels(self, x, y, heatmap): 
    631         font = QFont() 
    632         font.setPixelSize(min(max(self.CellWidth - 1, 1), 11)) 
    633         t = QGraphicsSimpleTextItem("Dummy123", None, self.scene) 
    634         t.setFont(font) 
    635         if t.boundingRect().height() < self.CellWidth: 
    636             x += (self.CellWidth - t.boundingRect().height()) / 2 
    637         self.scene.removeItem(t) 
    638  
    639         maxY = y 
    640         items = [] 
    641         if self.ShowColumnLabels: 
    642             angle = -90 #-90 if self.ColumnLabelPosition == 0 else 90 
    643             for attr in heatmap[0].examples.domain.attributes: 
    644                 t = QGraphicsSimpleTextItem(str(attr.name), None, self.scene) 
    645                 t.setFont(font) 
    646                 t.setPos(x, y) 
    647                 t.show() 
    648                 x += self.CellWidth 
    649                 maxY = max(y + t.boundingRect().width(), maxY) 
    650                 t.rotate(angle) 
    651                 items.append(t) 
    652  
    653         for item in items: 
    654             if self.ColumnLabelPosition == 0: 
    655                 item.setPos(item.x(), maxY) 
     1113             
     1114    def on_selection_changed(self): 
     1115        for item in self.selection_rects: 
     1116            item.hide() 
     1117            item.setParentItem(None) 
     1118            item.update() 
     1119            self.heatmap_scene.removeItem(item) 
     1120        self.selection_rects = [] 
     1121        self.selection_manager.update_selection_rects() 
     1122        rects = self.selection_manager.selection_rects 
     1123        for rect in rects: 
     1124            item = QGraphicsRectItem(rect, None, self.heatmap_scene) 
     1125            item.setPen(QPen(Qt.black, 2)) 
     1126            self.selection_rects.append(item) 
     1127             
     1128    def on_selection_finished(self): 
     1129        self.selected_rows = self.selection_manager.selections 
     1130        self.commit_if() 
     1131         
     1132    def commit_if(self): 
     1133        if self.auto_commit: 
     1134            self.commit() 
     1135        else: 
     1136            self.selection_changed_flag = True 
     1137         
     1138    def commit(self): 
     1139        data = None 
     1140        if self.sorted_data: 
     1141            if self.selected_rows: 
     1142                examples = [self.sorted_data[i] for i in self.selected_rows] 
     1143                data = orange.ExampleTable(examples) 
    6561144            else: 
    657                 item.setPos(item.x(), item.y() + item.boundingRect().width()) 
    658  
    659         return maxY                 
    660  
    661     def drawHeatMap(self): 
    662         # remove everything from current canvas 
    663         for i in self.scene.items(): 
    664             self.scene.removeItem(i) 
    665         if not len(self.data): 
    666             return 
    667  
    668         lo = self.CutEnabled and self.CutLow   or self.lowerBound 
    669         hi = self.CutEnabled and self.CutHigh  or self.upperBound 
    670  
    671 ##        self.sceneView.heatmapParameters(self, self.CellWidth, self.CellHeight) # needed for event handling 
    672         self.scene.heatmapParameters(self, self.CellWidth, self.CellHeight) # needed for event handling 
    673  
    674 ##        palette = self.ColorPalettes[self.CurrentPalette] 
    675         palette = self.getGammaCorrectedPalette() if self.Gamma !=0 else self.palette 
    676         groups = (not self.data[0].domain.classVar and 1) or \ 
    677                  len(self.data[0].domain.classVar.values) # mercy! (just had to do this) 
    678  
    679         self.bmps = []; self.heights = []; self.widths = []; self.imgStart = []; self.imgEnd = [] 
    680         for (i,hm) in enumerate(self.heatmaps): 
    681             bmpl = [] 
    682             for g in range(groups): 
    683                 bmp, self.imageWidth, imageHeight = hm[g].getBitmap(int(self.CellWidth), \ 
    684                     int(self.CellHeight), lo, hi, 1.0) #self.Gamma) 
    685                 bmpl.append(bmp) 
    686                 if not i: self.heights.append(imageHeight) 
    687             self.bmps.append(bmpl) 
    688             self.widths.append(self.imageWidth) 
    689  
    690         totalHeight = max(reduce(lambda x,y:x+y, self.heights) + 500, 2000) 
    691         self.scene.setSceneRect(0, 0, 2000, totalHeight) # this needs adjustment 
    692         x = c_offsetX; y0 = c_offsetY 
    693  
    694         self.legend = self.heatmapconstructor[0].getLegend(self.imageWidth, c_legendHeight, 1.0) #self.Gamma) 
    695         if self.LegendOnTop: 
    696             y0 = self.drawLegend(x, y0, self.imageWidth, c_legendHeight, palette) 
    697  
    698         self.heatmapPositionsX = [] # start and end positions of heatmaps 
    699         for i in range(len(self.data)): 
    700             y = y0; y1 = y0 
    701             if self.ShowDataFileNames and len(self.data)>1: 
    702                 y1 = self.drawFileName(self.data[i].name, x, y, \ 
    703                     self.imageWidth+self.ShowAverageStripe*(c_averageStripeWidth + c_spaceAverageX)) 
    704             x0 = x                     
    705             # plot the heatmap (and group label) 
    706             showClusters = (i == 0 and self.groupClusters and self.ShowClustering) 
    707             ycoord = [] 
    708             y = y1; x += self.ShowAverageStripe * (c_averageStripeWidth + c_spaceAverageX) 
    709              
    710             if self.attrCluster and self.ShowClustering: 
    711                 item = HierarchicalClusterItem.create(self.attrCluster, None, self.scene) 
    712                 item.setSize(self.widths[i], 100.0) 
    713                 item.scale(1.0, -1.0) 
    714                 item.setPos(x + self.CellWidth/2.0, y + 100.0) 
    715                 y += 100.0 + c_spaceY 
    716             if self.ColumnLabelPosition == 0: 
    717                 y = self.drawColumnLabels(x, y, self.heatmaps[i]) + 2  
    718                  
    719             self.heatmapPositionsX.append((x, x + self.widths[i]-1)) 
    720             for g in range(groups): 
    721               if self.heights[g]: 
    722                 if self.ShowGroupLabel and groups>1: 
    723                     y = self.drawGroupLabel(self.data[i][0].domain.classVar.values[g], x, y, self.imageWidth)           
    724                 if not i: self.imgStart.append(y) 
    725                 ycoord.append(y) 
    726                 if showClusters: 
    727                     item = HierarchicalClusterItem.create(self.groupClusters[g], None, self.scene) 
    728                     item.setSize(self.heights[g], 100.0) 
    729 #                    item.setTransform(QTransform().scale(100.0/item.rect().height(), self.heights[g]/float(len(item.cluster))).\ 
    730 #                                    rotate(90).translate(0, -item.rect().height())) 
    731                     item.rotate(90) 
    732 #                    item.setPos(-100, y+self.CellHeight/2.0) 
    733                     item.setPos(0, y+self.CellHeight/2.0/self.Merge) 
    734                     item.update() 
    735  
    736                 image = ImageItem(self.bmps[i][g], self.scene, self.imageWidth, \ 
    737                                   self.heights[g], palette, x=x, y=y, z=z_heatmap) 
    738                 image.hm = self.heatmaps[i][g] # needed for event handling 
    739                 image.height = self.heights[g]; image.width = self.imageWidth 
    740                 if not i: self.imgEnd.append(y+self.heights[g]-1) 
    741                 y += self.heights[g] + c_spaceY 
    742              
    743             if self.ColumnLabelPosition == 1: 
    744                 self.drawColumnLabels(x, y - c_spaceY + 2, self.heatmaps[i]) 
    745             x = x0 
    746             # plot stripe with averages 
    747             if self.ShowAverageStripe: 
    748                 for g in range(groups): 
    749                     avg, avgWidth, avgHeight = self.heatmaps[i][g].getAverages(c_averageStripeWidth, \ 
    750                         int(self.CellHeight), lo, hi, 1.0) # self.Gamma) 
    751                     ImageItem(avg, self.scene, avgWidth, avgHeight, palette, x=x, y=ycoord[g]) 
    752             x += self.imageWidth + self.SpaceX + self.ShowAverageStripe * \ 
    753                 (c_averageStripeWidth + c_spaceAverageX) 
    754  
    755         # plot the gene annotation 
    756         for g in range(groups): 
    757             if self.ShowGeneAnnotations and self.CellHeight>4: 
    758                 self.drawGeneAnnotation(x, ycoord[g], g) 
    759  
    760         self.selection.redraw() 
    761         self.scene.setSceneRect(self.scene.itemsBoundingRect().adjusted(-c_offsetX, -c_offsetY, c_offsetX, c_offsetY)) 
    762         self.scene.currentHighlightedCluster = None 
    763         self.scene.update() 
    764          
    765     def createHeatMap(self): 
    766         if len(self.data): 
    767             merge = min(self.Merge, float(len(self.data[0]))) 
    768             squeeze = 1. / merge 
    769             self.lowerBound = 1000; self.upperBound = -1000 # CHANGE!!! 
    770             self.heatmaps = [] 
    771             for (i, hmc) in enumerate(self.heatmapconstructor): 
    772                 hm, lb, ub = hmc(squeeze) 
    773                 self.heatmaps.append(hm) 
    774                 self.lowerBound = min(self.lowerBound, lb) 
    775                 self.upperBound = max(self.upperBound, ub) 
    776  
    777             self.sliderCutLow.setRange(self.lowerBound, 0, 0.1) 
    778             self.sliderCutHigh.setRange(1e-10, self.upperBound, 0.1) 
    779             self.CutLow = max(self.CutLow, self.lowerBound) 
    780             self.CutHigh = min(self.CutHigh, self.upperBound) 
    781             self.sliderCutLow.setValue(self.CutLow) 
    782             self.sliderCutHigh.setValue(self.CutHigh) 
    783             self.selection.remove() 
    784         self.drawHeatMap() 
    785  
    786     ########################################################################## 
    787     # file list management 
    788  
    789     # rel = -1 for up, or 1 for down 
    790     def fileOrderChange(self, rel): 
    791         sel = self.selectedFile 
    792         data = self.data 
    793         data[sel], data[sel+rel] = (data[sel+rel], data[sel]) 
    794         if sel == self.refFile: 
    795             self.refFile += rel 
    796         elif sel + rel == self.refFile: 
    797             self.refFile += -rel 
    798         # i got lazy here 
    799         self.fileLB.clear() 
    800         for i in range(len(data)): 
    801             self.fileLB.addItem(self.createListItem(data[i].name, i)) 
    802         self.fileLB.setSelected(sel + rel, 1) 
    803         self.constructHeatmap() 
    804  
    805     def setFileReferenceBySelection(self, sel): 
    806         self.fileSelectionChanged(sel) 
    807         self.setFileReference() 
    808          
    809     def setFileReference(self): 
    810         sel = self.selectedFile 
    811         self.fileLB.changeItem(self.createListItem(self.data[self.refFile].name, -1), self.refFile) 
    812         self.refFile = sel 
    813         self.fileLB.changeItem(self.createListItem(self.data[sel].name, sel), sel) 
    814         self.constructHeatmap() 
    815  
    816     def fileSelectionChanged(self, sel): 
    817         # self.fileRef.setDisabled(sel==0) 
    818         self.selectedFile = sel 
    819         self.fileDown.setEnabled(sel < len(self.data)-1) 
    820         self.fileUp.setEnabled(sel>0) 
    821         self.fileRef.setEnabled(sel <> self.refFile) 
    822  
    823     def createListItem(self, text, position): 
    824         pixmap = QPixmap(14, 13) 
    825         pixmap.fill(Qt.white) 
    826          
    827         if position == self.refFile: 
    828             painter = QPainter() 
    829             painter.begin(pixmap) 
    830             painter.setPen(Qt.black) 
    831             painter.setBrush(Qt.black) 
    832             painter.drawRect(3, 3, 8, 8) 
    833             painter.end() 
    834              
    835         listItem = QListWidgetItem(QIcon(pixmap), text) #QListBoxPixmap(pixmap) 
    836 ##        listItem.setText(text) 
    837         return listItem 
    838  
    839     # remove gene selection (when changing some of the options) 
    840     def removeSelection(self): 
    841         if self.selection: 
    842             self.selection.remove() 
    843             self.scene.update() 
    844              
    845     ## handle selections 
     1145                data = None 
     1146         
     1147        self.send("Examples", data) 
     1148        self.selection_changed_flag 
     1149             
     1150    ## handle saved selections  
    8461151    def settingsFromWidgetCallbackSelection(self, handler, context): 
    847         context.selection = self.selection.indxs, self.selection.startIndx, self.selection.rows 
    848         context.selectedRows = list(self.selection.rows) 
     1152        context.selection = self.selection_manager.selections 
    8491153 
    8501154    def settingsToWidgetCallbackSelection(self, handler, context): 
     
    8521156        if selection: 
    8531157            try: 
    854                 self.selection.setSelection(*selection) 
     1158                self.selection_manager.select_rows(selection) 
    8551159            except Exception, ex: 
    856                 sys.excepthook(*sys.exc_info()) 
    857          
    858  
    859 ################################################################################################## 
    860 # new canvas items 
    861  
    862 class ImageItem(QGraphicsPixmapItem): 
    863     def __init__(self, bitmap, scene, width, height, palette, depth=8, numColors=256, x=0, y=0, z=0): 
    864         image = QImage(bitmap, width, height, QImage.Format_Indexed8) 
    865         image.bitmap = bitmap # this is tricky: bitmap should not be freed, else we get mess. hence, we store it in the object 
     1160                pass 
     1161#                self.warning(3, "Could not restore selection") 
     1162                 
     1163    def split_changed(self): 
     1164        if self.data: 
     1165            self.clear_scene() 
     1166            self.construct_heatmaps(self.data, self.selected_split_label()) 
     1167            self.create_heatmaps(self.heatmapconstructor) 
     1168            self.construct_heatmaps_scene(self.heatmaps, self.data, 
     1169                                          attr_cluster=self.attr_cluster, 
     1170                                          data_clusters=self.data_clusters) 
     1171         
     1172 
     1173class GraphicsPixmapLayoutItem(QGraphicsLayoutItem): 
     1174    """ A layout item wraping a QGraphicsPixmapItem 
     1175    """ 
     1176    def __init__(self, pixmap_item, parent=None): 
     1177        QGraphicsLayoutItem.__init__(self, parent) 
     1178        self.pixmap_item = pixmap_item 
     1179        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 
     1180         
     1181    def setGeometry(self, rect): 
     1182        QGraphicsLayoutItem.setGeometry(self, rect) 
     1183        self.pixmap_item.setPos(rect.topLeft()) 
     1184         
     1185    def sizeHint(self, which, constraint=QSizeF()): 
     1186        return QSizeF(self.pixmap_item.pixmap().size()) 
     1187     
     1188    def setPixmap(self, pixmap): 
     1189        self.pixmap_item.setPixmap(pixmap) 
     1190        self.updateGeometry() 
     1191         
     1192         
     1193class GraphicsHeatmapWidget(QGraphicsWidget): 
     1194    def __init__(self, heatmap=None, parent=None, scene=None): 
     1195        QGraphicsWidget.__init__(self, parent) 
     1196        self.setAcceptHoverEvents(True) 
     1197        layout = QGraphicsLinearLayout(Qt.Horizontal) 
     1198        layout.setContentsMargins(0, 0, 0, 0) 
     1199        item = QGraphicsPixmapItem(self) 
     1200        item.setShapeMode(QGraphicsPixmapItem.BoundingRectShape) 
     1201        self.heatmap_item = GraphicsPixmapLayoutItem(item, self) 
     1202         
     1203        item = QGraphicsPixmapItem(self) 
     1204        item.setShapeMode(QGraphicsPixmapItem.BoundingRectShape) 
     1205        self.averages_item = GraphicsPixmapLayoutItem(item, self) 
     1206         
     1207        layout.addItem(self.averages_item) 
     1208        layout.addItem(self.heatmap_item) 
     1209        layout.setItemSpacing(0, 2) 
     1210         
     1211        self.setLayout(layout) 
     1212         
     1213        self.heatmap = None 
     1214        self.show_averages = True 
     1215        self._pixmap_args = None 
     1216        self.color_table = None 
     1217        self.selection_manager = None 
     1218        self.set_cell_size(4, 4) 
     1219        self.set_cuts(0, 255) 
     1220        self.set_heatmap(heatmap) 
     1221         
     1222        if scene is not None: 
     1223            scene.addItem(self) 
     1224             
     1225    def clear(self): 
     1226        """ Clear the current heatmap. 
     1227        """ 
     1228        self.heatmap = None 
     1229        self._pixmap_args = None 
     1230        self.heatmap_item.setPixmap(QPixmap()) 
     1231        self.averages_item.setPixmap(QPixmap()) 
     1232        self.show_averages = True 
     1233        self.layout().invalidate() 
     1234             
     1235    def set_heatmap(self, heatmap): 
     1236        """ Set the heatmap for display. 
     1237        """ 
     1238        self.clear() 
     1239        self.heatmap = heatmap 
     1240        self.update() 
     1241         
     1242    def set_cell_size(self, width, height): 
     1243        self.cell_width = width 
     1244        self.cell_height = height 
     1245        self.update() 
     1246         
     1247    def set_cuts(self, low, high): 
     1248        self.cut_low = low 
     1249        self.cut_high = high 
     1250        self.update() 
     1251         
     1252    def set_show_averages(self, show): 
     1253        self.show_averages = show 
     1254        self._pixmap_args = None 
     1255        self.update() 
     1256 
     1257    def set_color_table(self, color_table): 
    8661258        if qVersion() <= "4.5": 
    867             image.setColorTable(signedPalette(palette)) 
    868         else: 
    869             image.setColorTable(palette) 
    870         pixmap = QPixmap.fromImage(image) 
    871         QGraphicsPixmapItem.__init__(self, pixmap, None, scene) 
    872         self.setPos(x, y) 
    873         self.setZValue(z) 
    874          
    875 # ################################################################################################ 
    876 # mouse event handler 
    877  
    878 v_sel_width = 2 
    879  
    880 class HeatMapGraphicsScene(QGraphicsScene): 
    881     def __init__(self, *args): 
    882         QGraphicsScene.__init__(self, *args) 
    883         self.clicked = False 
    884         self.shiftPressed = False 
    885         self.currentHighlightedCluster = None 
    886         self.selectedClusters = [] 
    887  
    888     def heatmapParameters(self, master, cellWidth, cellHeight): 
    889         self.master = master 
    890         self.dx, self.dy = cellWidth, cellHeight 
    891         self.selector = QGraphicsRectItem(0, 0, \ 
    892             self.dx + 2 * v_sel_width - 1, self.dy + 2 * v_sel_width - 1, None, self) 
    893         self.selector.setPen(QPen(self.master.SelectionColors[self.master.CurrentPalette], v_sel_width)) 
    894         self.selector.setZValue(10) 
    895  
     1259            self.color_table = signedPalette(color_table) 
     1260        else: 
     1261            self.color_table = color_table 
     1262         
     1263        self._pixmap_args = None 
     1264        self.update() 
     1265             
     1266    def _update_pixmap(self): 
     1267        """ Update the pixmap if its construction arguments changed. 
     1268        """ 
     1269        if self.heatmap: 
     1270            args = (int(self.cell_width), int(self.cell_height), 
     1271                    self.cut_low, self.cut_high, 1.0) 
     1272             
     1273            if args != self._pixmap_args: 
     1274                bitmap, width, height = self.heatmap.getBitmap(*args) 
     1275                image = QImage(bitmap, width, height, QImage.Format_Indexed8) 
     1276                color_table = self.color_table 
     1277                if color_table: 
     1278                    image.setColorTable(color_table) 
     1279                self.pixmap = QPixmap.fromImage(image) 
     1280                 
     1281                bitmap, width, height = self.heatmap.getAverages(*((c_averageStripeWidth,) + args[1:])) 
     1282                image = QImage(bitmap, width, height, QImage.Format_Indexed8) 
     1283                if self.color_table: 
     1284                    image.setColorTable(color_table) 
     1285                self.averages_pixmap = QPixmap.fromImage(image) 
     1286                 
     1287                self._pixmap_args = args 
     1288                 
     1289                self.layout().invalidate() 
     1290        else: 
     1291            self.averages_pixmap = None 
     1292            self.pixmap = None 
     1293             
     1294        self.heatmap_item.setPixmap(self.pixmap or QPixmap()) 
     1295        if self.show_averages and self.averages_pixmap: 
     1296            self.averages_item.setPixmap(self.averages_pixmap) 
     1297        else: 
     1298            self.averages_item.setPixmap(QPixmap()) 
     1299             
     1300    def update(self): 
     1301        self._update_pixmap() 
     1302        QGraphicsWidget.update(self) 
     1303         
     1304    def set_selection_manager(self, manager): 
     1305        self.selection_manager = manager 
     1306         
     1307    def cell_at(self, pos): 
     1308        """ Return the cell row, column from a point `pos` in local 
     1309        coordinates. 
     1310         
     1311        """ 
     1312        pos = self.mapToItem(self.heatmap_item.pixmap_item, pos) 
     1313        x, y = pos.x(), pos.y() 
     1314        def clamp(i, m): 
     1315            return int(min(max(i, 0), m)) 
     1316        return (clamp(math.floor(y / self.cell_height), self.heatmap.height), 
     1317                clamp(math.floor(x / self.cell_width), self.heatmap.width)) 
     1318     
     1319    def cell_rect(self, row, column): 
     1320        """ Return a QRectF in local coordinates containing the cell 
     1321        at `row` and `column`. 
     1322         
     1323        """ 
     1324        top = QPointF(column * self.cell_width, row * self.cell_height) 
     1325        top = self.mapFromItem(self.heatmap_item.pixmap_item, top) 
     1326        size = QSizeF(self.cell_width, self.cell_height) 
     1327        return QRectF(top, size) 
     1328 
     1329    def row_rect(self, row): 
     1330        """ Return a QRectF in local coordinates containing the entire row. 
     1331        """ 
     1332        rect = self.cell_rect(row, 0).united(self.cell_rect(row, self.heatmap.width - 1)) 
     1333        rect.setLeft(0) # To include the average stripe if show. 
     1334        return rect 
     1335     
     1336    def cell_tool_tip(self, row, column): 
     1337        hm = self.heatmap 
     1338        start = int(hm.exampleIndices[row]) 
     1339        end = int(hm.exampleIndices[row + 1]) 
     1340        examples = [hm.examples[start]] 
     1341        attr = hm.examples.domain[column] 
     1342        val = "%i, %i: %f" % (row, column, float(examples[0][attr])) 
     1343        return val 
     1344     
     1345    def hoverEnterEvent(self, event): 
     1346        row, col = self.cell_at(event.pos()) 
     1347     
     1348    def hoverMoveEvent(self, event): 
     1349        pos = event.pos() 
     1350        row, column = self.cell_at(pos) 
     1351        tooltip = self.cell_tool_tip(row, column) 
     1352        QToolTip.showText(event.screenPos(), tooltip) 
     1353        return QGraphicsWidget.hoverMoveEvent(self, event) 
     1354     
     1355    def hoverLeaveEvent(self, event): 
     1356        row, col = self.cell_at(event.pos()) 
     1357     
     1358    if DEBUG: 
     1359        def paint(self, painter, option, widget=0): 
     1360            rect =  self.geometry() 
     1361            rect.translate(-self.pos()) 
     1362            painter.drawRect(rect.adjusted(-1, -1, 1, 1)) 
     1363     
     1364     
     1365class GridWidget(QGraphicsWidget): 
     1366    def __init__(self, parent=None): 
     1367        QGraphicsWidget.__init__(self, parent) 
     1368         
     1369    if DEBUG: 
     1370        def paint(self, painter, option, widget=0): 
     1371            rect =  self.geometry() 
     1372            rect.translate(-self.pos()) 
     1373            painter.drawRect(rect) 
     1374             
     1375             
     1376class HeatmapScene(QGraphicsScene): 
     1377    """ A Graphics Scene with heatmap widgets. 
     1378    """ 
     1379    def __init__(self, parent=None): 
     1380        QGraphicsScene.__init__(self, parent) 
     1381        self.selection_manager = HeatmapSelectionManager() 
     1382         
     1383    def set_selection_manager(self, manager): 
     1384        self.selection_manager = manager 
     1385         
     1386    def _items(self, pos=None, cls=object): 
     1387        if pos is not None: 
     1388            items = self.items(QRectF(pos, QSizeF(3, 3)).translated(-1.5, -1.5)) 
     1389        else: 
     1390            items = self.items() 
     1391             
     1392        for item in items: 
     1393            if isinstance(item, cls): 
     1394                yield item 
     1395             
     1396    def heatmap_at_pos(self, pos): 
     1397        items  = list(self._items(pos, GraphicsHeatmapWidget)) 
     1398        if items: 
     1399            return items[0] 
     1400        else: 
     1401            return None 
     1402         
     1403    def dendrogram_at_pos(self, pos): 
     1404        items  = list(self._items(pos, DendrogramItem)) 
     1405        if items: 
     1406            return items[0] 
     1407        else: 
     1408            return None 
     1409         
     1410    def heatmap_widgets(self): 
     1411        return self._items(None, GraphicsHeatmapWidget) 
     1412         
     1413    def select_from_dendrogram(self, dendrogram, clear=True): 
     1414        """ Select all heatmap rows which belong to the dendrogram. 
     1415        """ 
     1416        dendrogram_widget = dendrogram.parentWidget() 
     1417        anchors = list(dendrogram_widget.leaf_anchors()) 
     1418        cluster = dendrogram.cluster 
     1419        start, end = anchors[cluster.first], anchors[cluster.last - 1] 
     1420        start, end = dendrogram_widget.mapToScene(start), dendrogram_widget.mapToScene(end) 
     1421        # Find a heatmap widget containing start and end y coordinates. 
     1422         
     1423        heatmap = None 
     1424        for hm in self.heatmap_widgets(): 
     1425            b_rect = hm.sceneBoundingRect() 
     1426            if b_rect.contains(QPointF(b_rect.center().x(), start.y())): 
     1427                heatmap = hm 
     1428                break 
     1429             
     1430        if dendrogram: 
     1431            b_rect = hm.boundingRect() 
     1432            start, end = hm.mapFromScene(start), hm.mapFromScene(end) 
     1433            start, _ = hm.cell_at(QPointF(b_rect.center().x(), start.y())) 
     1434            end, _ = hm.cell_at(QPointF(b_rect.center().x(), end.y())) 
     1435            self.selection_manager.selection_add(start, end, hm, clear=clear) 
     1436        return 
     1437         
     1438    def mousePressEvent(self, event): 
     1439        pos = event.scenePos() 
     1440        heatmap = self.heatmap_at_pos(pos) 
     1441        if heatmap and event.button() & Qt.LeftButton: 
     1442            row, _ = heatmap.cell_at(heatmap.mapFromScene(pos)) 
     1443            self.selection_manager.selection_start(heatmap, event) 
     1444             
     1445        dendrogram = self.dendrogram_at_pos(pos) 
     1446        if dendrogram and event.button() & Qt.LeftButton: 
     1447            if dendrogram.orientation == Qt.Vertical: 
     1448                self.select_from_dendrogram(dendrogram, clear=not event.modifiers() & Qt.ControlModifier) 
     1449            return  
     1450         
     1451        return QGraphicsScene.mousePressEvent(self, event) 
     1452     
    8961453    def mouseMoveEvent(self, event): 
    897         QGraphicsScene.mouseMoveEvent(self, event) 
    898         # handling of selection 
    899         if self.clicked: 
    900             self.master.selection(self.clicked, (event.scenePos().x(), event.scenePos().y())) 
    901  
    902         item = self.itemAt(event.scenePos()) 
    903         if self.currentHighlightedCluster and self.currentHighlightedCluster != item: 
     1454        pos = event.scenePos() 
     1455        heatmap = self.heatmap_at_pos(pos) 
     1456        if heatmap and event.buttons() & Qt.LeftButton: 
     1457            row, _ = heatmap.cell_at(heatmap.mapFromScene(pos)) 
     1458            self.selection_manager.selection_update(heatmap, event) 
     1459             
     1460        dendrogram = self.dendrogram_at_pos(pos) 
     1461        if dendrogram and dendrogram.orientation == Qt.Horizontal: # Filter mouse move events 
     1462            return 
     1463             
     1464        return QGraphicsScene.mouseMoveEvent(self, event) 
     1465     
     1466    def mouseReleaseEvent(self, event): 
     1467        pos = event.scenePos() 
     1468        heatmap = self.heatmap_at_pos(pos) 
     1469        if heatmap: 
     1470            row, _ = heatmap.cell_at(heatmap.mapFromScene(pos)) 
     1471            self.selection_manager.selection_finish(heatmap, event) 
     1472         
     1473        dendrogram = self.dendrogram_at_pos(pos) 
     1474        if dendrogram and dendrogram.orientation == Qt.Horizontal: # Filter mouse events 
     1475            return 
     1476         
     1477        return QGraphicsScene.mouseReleaseEvent(self, event) 
     1478     
     1479    def mouseDoubleClickEvent(self, event): 
     1480        pos = event.scenePos() 
     1481        dendrogram = self.dendrogram_at_pos(pos) 
     1482        if dendrogram: # Filter mouse events 
     1483            return 
     1484        return QGraphicsScene.mouseDoubleClickEvent(self, event) 
     1485         
     1486         
     1487class GtI(QGraphicsSimpleTextItem): 
     1488    if DEBUG: 
     1489        def paint(self, painter, option, widget =0): 
     1490            QGraphicsSimpleTextItem.paint(self, painter, option, widget) 
     1491            painter.drawRect(self.boundingRect()) 
     1492             
     1493     
     1494class GraphicsSimpleTextLayoutItem(QGraphicsLayoutItem): 
     1495    """ A Graphics layout item wrapping a QGraphicsSimpleTextItem alowing it  
     1496    to be managed by a layout. 
     1497    """ 
     1498    def __init__(self, text_item, orientation=Qt.Horizontal, parent=None): 
     1499        QGraphicsLayoutItem.__init__(self, parent) 
     1500        self.orientation = orientation 
     1501        self.text_item = text_item 
     1502        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 
     1503        if orientation == Qt.Vertical: 
     1504            self.text_item.rotate(-90) 
     1505            self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) 
     1506        else: 
     1507            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) 
     1508         
     1509    def setGeometry(self, rect): 
     1510        QGraphicsLayoutItem.setGeometry(self, rect) 
     1511        if self.orientation == Qt.Horizontal: 
     1512            self.text_item.setPos(rect.topLeft()) 
     1513        else: 
     1514            self.text_item.setPos(rect.bottomLeft()) 
     1515         
     1516    def sizeHint(self, which, constraint=QSizeF()): 
     1517        if which in [Qt.PreferredSize]: 
     1518            size = self.text_item.boundingRect().size() 
     1519            if self.orientation == Qt.Horizontal: 
     1520                return size 
     1521            else: 
     1522                return QSizeF(size.height(), size.width()) 
     1523        else: 
     1524            return QSizeF() 
     1525     
     1526    def setFont(self, font): 
     1527        self.text_item.setFont(font) 
     1528        self.updateGeometry() 
     1529         
     1530    def setText(self, text): 
     1531        self.text_item.setText(text) 
     1532        self.updateGeometry() 
     1533         
     1534         
     1535class GraphicsSimpleTextList(QGraphicsWidget): 
     1536    """ A simple text list widget. 
     1537    """ 
     1538    def __init__(self, labels=[], orientation=Qt.Vertical, parent=None, scene=None): 
     1539        QGraphicsWidget.__init__(self, parent) 
     1540        layout = QGraphicsLinearLayout(orientation) 
     1541        layout.setContentsMargins(0, 0, 0, 0) 
     1542        layout.setSpacing(0) 
     1543        self.setLayout(layout) 
     1544        self.orientation = orientation 
     1545        self.alignment = Qt.AlignCenter 
     1546        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) 
     1547        self.set_labels(labels) 
     1548         
     1549        if scene is not None: 
     1550            scene.addItem(self) 
     1551         
     1552    def clear(self): 
     1553        """ Remove all text items. 
     1554        """ 
     1555        layout = self.layout() 
     1556        for i in reversed(range(layout.count())): 
     1557            item = layout.itemAt(i) 
     1558            item.text_item.setParentItem(None) 
     1559            if self.scene(): 
     1560                self.scene().removeItem(item.text_item) 
     1561            layout.removeAt(i) 
     1562         
     1563        self.label_items = [] 
     1564        self.updateGeometry() 
     1565         
     1566    def set_labels(self, labels): 
     1567        """ Set the text labels to show in the widget. 
     1568        """ 
     1569        self.clear() 
     1570        orientation = Qt.Horizontal if self.orientation == Qt.Vertical else Qt.Vertical 
     1571        for text in labels: 
     1572#            item = QGraphicsSimpleTextItem(text, self) 
     1573            item = GtI(text, self) 
     1574            item.setFont(self.font()) 
     1575            item.setToolTip(text) 
     1576            item = GraphicsSimpleTextLayoutItem(item, orientation, parent=self) 
     1577            self.layout().addItem(item) 
     1578            self.layout().setAlignment(item, self.alignment) 
     1579            self.label_items.append(item) 
     1580             
     1581        self.layout().activate() 
     1582        self.updateGeometry() 
     1583     
     1584    def setAlignment(self, alignment): 
     1585        """ Set alignment of text items in the widget 
     1586        """ 
     1587        self.alignment = alignment 
     1588        layout = self.layout() 
     1589        for i in range(layout.count()): 
     1590            layout.setAlignment(layout.itemAt(i), alignment) 
     1591             
     1592    def setVisible(self, bool): 
     1593        QGraphicsWidget.setVisible(self, bool) 
     1594        self.updateGeometry() 
     1595             
     1596    def setFont(self, font): 
     1597        """ Set the font for the text. 
     1598        """ 
     1599        QGraphicsWidget.setFont(self, font) 
     1600        for item in self.label_items: 
     1601            item.setFont(font) 
     1602        self.layout().invalidate() 
     1603        self.updateGeometry() 
     1604         
     1605    def sizeHint(self, which, constraint=QRectF()): 
     1606        if not self.isVisible(): 
     1607            return QSizeF(0, 0) 
     1608        else: 
     1609            return QGraphicsWidget.sizeHint(self, which, constraint) 
     1610             
     1611    if DEBUG: 
     1612        def paint(self, painter, options, widget=0): 
     1613            rect =  self.geometry() 
     1614            rect.translate(-self.pos()) 
     1615            painter.drawRect(rect) 
     1616         
     1617class GraphicsLegendWidget(QGraphicsWidget): 
     1618    def __init__(self, heatmap_constructor, low, high, parent=None, scene=None): 
     1619        QGraphicsWidget.__init__(self, parent) 
     1620        layout = QGraphicsLinearLayout(Qt.Vertical) 
     1621        self.setLayout(layout) 
     1622        layout.setContentsMargins(0, 0, 0, 0) 
     1623        layout.setSpacing(1) 
     1624         
     1625        layout_labels = QGraphicsLinearLayout(Qt.Horizontal) 
     1626        layout.addItem(layout_labels) 
     1627        layout_labels.setContentsMargins(0, 0, 0, 0) 
     1628        label_lo = GtI("%.2f" % low, self) 
     1629        label_hi = GtI("%.2f" % high, self) 
     1630        self.item_low = GraphicsSimpleTextLayoutItem(label_lo, parent=self) 
     1631        self.item_high = GraphicsSimpleTextLayoutItem(label_hi, parent=self) 
     1632         
     1633        layout_labels.addItem(self.item_low) 
     1634        layout.addStretch() 
     1635        layout_labels.addItem(self.item_high) 
     1636         
     1637        self._pixmap = QPixmap(c_legendHeight, c_legendHeight) 
     1638        self.pixmap_item = QGraphicsPixmapItem(self._pixmap, self) 
     1639        self.pixmap_item = GraphicsPixmapLayoutItem(self.pixmap_item, parent=self) 
     1640        layout.addItem(self.pixmap_item) 
     1641         
     1642        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) 
     1643        self.set_legend(heatmap_constructor, low, high) 
     1644         
     1645        if scene is not None: 
     1646            scene.addItem(self) 
     1647         
     1648    def set_legend(self, heatmap_constructor, low, high): 
     1649        self.heatmap_constructor = heatmap_constructor 
     1650        self.low = low 
     1651        self.high = high 
     1652        self.color_table = None 
     1653        self._pixmap = None 
     1654        self._pixmap_args = None 
     1655        self.update() 
     1656        self.updateGeometry() 
     1657         
     1658    def set_color_table(self, color_table): 
     1659        if qVersion() <= "4.5": 
     1660            self.color_table = signedPalette(color_table) 
     1661        else: 
     1662            self.color_table = color_table 
     1663         
     1664        self._pixmap_args = None 
     1665        self.update() 
     1666         
     1667    def update(self): 
     1668        crect = self.contentsRect() 
     1669        width = crect.width() 
     1670        height = c_legendHeight 
     1671        if not self.pixmap_item or self._pixmap_args != (width, height): 
     1672            bitmap = self.heatmap_constructor.getLegend(int(width), int(height), 1.0) 
     1673            image = QImage(bitmap, width, height, QImage.Format_Indexed8) 
     1674            color_table = self.color_table 
     1675            if color_table: 
     1676                image.setColorTable(color_table) 
     1677            self._pixmap = QPixmap.fromImage(image) 
     1678             
     1679        self.pixmap_item.setPixmap(self._pixmap) 
     1680        self.item_low.setText("%.2f" % self.low) 
     1681        self.item_high.setText("%.2f" % self.high) 
     1682        self.layout().activate() 
     1683        QGraphicsWidget.update(self) 
     1684             
     1685    if DEBUG: 
     1686        def paint(self, painter, options, widget=0): 
     1687            rect =  self.geometry() 
     1688            rect.translate(-self.pos()) 
     1689            painter.drawRect(rect) 
     1690 
     1691         
     1692class HeatmapSelectionManager(QObject): 
     1693    """ Selection manager for heatmap rows 
     1694    """ 
     1695    def __init__(self, parent=None): 
     1696        QObject.__init__(self, parent) 
     1697        self.selections = [] 
     1698        self.selection_ranges = [] 
     1699        self.heatmap_widgets = [] 
     1700        self.selection_rects = [] 
     1701        self.heatmaps = [] 
     1702        self._heatmap_ranges = {} 
     1703        self._start_row = 0 
     1704         
     1705    def set_heatmap_widgets(self, widgets): 
     1706        self.remove_rows(self.selections) 
     1707        self.heatmaps = widgets 
     1708         
     1709        # Compute row ranges for all heatmaps 
     1710        self._heatmap_ranges = {} 
     1711        for group in widgets: 
     1712            start = end = 0 
     1713            for heatmap in group: 
     1714                end += heatmap.heatmap.height 
     1715                self._heatmap_ranges[heatmap] = (start, end) 
     1716                start = end 
     1717         
     1718    def select_rows(self, rows, heatmap=None, clear=True): 
     1719        """ Add `rows` to selection. If `heatmap` is provided the rows 
     1720        are mapped from the local indices to global heatmap indics. If `clear` 
     1721        then remove previous rows. 
     1722        """ 
     1723        if heatmap is not None: 
     1724            start, end = self._heatmap_ranges[heatmap] 
     1725            rows = [start + r for r in rows] 
     1726             
     1727        old_selection = list(self.selections) 
     1728        if clear: 
     1729            self.selections = rows 
     1730        else: 
     1731            self.selections = sorted(set(self.selections + rows)) 
     1732        if self.selections != old_selection: 
     1733            self.update_selection_rects() 
     1734            self.emit(SIGNAL("selection_changed()")) 
     1735            self.emit(SIGNAL("selection_rects_changed"), self.selection_rects) 
     1736             
     1737    def remove_rows(self, rows): 
     1738        """ Remove `rows` from the selection. 
     1739        """ 
     1740        old_selection = list(self.selections) 
     1741        self.selections = sorted(set(self.selections) - set(rows)) 
     1742        if old_selection != self.selections: 
     1743            self.update_selection_rects() 
     1744            self.emit(SIGNAL("selection_changed()")) 
     1745            self.emit(SIGNAL("selection_rects_changed"), self.selection_rects) 
     1746             
     1747    def combined_ranges(self, ranges): 
     1748        combined_ranges = set() 
     1749        for start, end in ranges: 
     1750            if start <= end: 
     1751                rng = range(start, end + 1) 
     1752            else: 
     1753                rng = range(start, end - 1, -1) 
     1754            combined_ranges.update(rng) 
     1755        return sorted(combined_ranges) 
     1756         
     1757    def selection_start(self, heatmap_widget, event): 
     1758        """ Selection  started by `heatmap_widget` due to `event`. 
     1759        """ 
     1760        pos = heatmap_widget.mapFromScene(event.scenePos()) 
     1761        row, column = heatmap_widget.cell_at(pos) 
     1762        start, _ = self._heatmap_ranges[heatmap_widget] 
     1763        row = start + row 
     1764        self._start_row = row 
     1765        range = (row, row) 
     1766        if event.modifiers() & Qt.ControlModifier: 
     1767            self.selection_ranges.append(range) 
     1768        else: 
     1769            self.selection_ranges = [range] 
     1770        self.select_rows(self.combined_ranges(self.selection_ranges)) 
     1771         
     1772    def selection_update(self, heatmap_widget, event): 
     1773        """ Selection updated by `heatmap_widget due to `event` (mouse drag). 
     1774        """ 
     1775        pos = heatmap_widget.mapFromScene(event.scenePos()) 
     1776        row, column = heatmap_widget.cell_at(pos) 
     1777        start, _ = self._heatmap_ranges[heatmap_widget] 
     1778        row = start + row 
     1779        if self.selection_ranges: 
     1780            self.selection_ranges[-1] = (self._start_row, row) 
     1781        else: 
     1782            self.selection_ranges = [(row, row)] 
     1783             
     1784        self.select_rows(self.combined_ranges(self.selection_ranges)) 
     1785         
     1786    def selection_finish(self, heatmap_widget, event): 
     1787        """ Selection finished by `heatmap_widget due to `event`. 
     1788        """ 
     1789        pos = heatmap_widget.mapFromScene(event.scenePos()) 
     1790        row, column = heatmap_widget.cell_at(pos) 
     1791        start, _ = self._heatmap_ranges[heatmap_widget] 
     1792        row = start + row 
     1793        range = (self._start_row, row) 
     1794        self.selection_ranges[-1] = range 
     1795        self.select_rows(self.combined_ranges(self.selection_ranges), 
     1796                         clear=not event.modifiers() & Qt.ControlModifier) 
     1797        self.emit(SIGNAL("selection_finished()")) 
     1798         
     1799    def selection_add(self, start, end, heatmap=None, clear=True): 
     1800        """ Add a selection range from `start` to `end`. 
     1801        """  
     1802        if heatmap is not None: 
     1803            _start, _ = self._heatmap_ranges[heatmap] 
     1804            start = _start + start 
     1805            end = _start + end 
     1806         
     1807        if clear: 
     1808            self.selection_ranges = [] 
     1809        self.selection_ranges.append((start, end)) 
     1810        self.select_rows(self.combined_ranges(self.selection_ranges)) 
     1811        self.emit(SIGNAL("selection_finished()")) 
     1812         
     1813    def update_selection_rects(self): 
     1814        """ Update the selection rects. 
     1815        """ 
     1816        def continuous_ranges(selections): 
     1817            """ Group continuous ranges 
     1818            """ 
     1819            selections = iter(selections) 
     1820            start = end = selections.next() 
    9041821            try: 
    905                 self.currentHighlightedCluster.setHighlight(False) 
    906             except RuntimeError: ## Underlying object deleted. Why? 
    907                 self.currentHighlightedCluster = None 
    908          
    909         if isinstance(item, HierarchicalClusterItem): 
    910             root = item 
    911             while root.parentItem(): 
    912                 root = root.parentItem() 
    913 #            print item, root, self.master.attrCluster 
    914             if root.cluster != self.master.attrCluster: 
    915                 item.setHighlight(True) 
    916                 self.currentHighlightedCluster = item 
    917         items = filter(lambda ci: ci.zValue()==z_heatmap, self.items(event.scenePos())) 
    918         if len(items) == 0 and hasattr(self, "selector"): # mouse over nothing special 
    919             self.selector.hide() 
    920             self.update() 
    921         elif items: 
    922             item = items[0] 
    923             hm = item.hm 
    924             x, y = event.scenePos().x() - item.x(), event.scenePos().y() - item.y() 
    925             if x<0 or y<0 or x>item.width-1 or y>item.height-1:  
    926                 self.selector.hide() 
    927                 return 
    928             col, row = int(x / self.dx), int(y / self.dy) 
    929             # hm.getCellIntensity(row, col), hm.getRowIntensity(row) 
    930             ex = hm.examples[hm.exampleIndices[row] : hm.exampleIndices[row+1]] 
    931             bb = self.selector.sceneBoundingRect() 
    932             self.selector.setPos(item.x()+col*self.dx-v_sel_width+1, item.y()+row*self.dy-v_sel_width+1) 
    933             self.selector.show() 
    934             self.update(bb) 
    935             # balloon handling 
     1822                while True: 
     1823                    new_end = selections.next() 
     1824                    if new_end > end + 1: 
     1825                        yield start, end 
     1826                        start = end = new_end 
     1827                    else: 
     1828                        end = new_end 
     1829            except StopIteration: 
     1830                yield start, end 
     1831                 
     1832        def group_selections(selections): 
     1833            """ Group selections along with heatmaps. 
     1834            """ 
     1835            rows2hm = self.rows_to_heatmaps() 
     1836            selections = iter(selections) 
     1837            start = end = selections.next() 
     1838            end_heatmaps= rows2hm[end] 
    9361839            try: 
    937                 if self.master <> None and not self.master.BShowballoon: return 
    938             except: 
    939                 return 
    940  
    941             # bubble, construct head 
    942             if hm.getCellIntensity(row, col)!=None: 
    943                 head = "%6.4f" % hm.getCellIntensity(row, col) 
    944             else: 
    945                 head = "Missing Data" 
    946             if self.master.BShowColumnID: 
    947                 head += "\n"+ex[0].domain.attributes[col].name 
    948             # bubble, construct body 
    949             body = "" 
    950             if (self.master.BShowSpotIndex and self.master.BSpotVar) or \ 
    951                     self.master.BShowAnnotation or self.master.BShowGeneExpression: 
    952                 for (i, e) in enumerate(ex): 
    953                     if i>5: 
    954                         body += "\n... (%d more)" % (len(ex)-5) 
    955                         break 
     1840                while True: 
     1841                    new_end = selections.next() 
     1842                    new_end_heatmaps = rows2hm[new_end] 
     1843                    if new_end > end + 1 or new_end_heatmaps != end_heatmaps: 
     1844                        yield start, end, end_heatmaps 
     1845                        start = end = new_end 
     1846                        end_heatmaps = new_end_heatmaps 
    9561847                    else: 
    957                         s = [] 
    958                         if self.master.BShowSpotIndex and self.master.BSpotVar: 
    959                             s.append(str(e[self.master.BSpotVar])) 
    960                         if self.master.BShowGeneExpression: 
    961                             s.append(str(e[col])) 
    962                         if self.master.BShowAnnotation and self.master.BAnnotationVar: 
    963                             s.append(str(e[self.master.BAnnotationVar])) 
    964                     if body: body += "\n" 
    965                     else: body="" 
    966                     body += reduce(lambda x,y: x + ' | ' + y, s, "") 
    967  
    968             QToolTip.showText(QPoint(event.screenPos().x(), event.screenPos().y()), "") 
    969             QToolTip.showText(QPoint(event.screenPos().x(), event.screenPos().y()), head + body) 
    970  
    971     def keyPressEvent(self, e): 
    972         if e.key() == 4128: 
    973             self.shiftPressed = True 
    974         else: 
    975             QGraphicsScene.keyPressEvent(self, e) 
    976  
    977     def keyReleaseEvent(self, e):         
    978         if e.key() == 4128: 
    979             self.shiftPressed = False 
    980         else: 
    981             QGraphicsScene.keyReleaseEvent(self, e) 
    982  
    983     def mousePressEvent(self, event): 
    984         # self.viewport().setMouseTracking(False) 
    985         item = self.itemAt(event.scenePos()) 
    986         if isinstance(item ,HierarchicalClusterItem): 
    987             leaves = list(item) 
    988             first, last = leaves[0], leaves[-1] 
    989             first_x, first_y = first.mapToScene(first.rect().topLeft()).x(), first.mapToScene(first.rect().topLeft()).y() 
    990             last_x, last_y = last.mapToScene(last.rect().topLeft()).x(), last.mapToScene(last.rect().topLeft()).y() 
    991             self.master.selection.start((first_x, first_y), (first_x, first_y), self.shiftPressed) 
    992             self.master.selection((first_x, first_y), (last_x, last_y)) 
    993             self.master.selection.release() 
    994             return 
    995         self.clicked = (event.scenePos().x(), event.scenePos().y()) 
    996         if not self.master.selection.start(self.clicked, self.clicked, self.shiftPressed): 
    997             self.clicked = None 
    998  
    999     def mouseReleaseEvent(self, event): 
    1000         if self.clicked: 
    1001             self.clicked = False 
    1002             self.update() 
    1003             self.master.selection.release() 
    1004              
    1005  
    1006 class MyGraphicsView(QGraphicsView): 
    1007     def __init__(self, *args): 
    1008         QGraphicsView.__init__(self, *args) 
    1009         self.clicked = False 
    1010         self.viewport().setMouseTracking(True) 
    1011         self.setFocusPolicy(Qt.WheelFocus) 
    1012         self.shiftPressed = False 
    1013  
    1014     def keyPressEvent(self, e): 
    1015         if e.key() == Qt.Key_Shift: 
    1016             self.scene().shiftPressed = True 
    1017         else: 
    1018             QGraphicsView.keyPressEvent(self, e) 
    1019  
    1020     def keyReleaseEvent(self, e):         
    1021         if e.key() == Qt.Key_Shift: 
    1022             self.scene().shiftPressed = False 
    1023         else: 
    1024             QGraphicsView.keyReleaseEvent(self, e) 
    1025  
    1026 # ################################################################################################ 
    1027 # data selection 
    1028  
    1029 def interval_position(x, l, forced=None): 
    1030     if forced: 
    1031         for (i,(min,max)) in enumerate(l): 
    1032             if x>=min and x<=max: 
    1033                 return i 
    1034         return None 
    1035     else: 
    1036         for (i,(min,max)) in enumerate(l): 
    1037             if x>=min and x<=max: 
    1038                 return i 
    1039             if x<min: 
    1040                 if i>forced: 
    1041                     if i-1>0: return i-1 
    1042                     return 0 
    1043                 else: 
    1044                     return i 
    1045         return i 
    1046  
    1047 class SelectData(object): 
    1048     def __init__(self, master, scene): 
    1049         self.scene = scene 
    1050         self.master = master 
    1051         self.add = False 
    1052         self.squares = []; self.rows = []    # cumulative, used for multiple selection 
    1053         self.cRows = []; self.cSquares = []  # workaround for when release(self) is called 
    1054                         # before any clicking at all has occured (maybe caused by a Qt bug?) 
    1055         self.startIndx = None; self.indxs = [] 
    1056  
    1057     # removes the selection and relate information 
    1058     def remove(self): 
    1059         for r in self.squares: 
    1060             self.scene.removeItem(r) 
    1061         self.squares = []; self.rows = [] 
    1062  
    1063     # starts the selection, called after the first click 
    1064     def start(self, p1, p2, add=False): 
    1065         indx = interval_position(p1[0], self.master.heatmapPositionsX) 
    1066         if indx == None: 
    1067             self.remove() 
    1068             self.indx = []; self.startIndx = None 
    1069             return False 
    1070          
    1071         if not add: 
    1072             self.remove() 
    1073             self.startIndx = indx 
    1074             self.indxs = [indx] 
    1075         else: 
    1076             if self.master.SelectionType==0: 
    1077                 if self.startIndx<>None and self.startIndx<>indx: 
    1078                     self.remove() 
    1079                     self.indx = [indx] 
    1080                     self.startIndx = indx                     
    1081             else: 
    1082                 self.startIndx = indx 
    1083                 if indx not in self.indxs: 
    1084                     self.indxs.append(indx) 
    1085  
    1086         self.cSquares = []; self.cRows = [] # current selection 
    1087         self.add = add 
    1088         self.__call__(p1, p2) 
    1089         # determines which microarray was clicked 
    1090         return True 
    1091   
    1092     # called during dragging (extending the selection) 
    1093     def __call__(self, p1, p2): 
    1094         for r in self.cSquares: 
    1095             self.scene.removeItem(r) 
    1096         y1 = min(p1[1], p2[1]); y2 = max(p1[1], p2[1]) 
    1097         if self.master.SelectionType == 1: 
    1098             indx = interval_position(p2[0], self.master.heatmapPositionsX) 
    1099             if indx==None: 
    1100                 indx = interval_position(p2[0], self.master.heatmapPositionsX, forced=self.startIndx) 
    1101             irange = range(min(indx, self.startIndx), max(indx, self.startIndx)+1) 
    1102             for i in irange: 
    1103                 if indx not in self.indxs: 
    1104                     self.indxs.append(indx) 
    1105  
    1106         self.cRows = self.findSelectionRows([(y1,y2)]) 
    1107         self.cSquares = self.draw(self.cRows) 
    1108  
    1109     # merges two selection lists, account for any overlaping or directly neighboring selections 
    1110     def mergeRows(self, pl1, pl2): 
    1111         new = [] 
    1112         l = {} 
    1113         for (g, p1, p2) in pl1+pl2: 
    1114             if l.has_key((g,p1)): l[(g,p1)] += 1 
    1115             else: l[(g,p1)] = 1 
    1116             if l.has_key((g,p2)): l[(g,p2)] += -1 
    1117             else: l[(g,p2)] = -1 
    1118         kk = l.keys() 
    1119         kk.sort() 
    1120         current = 0 # sum of ups and downs, if positive then selection, else, empty 
    1121         intermediate = FALSE; # true if previous end was only one point before, indicates join 
    1122         for k in kk: 
    1123             if current == 0 and not intermediate: 
    1124                 start = k 
    1125             current += l[k] 
    1126             if current == 0: 
    1127                 if l.has_key((k[0],k[1]+1)): 
    1128                     intermediate = TRUE 
    1129                 else: 
    1130                     intermediate = FALSE 
    1131                     new += [(start[0], start[1], k[1])] 
    1132         return new 
    1133  
    1134     # mouse release, end of selection 
    1135     # if shift click, then merge with previous, else only remember current 
    1136     def release(self): 
    1137         if self.add: 
    1138             newrows = self.mergeRows(self.rows, self.cRows) 
    1139             self.remove() 
    1140             for r in self.cSquares: 
    1141                 self.scene.removeItem(r) 
    1142             self.rows = newrows 
    1143             squares = self.draw(self.rows) 
    1144             self.squares = squares 
    1145         else: 
    1146             self.rows = self.cRows 
    1147             self.squares = self.cSquares 
    1148         if self.rows: 
    1149             self.master.sendData(self.rows, self.indxs) 
    1150  
    1151     def findSelectionRows(self, points): 
    1152         start = self.master.imgStart; end = self.master.imgEnd 
    1153         rows = [] 
    1154         for (y1, y2) in points: # this could be optimized, since points are ordered 
    1155             for i in range(len(start)): 
    1156                 if y2<start[i]: 
    1157                     break 
    1158                 if y1<end[i]: 
    1159                     if y1>start[i]: 
    1160                         a = int((y1-start[i])/self.master.CellHeight) 
    1161                     else: 
    1162                         a = 0 
    1163                     if y2<end[i]: 
    1164                         b = int((y2-start[i])/self.master.CellHeight) 
    1165                     else: 
    1166                         b = int((end[i]-start[i])/self.master.CellHeight) 
    1167                     rows.append((i,a,b)) 
    1168         return rows 
    1169  
    1170     # draws rectangles around selected points for a indx-th microarray 
    1171     def drawOne(self, rows, indx): 
    1172         lines = [] 
    1173         start = self.master.imgStart; end = self.master.imgEnd 
    1174         self.master.heatmapPositionsX[indx][0] 
    1175         for (g, r1, r2) in rows: 
    1176             y1 = start[g] + r1 * self.master.CellHeight 
    1177             y2 = start[g] + (r2+1) * self.master.CellHeight - 1 
    1178             r = QGraphicsRectItem(self.master.heatmapPositionsX[indx][0]-v_sel_width+1, y1-v_sel_width+1, \ 
    1179                     self.master.widths[indx]+2*v_sel_width-1, y2-y1+v_sel_width+2, None, self.scene) 
    1180             r.setPen(QPen(self.master.SelectionColors[self.master.CurrentPalette], v_sel_width)) 
    1181             #r.setPen(QPen(Qt.red, v_sel_width)) 
    1182             r.setZValue(10) 
    1183             r.show() 
    1184             lines.append(r) 
    1185         return lines 
    1186              
    1187     # draws rectangles around selected points 
    1188     def draw(self, rows): 
    1189         if self.master.SelectionType==0: # selection on single data set 
    1190             lines = self.drawOne(rows, self.startIndx) 
    1191         else: # selection on multiple data set 
    1192             lines = [] 
    1193             for i in self.indxs: 
    1194                 l = self.drawOne(rows, i) 
    1195                 lines += l 
    1196         self.scene.update() 
    1197         return lines 
    1198  
    1199     def redraw(self): 
    1200         for r in self.squares: 
    1201             self.scene.removeItem(r) 
    1202         if self.rows: 
    1203             self.squares = self.draw(self.rows) 
    1204              
    1205     def setSelection(self, indxs, startIndx, rows): 
    1206         self.indxs = indxs 
    1207         self.startIndx = startIndx 
    1208         self.rows = rows 
    1209         self.redraw() 
    1210  
    1211  
     1848                        end = new_end 
     1849                         
     1850            except StopIteration: 
     1851                yield start, end, end_heatmaps 
     1852                 
     1853        def selection_rect(start, end, heatmaps): 
     1854            rect = QRectF() 
     1855            for heatmap in heatmaps: 
     1856                h_start, _ = self._heatmap_ranges[heatmap]  
     1857                rect |= heatmap.mapToScene(heatmap.row_rect(start - h_start)).boundingRect() 
     1858                rect |= heatmap.mapToScene(heatmap.row_rect(end - h_start)).boundingRect() 
     1859            return rect 
     1860                  
     1861        self.selection_rects = [] 
     1862        for start, end, heatmaps in group_selections(self.selections): 
     1863            rect = selection_rect(start, end, heatmaps) 
     1864            self.selection_rects.append(rect) 
     1865             
     1866    def rows_to_heatmaps(self): 
     1867        heatmap_groups = zip(*self.heatmaps) 
     1868        rows2hm = {} 
     1869        for heatmaps in heatmap_groups: 
     1870            hm = heatmaps[0] 
     1871            start, end = self._heatmap_ranges[hm] 
     1872            rows2hm.update(dict.fromkeys(range(start, end), heatmaps)) 
     1873        return rows2hm 
     1874     
     1875          
    12121876################################################################################################## 
    12131877# test script 
     
    12161880    a=QApplication(sys.argv) 
    12171881    ow = OWHeatMap() 
    1218  
    1219     fn = "/home/marko/pipa4.tab" 
    1220      
    1221     ow.dataset(orange.ExampleTable(fn), 0) 
     1882     
     1883#    fn = "/home/marko/pipa4.tab" 
     1884#    fn = "../../../doc/datasets/brown-selected.tab" 
     1885    fn = os.path.expanduser("~/Documents/abc-subset") 
     1886    ow.set_dataset(orange.ExampleTable(fn), 0) 
    12221887    ow.handleNewSignals() 
    12231888    ow.show() 
    1224  
     1889     
    12251890    a.exec_() 
    1226  
    12271891    ow.saveSettings() 
    1228     """ 
    1229     import orngSignalManager 
    1230     signalManager = orngSignalManager.SignalManager(0) 
    1231     ow=OWHeatMap(signalManager = signalManager) 
    1232     #signalManager.addWidget(ow) 
    1233     #a.setMainWidget(ow) 
    1234     ow.show() 
    1235     import OWDataFiles 
    1236     ds = OWDataFiles.OWDataFiles(signalManager = signalManager, loaddata=0) 
    1237     signalManager.addWidget(ds) 
    1238     ds.loadData("potato.sub100") 
    1239 #    ds.loadData("yakApufA") 
    1240 #    ds.loadData("smallchipdata") 
    1241 #    ds.loadData("testchip") 
    1242     signalManager.setFreeze(1) 
    1243     signalManager.addLink(ds, ow, 'Structured Data', 'Structured Data', 1) 
    1244     signalManager.setFreeze(0) 
    1245      
    1246 ##    d = orange.ExampleTable('wt'); d.name = 'wt' 
    1247 ##    d = orange.ExampleTable('wt-nometa'); d.name = 'wt' 
    1248 ##    ow.dataset(d, 2) 
    1249 ##    d = orange.ExampleTable('wt-nometa'); d.name = 'wt' 
    1250 ##    ow.dataset(d, 1) 
    1251 ##    ow.dataset(None, 1) 
    1252 ##    ow.dataset(None, 2) 
    1253  
    1254 ##    names = ['wt1', 'wt2', 'wt3', 'wt4'] 
    1255 ##    for i, s in enumerate(names):  
    1256 ##        d = orange.ExampleTable(s); d.name = s 
    1257 ##        ow.dataset(d, i) 
    1258 ##    ow.dataset(None, 2) 
    1259 """ 
    1260  
    1261  
     1892 
Note: See TracChangeset for help on using the changeset viewer.