tksheet icon indicating copy to clipboard operation
tksheet copied to clipboard

Resizing columns automatically to fit the frame if they would be smaller in total than the available space

Open DoomOfMax97 opened this issue 1 year ago • 11 comments

Hello,

I have been trying a lot but I can't seem to make this happen reliably, do you have any tips/ideas?

DoomOfMax97 avatar May 21 '24 17:05 DoomOfMax97

Hi there,

Have you tried the option auto_resize_columns= which resizes them to an int amount of pixels?

Or do you mean resizing them to fit the cell text rather than a width in pixels?

ragardner avatar May 22 '24 15:05 ragardner

In my testing just now self.set_options(auto_resize_columns=self.winfo_width(), redraw=True) self.redraw(True, True) seems to work for resizing a single column correctly, but not if you have multiple, there could be an issue with my subclass though. The idea is, at least that's what my code tried to do initially is:

  1. Calculate a weight for each column based on the largest of the first n cells of the current column.
  2. If winfo_width() < sum(weights): 2a. Set each column to size "weight", they won't fit, but have an efficient column width 2b. Multiply each weight by winfo_width()/sum(weights) and fudge the last one, so all columns get resized to fit the frame

Is there a good/an easier way to do this?

DoomOfMax97 avatar May 24 '24 15:05 DoomOfMax97

Sorry about the confusion with auto_resize... you can find the docs here: https://github.com/ragardner/tksheet/wiki/Version-7#auto-resize-column-widths-to-fit-the-window

Basically the value is for the minimum width in pixels of each individual column, so you could set this to 50 for example

About your question, I'm afraid I can only point to the relevant functions at the moment, especially the one below

  • https://github.com/ragardner/tksheet/wiki/Version-7#set-or-get-a-specific-column-width

There is an internal function in the ColumnHeaders class, accessible from the Sheet() class, but considering the things above I might have to make changes to it in the future so maybe you don't want to use it,

self.CH.set_col_width(
    col=column int,
    width=None, # set to text size
    only_set_if_too_small=False,
    displayed_only=True, # if you have a lot of rows
    return_new_width=True or False, # when True this will not set the column width but return the width it was going to be set to
)

Looking at the available Sheet() functions and their uses I think there is a lack of functionality, such as text measuring functionality for columns, getting the currently visible columns, etc. This would make work like yours a lot easier

I will have to add some more functions for dealing with row heights / column widths, they may not have the functionality you seek above but by using them maybe you'll get to what you want. I can't offer a timeframe on this work though sorry

ragardner avatar May 24 '24 18:05 ragardner

I see, thank you for your help anyways. This is my current code, maybe it helps you in any way.

    def resize_columns_to_fit(self, n=5):
        minimum_scale_factor = 15
        scrollbar_width = 17

        if (self._headers is None
                or self._is_shown is False
                or self._show_mode == NO_DATA):
            return

        # Calculate the weight for each column based on the largest of the first n cells
        weights = []
        for col_idx in range(len(self._headers)):
            # Get the first n cells in the current column
            col_data = [len(str(self._data[row_idx][col_idx])) for row_idx in range(min(n, len(self._data)))]
            # Include the header in the calculation
            col_data.append(len(str(self._headers[col_idx])))
            # Calculate the weight based on the largest item
            max_width = max(col_data)
            weights.append(max_width)

        # Get the width of the sheet widget
        root_window.update_idletasks()  # Ensure the window is updated before getting its width
        winfo_width = self._sheet.winfo_width()
        total_weight = sum(weights)
        # Proportionally adjust the weights to fit the widget width
        if total_weight == 0:
            scale_factor = minimum_scale_factor
        else:
            scale_factor = max(winfo_width / total_weight, minimum_scale_factor)
        weights = [int(weight * scale_factor) for weight in weights]
        # Adjust the last column to ensure the total width matches the widget width
        if scale_factor > minimum_scale_factor:
            weights[-1] += winfo_width - sum(weights)
        if len(self._headers) == 1:
            self._sheet.set_all_column_widths(weights[-1] - scrollbar_width)
            return

        # Set each column width
        for col_idx, weight in enumerate(weights):
            self._sheet.column_width(col_idx, weight)

DoomOfMax97 avatar May 26 '24 15:05 DoomOfMax97

Hello,

Thanks for adding your code,

In version 7.2.2 I have added a few functions you may find useful:

  • visible_rows e.g. start_row, end_row = self._sheet.visible_rows (it's a property)
  • visible_columns e.g. start_column, end_column = self._sheet.visible_columns (it's a property)
  • get_row_text_height - https://github.com/ragardner/tksheet/wiki/Version-7#get-a-rows-text-height
  • get_column_text_width - https://github.com/ragardner/tksheet/wiki/Version-7#get-a-columns-text-width

Cheers

ragardner avatar May 27 '24 15:05 ragardner

No worries,

looks good, will give them a try.

Thank you

DoomOfMax97 avatar May 27 '24 16:05 DoomOfMax97

I can't seem to adjust the width of a column. sheet.get_column_widths() indicates that the widths have changed, but there is no change on the screen. I am not using autosizing. (This is without default_column_width, which does change the column widths on screen when it is not commented out.) This is what I have tried: self.mileage_table.column_width( column=5, width=400, redraw=True) Is my syntax correct?

PreacherBill avatar Mar 07 '25 18:03 PreacherBill

Your syntax appears to be correct make sure you havent used auto_resize_columns or max_column_width and check you're using the latest version of the library

I tried using the function in the same way and all seems ok, I also had a quick look over the code involved and nothing seems wrong

If you want to post more code or if you can manage to reproduce the issue in a minimal example I could take a look

Kind regards

ragardner avatar Mar 07 '25 19:03 ragardner

I moved the column_width code to after I populate the sheet with data and it resized correctly. Thanks for the really quick response!

PreacherBill avatar Mar 07 '25 19:03 PreacherBill

What does the iterator in the below code look like? Can I resize all columns to a different value with it?

set_column_widths( column_widths: Iterator[int, float] | None = None, canvas_positions: bool = False, reset: bool = False, ) -> Sheet

PreacherBill avatar Mar 07 '25 19:03 PreacherBill

any object you can iterate over, lists, tuples, generators

from itertools import repeat

.set_column_widths(repeat(400, 5))

ragardner avatar Mar 07 '25 19:03 ragardner