Search code examples
mergeopenpyxlcellassignvertical-text

Unmerge and Assign Values Only Vertically or Horizontally Openpyxl


Using the answer provided by aka863 here: How to split merged Excel cells with Python?

I can unmerge, fill values and copy the styling. My questions is how to make the value assigning/filling process configurable.

I want the user to be able to choose whether the values will be filled vertically/horizontally.

I have tried changing the last loop where we assign the top_left_cell_values to unmerged cells. However I couldn't find a way to make it horizontal/vertical configurable. (I'm planning to use radio buttons and tkinter for this)


Solution

  • Its certainly possible to have the code de-merge cells and fill cells in whichever direction, vertically or horizontally regardless of which way the merge was originally. Or not fill at all, so only the top left cell retains the 'value' of the previously merged cells, which is default on unmerge.
    Changing the direction of the fill requires some change and re-calculation on the max row and column values in the iter_rows loop, but is simple enough.

    However it seems in your last comment you just want to give the user the option to fill or not fill on horizontal merges. In that case you just need to ask the question, and then run the iter_rows loop only if the response is yes.

    The code sample below is based on the answer referenced question.
    I'm assuming only single line horizontal merges since you dont mention what if anything should be done with vertical merges in the comment.
    The code does initially check and indicate the merge direction either vertically or horizontally so it can be included take some action if a merge is vertical.
    On code run after displaying the range and direction of the merge, the question is asked to fill, yes or no. If yes the cells are de-merged and all cells filled with the top left cell value using the iter_rows loop. If answer no then the cells are just de-merged.

    from openpyxl import load_workbook
    from openpyxl.utils.cell import range_boundaries
    
    wb = load_workbook(filename='foo.xlsx')
    
    st = wb['Sheet1']
    mcr_coord_list = [mcr.coord for mcr in st.merged_cells.ranges]
    direction_dict = {'v': 'vertical', 'h': 'horizontal'}
    
    for mcr in mcr_coord_list:
        print('---------------------------------------------------\n')
        merge_direction = ''
        min_col, min_row, max_col, max_row = range_boundaries(mcr)
        top_left_cell_value = st.cell(row=min_row, column=min_col).value
        if min_col == max_col:
            merge_direction = 'v'
        elif min_row == max_row:
            merge_direction = 'h'
    
        print(f"The cell range {mcr} is merged {direction_dict[merge_direction]}ly with the data '{top_left_cell_value}'")
    
        while True:
            demerge_fill = input('Do you want the de-merge to fill all cells(y|n)? ')
            if demerge_fill.lower() in ["y", "n"]:
                break
            else:
                print('Invalid response')
    
        st.unmerge_cells(mcr)
    
        if demerge_fill == 'y':
            for row in st.iter_rows(min_col=min_col, min_row=min_row, max_col=max_col, max_row=max_row):
                for cell in row:
                    cell.value = top_left_cell_value
        else:
            print(f"Only the top left cell {mcr.split(':')[0]} will contain the data!")
    
    wb.save('merged_tmp.xlsx')