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

Code before:
        super().__init__(ax)

        _api.check_isinstance((dict, None), label_props=label_props,
                              frame_props=frame_props, check_props=check_props)

        ax.set_xticks([])
        ax.set_yticks([])
        ax.set_navigate(False)

        if actives is None:
            actives = [False] * len(labels)

        self._useblit = useblit and self.canvas.supports_blit  # TODO: make dynamic

        label_props = _expand_text_props(label_props)

        # Calculate button and label positions
        button_xs, button_ys, label_xs, label_ys = _calculate_widget_button_layout(
            ax, labels, label_props, layout
        )

        self.labels = [
            ax.text(x, y, label, transform=ax.transAxes,
                    horizontalalignment="left", verticalalignment="center",
                    **props)
            for x, y, label, props in zip(label_xs, label_ys, labels, label_props)]
        text_size = np.array([text.get_fontsize() for text in self.labels]) / 2

        frame_props = {
            's': text_size**2,
            'linewidth': 1,
            **cbook.normalize_kwargs(frame_props, collections.PathCollection),
            'marker': 's',
            'transform': ax.transAxes,
        }
        frame_props.setdefault('facecolor', frame_props.get('color', 'none'))
        frame_props.setdefault('edgecolor', frame_props.pop('color', 'black'))
        self._frames = ax.scatter(button_xs, button_ys, **frame_props)
        check_props = {
            'linewidth': 1,
            's': text_size**2,
            **cbook.normalize_kwargs(check_props, collections.PathCollection),
            'marker': 'x',
            'transform': ax.transAxes,
            'animated': self._useblit,
        }
        check_props.setdefault('facecolor', check_props.pop('color', 'black'))
        self._checks = ax.scatter(button_xs, button_ys, **check_props)
        # The user may have passed custom colours in check_props, so we need to
        # create the checks (above), and modify the visibility after getting
        # whatever the user set.

Review comment (performance):
I don't know how expensive the double creation is, but I think you can get rid it by passing back the text objects you created in the method: 

```python
 button_xs, button_ys, label_xs, label_ys, self.labels = _calculate_widget_button_layout(
            ax, labels, label_props, layout)
        )

for ltext, x, y in zip(self.labels, label_xs, label_ys):
     ltext.set(position=(x,y), horizontalalignment="left", verticalalignment="center")
     ax.add_artist(ltext)
```

Going for even tighter scoping, you can do the loop setting the position inside the _calculate_widget_button_layout and then just modify the artist and add it in the specific way each method wants (if it differs):

```python
button_xs, button_ys, self.labels = _calculate_widget_button_layout(
            ax, labels, label_props, layout
        )

for ltext.in self.labels: 
     ltext.set(horizontalalignment ="left", verticalalignment="center")
     ax.add_artist(ltext)

``` 
It breaks scoping a bit to also return the objects but I think that's fine since this is a private method. 

Code after:
            mtext.Text(0, 0, text=label, transform=self.ax.transAxes,
                       horizontalalignment="left", verticalalignment="center",
                       **props)
            for label, props in zip(labels, label_props)
        ]
        # Set figure reference so Text objects can access figure properties
        for text in self.labels:
            text.set_figure(fig)
        # Calculate max text width per column (in axes coordinates)
        renderer = self.ax.figure.canvas.get_renderer()
        inv_trans = fig.dpi_scale_trans.inverted()
        col_widths = [
            max(
                (
                    text.get_window_extent(renderer).transformed(inv_trans).width * 72
                    for text in self.labels[col_idx::n_cols]
                ),
                default=0,
            )
            / axes_width_display
            for col_idx in range(n_cols)
        ]

        # 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
            )
        label_idx = np.arange(n_labels)
        self._buttons_xs = np.take(col_x_positions, label_idx % n_cols)
        self._buttons_ys = ys_per_row[label_idx // n_cols]
        for text, x, y in zip(self.labels, self._buttons_xs, self._buttons_ys):
            text.set_position((x + button_text_offset, y))
            self.ax.add_artist(text)

    def _init_props(self, text_size, **kwargs):
        raise NotImplementedError("This method should be defined in subclasses")

    def _clear(self, event):
        """Internal event handler to clear the buttons."""
        if self.ignore(event) or self.canvas.is_saving():
            return
        if self._useblit and self.canvas.supports_blit:
            self._save_blit_background(self.canvas.copy_from_bbox(self.ax.bbox))
        self.ax.draw_artist(self._buttons)