@@ -278,7 +278,7 @@ class Position(enum.IntFlag):
278278
279279 # Start row/column where the heatmap items are inserted
280280 # (after the titles/legends/dendrograms)
281- Row0 = 4
281+ Row0 = 5
282282 Col0 = 3
283283 # The (color) legend row and column
284284 LegendRow , LegendCol = 0 , 4
@@ -290,6 +290,8 @@ class Position(enum.IntFlag):
290290 DendrogramRow = 2
291291 # The row for top column annotation labels
292292 TopLabelsRow = 3
293+ # Top color annotation row
294+ TopAnnotationRow = 4
293295 # Vertical split title column
294296 GroupTitleColumn = 0
295297
@@ -312,6 +314,7 @@ def __init__(self, parent=None, **kwargs):
312314 self .col_dendrograms = [] # type: List[Optional[DendrogramWidget]]
313315 self .row_dendrograms = [] # type: List[Optional[DendrogramWidget]]
314316 self .right_side_colors = [] # type: List[Optional[GraphicsPixmapWidget]]
317+ self .top_side_colors = [] # type: List[Optional[GraphicsPixmapWidget]]
315318 self .heatmap_colormap_legend = None
316319 self .bottom_legend_container = None
317320 self .__layout = GridLayout ()
@@ -343,6 +346,7 @@ def clear(self):
343346 self .col_dendrograms = []
344347 self .row_dendrograms = []
345348 self .right_side_colors = []
349+ self .top_side_colors = []
346350 self .heatmap_colormap_legend = None
347351 self .bottom_legend_container = None
348352 self .parts = None
@@ -363,12 +367,14 @@ def setHeatmaps(self, parts: 'Parts') -> None:
363367 # The row for the horizontal dendrograms
364368 DendrogramRow = self .DendrogramRow
365369 RightLabelColumn = Col0 + 2 * M + 1
370+ TopAnnotationRow = self .TopAnnotationRow
366371 TopLabelsRow = self .TopLabelsRow
367372 BottomLabelsRow = Row0 + N
368373 colormap = self .__colormap
369374 column_dendrograms : List [Optional [DendrogramWidget ]] = [None ] * M
370375 row_dendrograms : List [Optional [DendrogramWidget ]] = [None ] * N
371376 right_side_colors : List [Optional [GraphicsPixmapWidget ]] = [None ] * N
377+ top_side_colors : List [Optional [GraphicsPixmapWidget ]] = [None ] * M
372378
373379 data = parts .data
374380 if parts .col_names is None :
@@ -492,8 +498,6 @@ def setHeatmaps(self, parts: 'Parts') -> None:
492498 objectName = "row-labels-right"
493499 )
494500 labelslist .setMaximumWidth (300 )
495- pm = QPixmap (1 , rowitem .size )
496- pm .fill (Qt .transparent )
497501 rowauxsidecolor = GraphicsPixmapWidget (
498502 parent = self , visible = False ,
499503 scaleContents = True , aspectMode = Qt .IgnoreAspectRatio ,
@@ -520,10 +524,20 @@ def setHeatmaps(self, parts: 'Parts') -> None:
520524 visible = self .__columnLabelPosition & Position .Top ,
521525 objectName = "column-labels-top" ,
522526 )
527+ colauxsidecolor = GraphicsPixmapWidget (
528+ parent = self , visible = False ,
529+ scaleContents = True , aspectMode = Qt .IgnoreAspectRatio ,
530+ sizePolicy = QSizePolicy (QSizePolicy .Ignored ,
531+ QSizePolicy .Maximum ),
532+ minimumSize = QSizeF (- 1 , 10 )
533+ )
534+
523535 grid .addItem (labelslist , TopLabelsRow , Col0 + 2 * j + 1 ,
524536 Qt .AlignBottom | Qt .AlignLeft )
537+ grid .addItem (colauxsidecolor , TopAnnotationRow , Col0 + 2 * j + 1 )
525538 col_annotation_widgets .append (labelslist )
526539 col_annotation_widgets_top .append (labelslist )
540+ top_side_colors [j ] = colauxsidecolor
527541
528542 # Bottom attr annotations
529543 labelslist = TextListWidget (
@@ -560,12 +574,33 @@ def setHeatmaps(self, parts: 'Parts') -> None:
560574 )
561575 legend .setMaximumWidth (300 )
562576 grid .addItem (legend , self .LegendRow , self .LegendCol , 1 , M * 2 )
563- legend_container = QGraphicsWidget (
577+
578+ def container (orientation = Qt .Horizontal , ** kwargs ):
579+ container = QGraphicsWidget (** kwargs )
580+ layout = QGraphicsLinearLayout (orientation )
581+ layout .setContentsMargins (0 , 0 , 0 , 0 )
582+ container .setLayout (layout )
583+ return container
584+ # Container for color annotation legends
585+ legend_container = container (
564586 visible = False ,
565- sizePolicy = QSizePolicy (QSizePolicy .Maximum , QSizePolicy .Fixed )
587+ sizePolicy = QSizePolicy (QSizePolicy .Maximum , QSizePolicy .Fixed ),
588+ objectName = "annotation-legend-container"
566589 )
567- legend_container .setLayout (QGraphicsLinearLayout ())
568- legend_container .layout ().setContentsMargins (0 , 0 , 0 , 0 )
590+ legend_container .layout ().setSpacing (3 )
591+ legend_container_rows = container (
592+ sizePolicy = QSizePolicy (QSizePolicy .Maximum , QSizePolicy .Fixed ),
593+ visible = False , objectName = "row-annotation-legend-container"
594+ )
595+ legend_container_cols = container (
596+ sizePolicy = QSizePolicy (QSizePolicy .Maximum , QSizePolicy .Fixed ),
597+ visible = False , objectName = "row-annotation-legend-container"
598+ )
599+ # ? segfault on scene.clear() ?
600+ legend_container ._keep_ref = (legend_container_cols , legend_container_rows )
601+ legend_container .layout ().addItem (legend_container_rows )
602+ legend_container .layout ().addItem (legend_container_cols )
603+
569604 grid .addItem (legend_container , BottomLabelsRow + 1 , Col0 + 1 , 1 , M * 2 - 1 ,
570605 alignment = Qt .AlignRight )
571606
@@ -577,6 +612,7 @@ def setHeatmaps(self, parts: 'Parts') -> None:
577612 self .col_dendrograms = column_dendrograms
578613 self .row_dendrograms = row_dendrograms
579614 self .right_side_colors = right_side_colors
615+ self .top_side_colors = top_side_colors
580616 self .heatmap_colormap_legend = legend
581617 self .bottom_legend_container = legend_container
582618 self .parts = parts
@@ -679,50 +715,113 @@ def setRowSideColorAnnotations(
679715 name: str
680716 Name/title for the annotation column.
681717 """
682- items = self .right_side_colors
683718 col = self .Col0 + 2 * len (self .parts .columns )
719+ legend_layout = self .bottom_legend_container .layout ()
720+ legend_container = legend_layout .itemAt (1 )
721+ self .__setColorAnnotationsHelper (
722+ data , colormap , name , self .right_side_colors , col , Qt .Vertical ,
723+ legend_container
724+ )
725+ legend_container .setVisible (True )
726+
727+ def setColumnSideColorAnnotations (
728+ self , data : np .ndarray , colormap : ColorMap = None , name = ""
729+ ):
730+ """
731+ Set an optional column color annotations.
732+
733+ Parameters
734+ ----------
735+ data: Optional[np.ndarray]
736+ A sequence such that it is accepted by `colormap.apply`. If None
737+ then the color annotations are cleared.
738+ colormap: ColorMap
739+ name: str
740+ Name/title for the annotation column.
741+ """
742+ row = self .TopAnnotationRow
743+ legend_layout = self .bottom_legend_container .layout ()
744+ legend_container = legend_layout .itemAt (0 )
745+ self .__setColorAnnotationsHelper (
746+ data , colormap , name , self .top_side_colors , row , Qt .Horizontal ,
747+ legend_container )
748+ legend_container .setVisible (True )
749+
750+ def __setColorAnnotationsHelper (
751+ self , data : np .ndarray , colormap : ColorMap , name : str ,
752+ items : List [GraphicsPixmapWidget ], position : int ,
753+ orientation : Qt .Orientation , legend_container : QGraphicsWidget ):
684754 layout = self .__layout
685- nameitem = layout .itemAt (self .TopLabelsRow , col )
686- width = QFontMetrics (self .font ()).lineSpacing ()
687- legend_container = self .bottom_legend_container
755+ if orientation == Qt .Horizontal :
756+ nameitem = layout .itemAt (position , 0 )
757+ else :
758+ nameitem = layout .itemAt (self .TopLabelsRow , position )
759+ size = QFontMetrics (self .font ()).lineSpacing ()
688760 layout_clear (legend_container .layout ())
689761
762+ def grid_set_maximum_size (position : int , size : float ):
763+ if orientation == Qt .Horizontal :
764+ layout .setRowMaximumHeight (position , size )
765+ else :
766+ layout .setColumnMaximumWidth (position , size )
767+
768+ def set_minimum_size (item : QGraphicsLayoutItem , size : float ):
769+ if orientation == Qt .Horizontal :
770+ item .setMinimumHeight (size )
771+ else :
772+ item .setMinimumWidth (size )
773+ item .updateGeometry ()
774+
775+ def reset_minimum_size (item : QGraphicsLayoutItem ):
776+ set_minimum_size (item , - 1 )
777+
690778 def set_hidden (item : GraphicsPixmapWidget ):
691779 item .setVisible (False )
692- item .setMinimumWidth (- 1 )
693- item .updateGeometry ()
780+ reset_minimum_size (item ,)
694781
695782 def set_visible (item : GraphicsPixmapWidget ):
696783 item .setVisible (True )
697- item .setMinimumWidth (10 )
784+ set_minimum_size (item , 10 )
785+
786+ def set_preferred_size (item , size ):
787+ if orientation == Qt .Horizontal :
788+ item .setPreferredHeight (size )
789+ else :
790+ item .setPreferredWidth (size )
698791 item .updateGeometry ()
699792
700793 if data is None :
701794 apply_all (filter (None , items ), set_hidden )
702- layout .setColumnMaximumWidth (col , 0 )
795+ grid_set_maximum_size (position , 0 )
796+
703797 nameitem .item .setVisible (False )
704798 nameitem .updateGeometry ()
705799 legend_container .setVisible (False )
706800 return
707801 else :
708802 apply_all (filter (None , items ), set_visible )
709- layout . setColumnMaximumWidth ( col , FLT_MAX )
803+ grid_set_maximum_size ( position , FLT_MAX )
710804 legend_container .setVisible (True )
711805
712- parts = self .parts .rows
806+ if orientation == Qt .Horizontal :
807+ parts = self .parts .columns
808+ else :
809+ parts = self .parts .rows
713810 for p , item in zip (parts , items ):
714811 if item is not None :
715812 subset = data [p .normalized_indices ]
716813 subset = colormap .apply (subset )
717- img = qimage_from_array (subset .reshape ((- 1 , 1 , subset .shape [- 1 ])))
814+ rgbdata = subset .reshape ((- 1 , 1 , subset .shape [- 1 ]))
815+ if orientation == Qt .Horizontal :
816+ rgbdata = rgbdata .reshape ((1 , - 1 , rgbdata .shape [- 1 ]))
817+ img = qimage_from_array (rgbdata )
718818 item .setPixmap (img )
719819 item .setVisible (True )
720- item . setPreferredWidth ( width )
820+ set_preferred_size ( item , size )
721821
722822 nameitem .item .setText (name )
723823 nameitem .item .setVisible (True )
724- nameitem .setPreferredWidth (width )
725- nameitem .updateGeometry ()
824+ set_preferred_size (nameitem , size )
726825
727826 container = legend_container .layout ()
728827 if isinstance (colormap , CategoricalColorMap ):
0 commit comments