I created a time series and assigned a cursor to each line so that I could identify the time associated with various behavior in the time series. The first time I made the figure, I selected the periods of interest so that the cursors could serve as "data tips" (this language comes from MATLAB, which is my first coding language and I'm now learning to switch over to Python).
I saved the figures. Now I am reopening them. I want to make adjustments to the data tips I previously set. I need to delete some because I think they need to be moved a bit. I am having a hard time getting the data tips I set originally to be active when I reopen the figure. When I reopen the figure, I can add new data tips and delete those, but I am having a hard time getting the original ones to be active and to delete them.
Attached is an example of a time series with the original data tips that I want to modify. Any help is super super appreciated! :)
Here is my code to reopen the figure. I am trying to get the original data tips to be active, but haven't been able to get it yet:
import os import pickle import matplotlib.pyplot as plt import mplcursors from decimal import Decimal from matplotlib.backend_bases import MouseEvent
def reload_saved_pkl_file(load_folder, file): file_path = os.path.join(load_folder, file) with open(file_path, "rb") as f: fig = pickle.load(f) # Directly load the figure
print(f"Opening: {file}")
# Store cursors for potential modifications
cursors = {}
# List to track existing annotations (annotations should still be in the figure)
existing_annotations = []
# Restore any existing annotations (those that were originally saved in the figure)
for ax in fig.axes:
for artist in ax.texts:
existing_annotations.append(artist)
artist.set_visible(True) # Ensure annotations are visible
# Function to remove an annotation on right-click
def remove_annotation(event):
"""Removes a clicked annotation, including saved ones."""
if isinstance(event, MouseEvent) and event.button == 3: # Right-click
for annotation in existing_annotations:
bbox = annotation.get_window_extent(fig.canvas.get_renderer())
if bbox.contains(event.x, event.y): # Check if click is on the annotation
annotation.remove() # Remove annotation
existing_annotations.remove(annotation) # Remove from list
fig.canvas.draw_idle() # Redraw the figure
return # Exit after removing one annotation
# Function to add an annotation on left-click
def add_cursor(event):
"""Adds a new cursor where the user left-clicks."""
if isinstance(event, MouseEvent) and event.button == 1: # Left-click
for ax in fig.axes:
if ax.contains(event)[0]: # Check if the click is inside the current axis
for line in ax.lines:
if line not in cursors: # Only add a cursor if not already added
cursor = mplcursors.cursor(line, multiple=True, highlight=False, hover=False)
cursor.connect("add", lambda sel, lbl=line.get_label():
sel.annotation.set_text(f"{lbl}\nx: {Decimal(sel.target[0]):.2f}\ny: {sel.target[1]:.2f}"))
# Add the annotation to existing_annotations list
cursor.connect("add", lambda sel: existing_annotations.append(sel.annotation))
cursor.connect("add", remove_annotation) # Allow removal
cursors[line] = cursor # Track the cursor
fig.canvas.draw_idle()
# Restore interactive cursors for existing lines
for ax in fig.axes: # Loop through all axes
for line in ax.lines: # Loop through all lines in each axis
cursor = mplcursors.cursor(line, multiple=True, highlight=False, hover=False)
cursor.connect("add", lambda sel, lbl=line.get_label():
sel.annotation.set_text(f"{lbl}\nx: {Decimal(sel.target[0]):.2f}\ny: {sel.target[1]:.2f}"))
cursor.connect("add", remove_annotation) # Attach right-click delete feature
cursor.connect("add", lambda sel: existing_annotations.append(sel.annotation)) # Track new annotations
cursors[line] = cursor # Track cursor to prevent errors
# Connect mouse events for adding cursors and removing saved ones
fig.canvas.mpl_connect("button_press_event", add_cursor)
# Show the interactive figure
plt.show()
Also here is my code for saving the figures:
import os import pickle import matplotlib.pyplot as plt
def save_figure_as_pkl(fig, save_folder, file): # Ensure the folder exists os.makedirs(save_folder, exist_ok=True)
# Save to pickle file
file_path = os.path.join(save_folder, file)
with open(file_path, "wb") as f:
pickle.dump(fig, f)
print(f"Figure saved to {file_path}")
I have been asking chatGPT for help and looking online for resources.
I did try to save different components of the figure, but I was not able to save the cursors in the pickle file because of an error that said:
TypeError: cannot pickle 'weakref.ReferenceType' object
Here was that code that I tried:
import os import pickle import matplotlib.pyplot as plt import mplcursors
def save_figure_with_annotations_and_cursors(fig, save_folder, file): # Ensure the folder exists os.makedirs(save_folder, exist_ok=True)
# Create lists to store annotations and cursors
annotations = []
cursors = []
# Loop through all axes and save annotations
for ax in fig.axes:
print(f"Checking annotations in axis {ax}")
for artist in ax.texts: # annotations are stored in ax.texts
print(f"Found annotation: {artist.get_text()}")
annotations.append(artist)
# Add cursors associated with this axis
for line in ax.lines:
cursor = mplcursors.cursor(line, multiple=True, highlight=False, hover=False)
cursors.append(cursor) # Store the cursor
# If no annotations are found, print a message
if not annotations:
print("No annotations found.")
if not cursors:
print("No cursors found.")
# Save the figure, annotations, and cursors together
fig_data = {
'figure': fig,
'annotations': annotations,
'cursors': cursors # Store cursors as well
}
# Save to pickle file
file_path = os.path.join(save_folder, file)
with open(file_path, "wb") as f:
pickle.dump(fig_data, f)
print(f"Figure, annotations, and cursors saved to {file_path}")
Example of time series