Search code examples
pythonpaint

A number of errors when creating save/load functions in simple Python MS Paint clone


Bear with me, I'm very new to programming. I'm running into a comedy of errors while trying to implement a rudimentary save and load feature in my Paint clone.

In my Paint clone, the grid of pixels is stored as a list of 100 lists, each with 100 tuples representing the RGB color value of every given pixel. I'm trying to implement the ability to save and load the grid at its current state to a .txt file. The current implementation of the save_grid function looks like:

def save_grid(grid, filename):
    with open(filename, 'w') as file:
        for row in grid:
            row_str = ','.join(str(value) for value in row)
            file.write(row_str + '\n')

This works as intended and saves a string of tuples separated by a new line character after each row in the grid, such that the save.txt file looks like: (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255) (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255) (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255), (255, 255, 255)...etc when saving a blank white grid. My current implementation of the load grid function looks like:

def load_grid(filename):
    with open(filename, 'r') as f:
        lines = f.readlines()

    lines = [line.strip() for line in lines]

    rows = [line.split(',') for line in lines]

    grid = [[tuple(map(int, value.split())) for value in row] for row in rows]

    return grid

This throws an error because the first value it tries to change into an integer is '(255'. I've tried reworking both functions to get around this, adding a '.replace("(", "").replace(")", "")', and a few others. Each time I seem to cause a new problem. Is there a simple solution with this implementation I'm not seeing? Should I rework it entirely?


Solution

  • The problems lies in this line:

    rows = [line.split(',') for line in lines]
    

    Python can't see a difference between , separating tuples and , separating numbers. You can either:

    1. Change file format using two different separators
    2. Use json module to serialize and deserialize data

    Solution #1 – Different file format.

    You could use for example ; to separate tuples. Then you can parse the line as this:

    # each line is: "(255,255,255);(255,255,255);(255,255,255)"
    rows = [line.split(';') for line in lines]
    
    # Each row is now: ["(255,255,255)", "(255,255,255)", "(255,255,255)"]
    # Parse each tuple:
    rows = [row.strip('()').split(",") for row in rows]
    
    # Each row is now: [["255", "255", "255"], ["255", "255", "255"], ["255", "255", "255"]
    # Finally, you can map ints to str:
    grid = [[tuple(map(int, rgb_tuple)) for rgb_tuple in row] for row in rows]
    
    # Result grid:
    # [
    #     [(255, 255, 255), (255, 255, 255), (255, 255, 255)],
    #     [(255, 255, 255), (255, 255, 255), (255, 255, 255)],
    #     ...
    # ] 
    

    Solution #2 – Use json to serialize and deserialize data

    This is the easiest solution, but it has one drawback: it will turn your tuples into lists.

    # Say your data looks like this:
    # data = [
    #     [(255, 255, 255), (255, 255, 255), (255, 255, 255)],
    #     [(255, 255, 255), (255, 255, 255), (255, 255, 255)],
    #     [(255, 255, 255), (255, 255, 255), (255, 255, 255)],
    # ]
    
    # Serialize data and save to file:
    with open(filename, 'w') as f:
        json.dump(data, f)
    
    
    # Load data from file:
    with open(filename, 'r') as f:
        data = json.load(f)
    
    # Loaded data looks like this:
    # data = [
    #     [[255, 255, 255], [255, 255, 255], [255, 255, 255]],
    #     [[255, 255, 255], [255, 255, 255], [255, 255, 255]],
    #     [[255, 255, 255], [255, 255, 255], [255, 255, 255]],
    # ]
    

    As you can see, tuples were changed into lists.