753753stack, stacked : bool, default: False
754754 Whether to "stack" bars from successive columns of {y}
755755 data or plot bars side-by-side in groups.
756+ bar_labels : bool, default rc["bar.bar_labels"]
757+ Whether to show the height values for vertical bars or width values for horizontal bars.
758+ bar_labels_kw : dict, default None
759+ Keywords to format the bar_labels, see :func:`~matplotlib.pyplot.bar_label`.
756760%(plot.args_1d_shared)s
757761
758762Other parameters
@@ -4165,6 +4169,8 @@ def _apply_bar(
41654169 # Parse args
41664170 kw = kwargs .copy ()
41674171 kw , extents = self ._inbounds_extent (** kw )
4172+ bar_labels = kw .pop ("bar_labels" , rc ["bar.bar_labels" ])
4173+ bar_labels_kw = kw .pop ("bar_labels_kw" , {})
41684174 name = "barh" if orientation == "horizontal" else "bar"
41694175 stack = _not_none (stack = stack , stacked = stacked )
41704176 xs , hs , kw = self ._parse_1d_args (xs , hs , orientation = orientation , ** kw )
@@ -4212,6 +4218,10 @@ def _apply_bar(
42124218 obj = self ._call_negpos (name , x , h , w , b , use_zero = True , ** kw )
42134219 else :
42144220 obj = self ._call_native (name , x , h , w , b , ** kw )
4221+ if bar_labels :
4222+ if isinstance (obj , mcontainer .BarContainer ):
4223+ self ._add_bar_labels (obj , orientation = orientation , ** bar_labels_kw )
4224+
42154225 self ._fix_patch_edges (obj , ** edgefix_kw , ** kw )
42164226 for y in (b , b + h ):
42174227 self ._inbounds_xylim (extents , x , y , orientation = orientation )
@@ -4224,6 +4234,59 @@ def _apply_bar(
42244234 self ._update_guide (objs , ** guide_kw )
42254235 return objs [0 ] if len (objs ) == 1 else cbook .silent_list ("BarContainer" , objs )
42264236
4237+ def _add_bar_labels (
4238+ self ,
4239+ container ,
4240+ * ,
4241+ orientation = "horizontal" ,
4242+ ** kwargs ,
4243+ ):
4244+ """
4245+ Automatically add bar labels and rescale the
4246+ limits to produce a striking visual image.
4247+ """
4248+ # Drawing the labels does not rescale the limits to account
4249+ # for the labels. We therefore first draw them and then
4250+ # adjust the range for x or y depending on the orientation of the bar
4251+ bar_labels = self ._call_native ("bar_label" , container , ** kwargs )
4252+
4253+ which = "x" if orientation == "horizontal" else "y"
4254+ other_which = "y" if orientation == "horizontal" else "x"
4255+
4256+ # Get current limits
4257+ current_lim = getattr (self , f"get_{ which } lim" )()
4258+ other_lim = getattr (self , f"get_{ other_which } lim" )()
4259+
4260+ # Find the maximum extent of text + bar position
4261+ max_extent = current_lim [1 ] # Start with current upper limit
4262+
4263+ for label , bar in zip (bar_labels , container ):
4264+ # Get text bounding box
4265+ bbox = label .get_window_extent (renderer = self .figure .canvas .get_renderer ())
4266+ bbox_data = bbox .transformed (self .transData .inverted ())
4267+
4268+ if orientation == "horizontal" :
4269+ # For horizontal bars, check if text extends beyond right edge
4270+ bar_end = bar .get_width () + bar .get_x ()
4271+ text_end = bar_end + bbox_data .width
4272+ max_extent = max (max_extent , text_end )
4273+ else :
4274+ # For vertical bars, check if text extends beyond top edge
4275+ bar_end = bar .get_height () + bar .get_y ()
4276+ text_end = bar_end + bbox_data .height
4277+ max_extent = max (max_extent , text_end )
4278+
4279+ # Only adjust limits if text extends beyond current range
4280+ if max_extent > current_lim [1 ]:
4281+ padding = (max_extent - current_lim [1 ]) * 1.25 # Add a bit of padding
4282+ new_lim = (current_lim [0 ], max_extent + padding )
4283+ getattr (self , f"set_{ which } lim" )(new_lim )
4284+
4285+ # Keep the other axis unchanged
4286+ getattr (self , f"set_{ other_which } lim" )(other_lim )
4287+
4288+ return bar_labels
4289+
42274290 @inputs ._preprocess_or_redirect ("x" , "height" , "width" , "bottom" )
42284291 @docstring ._concatenate_inherited
42294292 @docstring ._snippet_manager
0 commit comments