anntzer / mplcursors

Interactive data selection cursors for Matplotlib.
https://mplcursors.readthedocs.io
zlib License
114 stars 20 forks source link

Hide or remove bbox annotations when mouse hover out of the graph #82

Closed ntahlahweh closed 2 months ago

ntahlahweh commented 2 months ago

I am currently using matplotlib to plot the line graph and mplcursors to annotate the line graph. My current problem right now is how to hide the bbox annotation everytime the mouse is hover outside of the graph?

here is the snippet of my code:

def update_plot(self):
    if not self.csv_initialized:
        print("CSV not yet initialized; skipping plot update.")
        return

    if self.monitoring_complete:  # Check if monitoring is complete
        print("Monitoring is complete; stopping plot updates.")
        return

    file_path = os.path.join(cwd, 'analysis\\monitoring\\CableSense_' + self.save_time + '.csv')
    print(f"Attempting to read data from: {file_path}")

    if not os.path.exists(file_path):
        print(f"File not found: {file_path}")
        return

    try:
        df = pd.read_csv(file_path)
        print(f"Data read from CSV:\n{df.head()}")
        self.ax.clear()

        if 'Time' not in df.columns:
            print("Error: 'Time' column not found in CSV data.")
            return

        # Convert Time column to datetime
        df['Time'] = pd.to_datetime(df['Time'], format='%H:%M:%S')

        for column in df.columns:
            if column != 'Time':
                line, = self.ax.plot(df['Time'], df[column], label=column)
                # Add label to the end of the line
                self.ax.annotate(column, xy=(df['Time'].iloc[-1], df[column].iloc[-1]),
                                xytext=(5, 0), textcoords='offset points', color=line.get_color())

        self.ax.set_title('Inverter IR Values Over Time')
        self.ax.set_xlabel('Time')
        self.ax.set_ylabel('IR Value')

        # Format the x-axis to show time properly
        self.ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
        self.ax.xaxis.set_major_locator(mdates.AutoDateLocator())

        self.ax.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size': 6})
        self.fig.autofmt_xdate()  # Rotate date labels
        self.canvas.draw()

        # Enable hovering annotations with mplcursors
        cursor = mplcursors.cursor(self.ax, hover=True)

        # Customize the annotations displayed on hover
        @cursor.connect("add")
        def on_add(sel):
            # Customize the hover text
            sel.annotation.set_text(f"{sel.artist.get_label()}\n{sel.target[1]:.2f}")
            sel.annotation.get_bbox_patch().set(fc="white", alpha=0.8)

    except Exception as e:
        print(f"Failed to read or update plot: {e}")

    self.schedule_task(60000, self.update_plot)  # Schedule next update

and this is how it looks like currently, the reason why it has two annotation is because the graph is constantly update every minute. So everytime user hover their mouse out of the graph the annotation is still there.

image

anntzer commented 2 months ago

I suspect you can set annotation_clip property on the Annotation object for that purpose. Unfortunately your example is not reproducible...