@@ -199,7 +199,7 @@ def boundingRect(self):
199199 self .__boundingRect = sh .boundingRect ().adjusted (- pw , - pw , pw , pw )
200200 return self .__boundingRect
201201
202- class SelectionItem (QGraphicsItemGroup ):
202+ class _SelectionItem (QGraphicsItemGroup ):
203203 def __init__ (self , parent , path , unscaled_path , label = "" ):
204204 super ().__init__ (parent )
205205 self .path = QGraphicsPathItem (path , self )
@@ -243,13 +243,14 @@ def _update_label_pos(self):
243243
244244 #: Emitted when a user clicks on the cluster item.
245245 itemClicked = Signal (ClusterGraphicsItem )
246+ #: Signal emitted when the selection changes.
246247 selectionChanged = Signal ()
248+ #: Signal emitted when the selection was changed by the user.
247249 selectionEdited = Signal ()
248250
249251 def __init__ (self , parent = None , root = None , orientation = Left ,
250252 hoverHighlightEnabled = True , selectionMode = ExtendedSelection ,
251253 ** kwargs ):
252-
253254 super ().__init__ (None , ** kwargs )
254255 # Filter all events from children (`ClusterGraphicsItem`s)
255256 self .setFiltersChildEvents (True )
@@ -272,11 +273,46 @@ def __init__(self, parent=None, root=None, orientation=Left,
272273 self .__hoverHighlightEnabled = hoverHighlightEnabled
273274 self .__selectionMode = selectionMode
274275 self .setContentsMargins (0 , 0 , 0 , 0 )
275- self .set_root (root )
276+ self .setRoot (root )
276277 if parent is not None :
277278 self .setParentItem (parent )
278279
280+ def setSelectionMode (self , mode ):
281+ """
282+ Set the selection mode.
283+ """
284+ assert mode in [DendrogramWidget .NoSelection ,
285+ DendrogramWidget .SingleSelection ,
286+ DendrogramWidget .ExtendedSelection ]
287+
288+ if self .__selectionMode != mode :
289+ self .__selectionMode = mode
290+ if self .__selectionMode == DendrogramWidget .NoSelection and \
291+ self ._selection :
292+ self .setSelectedClusters ([])
293+ elif self .__selectionMode == DendrogramWidget .SingleSelection and \
294+ len (self ._selection ) > 1 :
295+ self .setSelectedClusters ([self .selected_nodes ()[- 1 ]])
296+
297+ def selectionMode (self ):
298+ """
299+ Return the current selection mode.
300+ """
301+ return self .__selectionMode
302+
303+ def setHoverHighlightEnabled (self , enabled ):
304+ if self .__hoverHighlightEnabled != bool (enabled ):
305+ self .__hoverHighlightEnabled = bool (enabled )
306+ if self ._highlighted_item is not None :
307+ self ._set_hover_item (None )
308+
309+ def isHoverHighlightEnabled (self ):
310+ return self .__hoverHighlightEnabled
311+
279312 def clear (self ):
313+ """
314+ Clear the widget.
315+ """
280316 scene = self .scene ()
281317 if scene is not None :
282318 scene .removeItem (self ._itemgroup )
@@ -299,10 +335,15 @@ def clear(self):
299335 self ._cluster_parent = {}
300336 self .updateGeometry ()
301337
302- def set_root (self , root ):
303- """Set the root cluster.
338+ def setRoot (self , root ):
339+ # type: (Tree) -> None
340+ """
341+ Set the root cluster tree node for display.
304342
305- :param Tree root: Root tree.
343+ Parameters
344+ ----------
345+ root : Tree
346+ The tree root node.
306347 """
307348 self .clear ()
308349 self ._root = root
@@ -322,17 +363,30 @@ def set_root(self, root):
322363 self ._relayout ()
323364 self ._rescale ()
324365 self .updateGeometry ()
366+ set_root = setRoot
325367
326- def item (self , node ):
327- """Return the DendrogramNode instance representing the cluster.
368+ def root (self ):
369+ # type: () -> Tree
370+ """
371+ Return the cluster tree root node.
328372
329- :type cluster: :class:`Tree`
373+ Returns
374+ -------
375+ root : Tree
376+ """
377+ return self ._root
330378
379+ def item (self , node ):
380+ # type: (Tree) -> DendrogramWidget.ClusterGraphicsItem
381+ """
382+ Return the ClusterGraphicsItem instance representing the cluster `node`.
331383 """
332384 return self ._items .get (node )
333385
334- def height_at (self , point ):
335- """Return the cluster height at the point in widget local coordinates.
386+ def heightAt (self , point ):
387+ # type: (QPointF) -> float
388+ """
389+ Return the cluster height at the point in widget local coordinates.
336390 """
337391 if not self ._root :
338392 return 0
@@ -356,10 +410,12 @@ def height_at(self, point):
356410 if self .orientation in [self .Left , self .Bottom ]:
357411 height = Fr (base ) - Fr (height )
358412 return float (height )
413+ height_at = heightAt
359414
360- def pos_at_height (self , height ):
361- """Return a point in local coordinates for `height` (in cluster
362- height scale).
415+ def posAtHeight (self , height ):
416+ # type: (float) -> float
417+ """
418+ Return a point in local coordinates for `height` (in cluster
363419 """
364420 if not self ._root :
365421 return QPointF ()
@@ -374,6 +430,7 @@ def pos_at_height(self, height):
374430 else :
375431 p = QPointF (0 , height )
376432 return self ._transform .map (p )
433+ pos_at_height = posAtHeight
377434
378435 def _set_hover_item (self , item ):
379436 """Set the currently highlighted item."""
@@ -394,32 +451,34 @@ def branches(item):
394451 for it in postorder (item , branches ):
395452 it .setPen (hpen )
396453
397- def leaf_items (self ):
454+ def leafItems (self ):
398455 """Iterate over the dendrogram leaf items (:class:`QGraphicsItem`).
399456 """
400457 if self ._root :
401458 return (self ._items [leaf ] for leaf in leaves (self ._root ))
402459 else :
403460 return iter (())
461+ leaf_items = leafItems
404462
405- def leaf_anchors (self ):
463+ def leafAnchors (self ):
406464 """Iterate over the dendrogram leaf anchor points (:class:`QPointF`).
407465
408466 The points are in the widget local coordinates.
409467 """
410- for item in self .leaf_items ():
468+ for item in self .leafItems ():
411469 anchor = QPointF (item .element .anchor )
412470 yield self .mapFromItem (item , anchor )
471+ leaf_anchors = leafAnchors
413472
414- def selected_nodes (self ):
415- """Return the selected clusters."""
473+ def selectedNodes (self ):
474+ """
475+ Return the selected cluster nodes.
476+ """
416477 return [item .node for item in self ._selection ]
478+ selected_nodes = selectedNodes
417479
418- def set_selected_items (self , items ):
419- """Set the item selection.
420-
421- :param items: List of `GraphicsItems`s to select.
422- """
480+ def setSelectedItems (self , items : List [ClusterGraphicsItem ]):
481+ """Set the item selection."""
423482 to_remove = set (self ._selection ) - set (items )
424483 to_add = set (items ) - set (self ._selection )
425484
@@ -431,27 +490,26 @@ def set_selected_items(self, items):
431490 if to_add or to_remove :
432491 self ._re_enumerate_selections ()
433492 self .selectionChanged .emit ()
493+ set_selected_items = setSelectedItems
434494
435- def set_selected_clusters (self , clusters ) :
495+ def setSelectedClusters (self , clusters : List [ Tree ]) -> None :
436496 """Set the selected clusters.
437-
438- :param Tree items: List of cluster nodes to select .
439497 """
440- self .set_selected_items (list (map (self .item , clusters )))
498+ self .setSelectedItems (list (map (self .item , clusters )))
499+ set_selected_clusters = setSelectedClusters
441500
442- def is_selected (self , item ):
501+ def isItemSelected (self , item : ClusterGraphicsItem ) -> bool :
502+ """Is `item` selected (is a root of a selection)."""
443503 return item in self ._selection
444504
445- def is_included (self , item ):
505+ def isItemIncludedInSelection (self , item : ClusterGraphicsItem ) -> bool :
506+ """Is item included in any selection."""
446507 return self ._selected_super_item (item ) is not None
508+ is_included = isItemIncludedInSelection
447509
448- def select_item (self , item , state ):
449- """Set the `item`s selection state to `select_state`
450-
451- :param item: QGraphicsItem.
452- :param bool state: New selection state for item.
453-
454- """
510+ def setItemSelected (self , item , state ):
511+ # type: (ClusterGraphicsItem, bool) -> None
512+ """Set the `item`s selection state to `state`."""
455513 if state is False and item not in self ._selection or \
456514 state is True and item in self ._selection :
457515 return # State unchanged
@@ -482,6 +540,7 @@ def select_item(self, item, state):
482540
483541 self ._re_enumerate_selections ()
484542 self .selectionChanged .emit ()
543+ select_item = setItemSelected
485544
486545 @staticmethod
487546 def _create_path (item , path ):
@@ -493,7 +552,6 @@ def _create_path(item, path):
493552 ppath = path_outline (ppath , width = - 8 )
494553 return ppath
495554
496-
497555 @staticmethod
498556 def _create_label (i ):
499557 return f"C{ i + 1 } "
@@ -505,7 +563,7 @@ def _add_selection(self, item):
505563 path = self ._transform .map (outline )
506564 ppath = self ._create_path (item , path )
507565 label = self ._create_label (len (self ._selection ))
508- selection_item = self .SelectionItem (self , ppath , outline , label )
566+ selection_item = self ._SelectionItem (self , ppath , outline , label )
509567 selection_item .setPos (self .contentsRect ().topLeft ())
510568 self ._selection [item ] = selection_item
511569
@@ -697,7 +755,8 @@ def _rescale(self):
697755 self ._selection_items = None
698756 self ._update_selection_items ()
699757
700- def sizeHint (self , which , constraint = QSizeF ()):
758+ def sizeHint (self , which : Qt .SizeHint , constraint = QSizeF ()) -> QRectF :
759+ # reimplemented
701760 fm = QFontMetrics (self .font ())
702761 spacing = fm .lineSpacing ()
703762 mleft , mtop , mright , mbottom = self .getContentsMargins ()
@@ -726,31 +785,31 @@ def sceneEventFilter(self, obj, event):
726785 elif event .type () == QEvent .GraphicsSceneMousePress and \
727786 event .button () == Qt .LeftButton :
728787
729- is_selected = self .is_selected (obj )
788+ is_selected = self .isItemSelected (obj )
730789 is_included = self .is_included (obj )
731790 current_selection = list (self ._selection )
732791
733792 if self .__selectionMode == DendrogramWidget .SingleSelection :
734793 if event .modifiers () & Qt .ControlModifier :
735- self .set_selected_items (
794+ self .setSelectedItems (
736795 [obj ] if not is_selected else [])
737796 elif event .modifiers () & Qt .AltModifier :
738- self .set_selected_items ([])
797+ self .setSelectedItems ([])
739798 elif event .modifiers () & Qt .ShiftModifier :
740799 if not is_included :
741- self .set_selected_items ([obj ])
800+ self .setSelectedItems ([obj ])
742801 elif current_selection != [obj ]:
743- self .set_selected_items ([obj ])
802+ self .setSelectedItems ([obj ])
744803 elif self .__selectionMode == DendrogramWidget .ExtendedSelection :
745804 if event .modifiers () & Qt .ControlModifier :
746- self .select_item (obj , not is_selected )
805+ self .setItemSelected (obj , not is_selected )
747806 elif event .modifiers () & Qt .AltModifier :
748- self .select_item (self ._selected_super_item (obj ), False )
807+ self .setItemSelected (self ._selected_super_item (obj ), False )
749808 elif event .modifiers () & Qt .ShiftModifier :
750809 if not is_included :
751- self .select_item (obj , True )
810+ self .setItemSelected (obj , True )
752811 elif current_selection != [obj ]:
753- self .set_selected_items ([obj ])
812+ self .setSelectedItems ([obj ])
754813
755814 if current_selection != self ._selection :
756815 self .selectionEdited .emit ()
@@ -764,6 +823,7 @@ def sceneEventFilter(self, obj, event):
764823 return super ().sceneEventFilter (obj , event )
765824
766825 def changeEvent (self , event ):
826+ # reimplemented
767827 super ().changeEvent (event )
768828
769829 if event .type () == QEvent .FontChange :
@@ -774,11 +834,13 @@ def changeEvent(self, event):
774834 self ._rescale ()
775835
776836 def resizeEvent (self , event ):
837+ # reimplemented
777838 super ().resizeEvent (event )
778839 self ._rescale ()
779840
780841 def mousePressEvent (self , event ):
781- QGraphicsWidget .mousePressEvent (self , event )
842+ # reimplemented
843+ super ().mousePressEvent (event )
782844 # A mouse press on an empty widget part
783845 if event .modifiers () == Qt .NoModifier and self ._selection :
784846 self .set_selected_clusters ([])
0 commit comments