Saving objects from tk canvas

ghz 14hours ago ⋅ 3 views

I'm trying to make a save function in a program im doing for bubbling/ballooning drawings. The only thing I can't get to work is save a "work copy". As if a drawing gets revision changes, you don't need to redo all the work. Just load the work copy, and add/remove/re-arrage bubbles.

I'm using tkinter and canvas. And creates ovals and text for bubbles. But I can't figure out any good way to save the info from the oval/text objects. I tried to pickle the whole canvas, but that seems like it won't work after some googeling. And pickle every object when created seems to only save the object id. 1, 2 etc. And that also won't work since some bubbles will be moved and receive new coordinates. They might also have a different color, size etc.

In my next approach I'm thinking of saving the whole "can.create_oval( x1, y1, x2, y2, fill = fillC, outli...." as a string to a txt and make the function to recreate a with eval()

Any one have any good suggestion on how to approach this?

Answer

To save the state of your drawings on a tkinter.Canvas and allow for loading and continuing work, you need to serialize the necessary information for each drawable element (e.g., ovals, text, etc.) to a file and then use that data to reconstruct the canvas later.

Here’s how you can approach this:


Step 1: Serialize Canvas Objects

You can extract the attributes of each object on the canvas and save them in a structured format like JSON. JSON is human-readable and easy to work with.

For example:

  • Ovals: Store their bounding box (x1, y1, x2, y2), fill color, outline color, and other properties.
  • Text: Store its position, content, font, color, etc.

Step 2: Save to File

Write the serialized data to a file. You can save it as a .json file for easy reloading.


Step 3: Load from File

When loading the file, parse the JSON data and use it to recreate the objects on the canvas.


Example Implementation

Here’s a simple example:

import tkinter as tk
import json

class BubbleApp:
    def __init__(self, root):
        self.root = root
        self.canvas = tk.Canvas(root, width=800, height=600, bg="white")
        self.canvas.pack()

        self.objects = []  # Store the objects and their properties

        self.create_buttons()

    def create_buttons(self):
        save_btn = tk.Button(self.root, text="Save Work", command=self.save_work)
        save_btn.pack(side="left")
        
        load_btn = tk.Button(self.root, text="Load Work", command=self.load_work)
        load_btn.pack(side="left")
        
        create_btn = tk.Button(self.root, text="Create Bubble", command=self.create_bubble)
        create_btn.pack(side="left")

    def create_bubble(self):
        x1, y1, x2, y2 = 100, 100, 200, 200
        fill = "lightblue"
        outline = "blue"
        
        oval = self.canvas.create_oval(x1, y1, x2, y2, fill=fill, outline=outline)
        text = self.canvas.create_text((x1 + x2) // 2, (y1 + y2) // 2, text="Bubble")
        
        # Save properties for reconstruction
        self.objects.append({
            "type": "oval",
            "coords": (x1, y1, x2, y2),
            "fill": fill,
            "outline": outline,
        })
        self.objects.append({
            "type": "text",
            "coords": ((x1 + x2) // 2, (y1 + y2) // 2),
            "text": "Bubble",
        })

    def save_work(self):
        with open("work_copy.json", "w") as f:
            json.dump(self.objects, f, indent=4)
        print("Work saved to 'work_copy.json'")

    def load_work(self):
        self.canvas.delete("all")  # Clear the canvas
        with open("work_copy.json", "r") as f:
            self.objects = json.load(f)

        # Recreate objects on the canvas
        for obj in self.objects:
            if obj["type"] == "oval":
                self.canvas.create_oval(
                    *obj["coords"],
                    fill=obj["fill"],
                    outline=obj["outline"]
                )
            elif obj["type"] == "text":
                self.canvas.create_text(
                    *obj["coords"],
                    text=obj["text"]
                )
        print("Work loaded from 'work_copy.json'")

# Run the app
root = tk.Tk()
app = BubbleApp(root)
root.mainloop()

Key Points:

  1. Serialization:

    • Store all necessary attributes for each object in a structured format.
    • JSON is a good choice for saving the data.
  2. Reconstruction:

    • Use the saved data to recreate the canvas objects by calling the appropriate canvas.create_* methods.
  3. Extensibility:

    • You can easily extend this approach to support other types of objects (e.g., rectangles, lines) or additional properties like tags or fonts.
  4. No eval():

    • Using eval() to reconstruct objects from strings is dangerous and unnecessary. JSON or another structured format is safer and more robust.

This approach ensures that you can save, load, and modify your canvas drawings seamlessly without relying on unsafe practices.