1

I know that there are plenty of similar topics in StackOverflow but none of the answers solved my specific problem.

First of all, I am trying plot 2-dimensional data points from different classes with scatter command to the figure. The program uses matplotlib events like button_press_event and motion_notify_event and I assume that right after event commands plt.show() should be used. The main problem is that once some tasks are done with events (i.e. labeling), I want to update the whole figure but plt.show() is blocking the option to continue the program. Actually plt.show() should block the program until the user of program decides to go forward. Is there any solution to control this blocking attribute?

I have tried plt.ion(), plt.ioff(), plt.show(block=False), plt.draw(), plt.pause(0.001), global variables with while loop etc. without success. The only way the program works somehow correctly is when all the figures are closed inside of button_press_event when so called forward condition is met but it is not very user-friendly solution if all figures are closed every time data points are updated.

Here is the the glimpse of the code:

def draw_original_figure(random_sample, random_sample_label, random_sample_image, X_train_chosen, y_train_chosen, images_train_chosen, classes, accuracy, embedding, model, h=0.05):
    global points1, im1, s_im1, xybox, x1, y1, fig1, classes1, points_to_plot, y_train_chosen1, accuracy1, random_sample1, embedding1, y_train_chosen1, h1, random_sample_label1, result1
    fig1 = plt.gcf()
        .
        .
        .
    original_figure_plot()
    fig1.canvas.mpl_connect('motion_notify_event', hover)
    fig1.canvas.mpl_connect('button_press_event', click)
    plt.show()

def hover(event):
    # if the mouse is over the scatter points
    if points1.contains(event)[0]:
        # find out the index within the array from the event
        inds = points1.contains(event)[1]["ind"]
        ind = inds[0]
        # get the figure size
        w,h = fig1.get_size_inches()*fig1.dpi
        ws = (event.x > w/2.)*-1 + (event.x <= w/2.) 
        hs = (event.y > h/2.)*-1 + (event.y <= h/2.)
        # if event occurs in the top or right quadrant of the figure,
        # change the annotation box position relative to mouse.
        ab1.xybox = (xybox[0]*ws, xybox[1]*hs)
        # make annotation box visible
        ab1.set_visible(True)
        # place it at the position of the hovered scatter point
        ab1.xy =(x1[ind], y1[ind])
        # set the image corresponding to that point
        im1.set_data(s_im1[ind,:,:])
    else:
        #if the mouse is not over a scatter point
        ab1.set_visible(False)
    fig1.canvas.draw_idle()

def click(event):
    # if the mouse is over the scatter points
    if points1.contains(event)[0]:
        # find out the index within the array from the event
        inds = points1.contains(event)[1]["ind"]
        ind = inds[0]
        # if one specific point is chosen
        if ind == len(x1)-1:
            plt.scatter(x1[ind], y1[ind], s=25, marker='x', c='#556B2F')
            q = question(True, ind)
            # do nothing
            if q == "":
                original_figure_plot()
            # quit the program
            elif q == "q":
                exit()
            # continue the program without updating labels
            elif q == "n":
                result1 = copy.deepcopy(y_train_chosen1)
                plt.close("all")
            # continue the program after labels are updated
            else:
                result1 = copy.deepcopy(y_train_chosen1)
                result1 = np.append(result1, [int(q)], axis=0)
                plt.close("all")

        else:
            # if anyone else point is chosen
            plt.scatter(x1[ind], y1[ind], s=8, c='k')
            q = question(False, ind)
            # do nothing
            if q == "":
                original_figure_plot()
            # quit the program
            elif q == "q":
                exit()
            # update labels
            else:
                y_train_chosen1[ind] = int(q)
                original_figure_plot()
    fig1.canvas.draw_idle()

Probably it is better to use for example other libraries like plotly or dash but is it really true that you cannot update figure without closing it if you are using matplotlib events?? I can provide all the project files but I think so that if there is a solution, it should be done inside of these functions.

T. Holmström
  • 153
  • 1
  • 12

1 Answers1

0

It took the whole day to find the answer but here it is!

I use now plt.show() in interactive-mode with command plt.ion() and do blocking manually with commands fig.canvas.start_event_loop() and fig.canvas.stop_event_loop(). To be honest, it was surprisingly difficult to find the solution to this problem but the lesson is learned.

matplotlib figure does not continue program flow after close event triggered inside tk app

T. Holmström
  • 153
  • 1
  • 12