Repository: matplotlib/matplotlib
File: lib/matplotlib/widgets.py (Python)

Code before:
            label,
            transform=ax.transAxes,
            **props,
        ))
    # Force a draw to get accurate text measurements
    ax.figure.canvas.draw()

    # Calculate max text width per column (in axes coordinates)
    col_widths = []
    for col_idx in range(n_cols):
        col_texts = []
        for row_idx in range(n_rows):
            label_idx = row_idx * n_cols + col_idx
            if label_idx < n_labels:
                col_texts.append(temp_texts[label_idx])
        if col_texts:
            col_widths.append(
                max(
                    text.get_window_extent(
                        ax.figure.canvas.get_renderer()
                    ).width
                    for text in col_texts
                ) / axes_width_display
            )
        else:
            col_widths.append(0)
    # Remove temporary text objects
    for text in temp_texts:
        text.remove()

    # Center rows vertically in the axes
    ys_per_row = np.linspace(1, 0, n_rows + 2)[1:-1]
    # Calculate x positions based on text widths
    col_x_positions = [left_margin]  # First column starts at left margin
    for col_idx in range(n_cols - 1):
        col_x_positions.append(
            col_x_positions[-1] +
            button_text_offset +
            col_widths[col_idx] +
            col_spacing
        )
    # Create final positions (left-to-right, top-to-bottom)
    button_xs = []
    button_ys = []
    label_xs = []
    label_ys = []
    for label_idx in range(n_labels):
        row_idx = label_idx // n_cols
        col_idx = label_idx % n_cols
        x = col_x_positions[col_idx]
        y = ys_per_row[row_idx]

Review comment (suggestion):
```suggestion
    col_widths = [
        max(
            (
                text.get_window_extent(ax.figure.canvas.get_renderer()).width
                for text in temp_texts[col_idx::n_cols]
            ),
            default=0,
        )
        / axes_width_display
        for col_idx in range(n_cols)
    ]
```

Code after:
        self.stop_typing()

    @_call_with_reparented_event
    def _motion(self, event):
        if self.ignore(event):
            return
        c = self.hovercolor if self.ax.contains(event)[0] else self.color
        if not colors.same_color(c, self.ax.get_facecolor()):
            self.ax.set_facecolor(c)
            if self.drawon:
                self.ax.get_figure(root=True).canvas.draw()

    def on_text_change(self, func):
        """
        When the text changes, call this *func* with event.

        A connection id is returned which can be used to disconnect.
        """
        return self._observers.connect('change', lambda text: func(text))

    def on_submit(self, func):
        """
        When the user hits enter or leaves the submission box, call this
        *func* with event.

        A connection id is returned which can be used to disconnect.
        """
        return self._observers.connect('submit', lambda text: func(text))

    def disconnect(self, cid):
        """Remove the observer with connection id *cid*."""
        self._observers.disconnect(cid)


class RadioButtons(_Buttons):
    """
    A GUI neutral radio button.

    For the buttons to remain responsive you must keep a reference to this
    object.

    Connect to the RadioButtons with the `~._Buttons.on_clicked` method.

    Attributes
    ----------
    ax : `~matplotlib.axes.Axes`
        The parent Axes for the widget.
    activecolor : :mpltype:`color`
        The color of the selected button.
    labels : list of `.Text`
        The button labels.