0

I have a plot of data points with 2 GUI buttons. I want to click one button and then select points on the plot which will be stored in one array. I then want to click the other button and select additional points in the plot to store in a different array.

My initial attempt was to combine the code provide in these two links:

Select n data points from plot

https://matplotlib.org/3.1.1/gallery/widgets/buttons.html

Clicking the first button and selecting points functions as expected. However, selecting the second button and clicking addition points does not close the connection from the first button.

I've attempted to use .canvas.mpl_diconnect(cid) various ways but to no avail. I cannot seem to figure out how to initialize the cid and pass it through the class.

Below is sample output data from selection, and toy code to replicate.

SAMPLE OUTPUT FROM SELECTION:

# After clicking 'Buy' Button
# data point selections correctly stored in manual_buy array

Select Buy Points
Buy onpick point: ((736551.0, 12.12),)
Buy onpick point: ((736562.0, 12.05),)

# After clicking 'Sell' Button
# data points incorrectly being stored in manual_buy array and manual_sell array

Select Sell Points
Buy onpick point: ((736556.0, 13.02),)
Sell onpick point: ((736556.0, 13.02),)
Buy onpick point: ((736573.0, 13.19),)
Sell onpick point: ((736573.0, 13.19),)

Toy Code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
from datetime import datetime

x = np.arange(0, 10, 1)
y = np.sin(x)

fig_3 = plt.figure()
ax1 = fig_3.add_subplot(111)
plt.subplots_adjust(bottom=0.2)

# Candlestick
ax1.plot(x, y, '-o', picker=1) # 5 points tolerance
ax1.grid(True)
ax1.set_axisbelow(True)
ax1.set_title('Manual Buy / Sell Selection: ', color='white')
ax1.set_facecolor('black')
ax1.figure.set_facecolor('#121212')
ax1.tick_params(axis='x', colors='white')
ax1.tick_params(axis='y', colors='white')

manual_buy = []
manual_sell = []

#https://matplotlib.org/3.1.3/users/event_handling.html

def buy_onpick(event):
    thisline = event.artist
    xdata = thisline.get_xdata()   
    ydata = thisline.get_ydata()
    ind = event.ind
    point = tuple(zip(xdata[ind], ydata[ind]))    
    
    dt = datetime.fromordinal(xdata[ind])
    buy_str = "BUY"
    buy_num = 1
    point_data = np.array([[dt, buy_str, buy_num]])
    manual_buy.append(point_data)
    
    print('Buy onpick point:', point)
    
def sell_onpick(event):
    thisline = event.artist
    xdata = thisline.get_xdata()
    ydata = thisline.get_ydata()
    ind = event.ind
    point = tuple(zip(xdata[ind], ydata[ind]))
    
    dt = datetime.fromordinal(xdata[ind])
    sell_str = "SELL"
    sell_num = 0.5
    point_data = np.array([[dt, sell_str, sell_num]])
    manual_buy.append(point_data)
    
    print('Sell onpick point:', point)
      
class Buy_Sell:

    def Buy(self, event):
        print('\nSelect Buy Points')
        fig_3.canvas.mpl_connect('pick_event', buy_onpick)

    def Sell(self, event):
        print('\nSelect Sell Points')
        fig_3.canvas.mpl_connect('pick_event', sell_onpick)
        
    def Save(self, event):
        print('Would have saved data')  
        #Add functionality to save data 
        print('Saved')
        
callback = Buy_Sell()
axbuy = plt.axes([0.59, 0.05, 0.1, 0.075])
axsell = plt.axes([0.7, 0.05, 0.1, 0.075])
axsave = plt.axes([0.81, 0.05, 0.1, 0.075])
bbuy = Button(axbuy, 'Buy')
bbuy.on_clicked(callback.Buy)
bsell = Button(axsell, 'Sell')
bsell.on_clicked(callback.Sell)
bsave = Button(axsave, 'Save')
bsave.on_clicked(callback.Save)
BobDon
  • 3
  • 2

1 Answers1

0

You should switch modes, which is not in code. Pls see folowing code:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
from datetime import datetime

class Buy_Sell:

    def __init__(self, fig, x, y):
        self.fig  = fig
        self.ax = fig.axes[0]
        self.x = x
        self.y = y
        
        self.ax.plot(x, y, '-o', picker=1) # 5 points tolerance
        self.ax.grid(True)
        self.ax.set_axisbelow(True)
        self.ax.set_title('Manual Buy / Sell Selection: ', color='white')
        self.ax.set_facecolor('black')
        self.ax.figure.set_facecolor('#121212')
        self.ax.tick_params(axis='x', colors='white')
        self.ax.tick_params(axis='y', colors='white')
        
        self.manual_buy = []
        self.manual_sell = []
        
        self.mode = 'buy'
                
        self.pick1 = self.fig.canvas.mpl_connect('pick_event', buy_onpick)
        self.pick2 = self.fig.canvas.mpl_connect('pick_event', sell_onpick)


        self.fig.canvas.draw()


    def buy_onpick(self, event):
        print(self.mode)
        if self.mode == 'buy':
            thisline = event.artist
            xdata = thisline.get_xdata()   
            ydata = thisline.get_ydata()
            ind = event.ind
            point = tuple(zip(xdata[ind], ydata[ind]))    

            dt = datetime.fromordinal(xdata[ind])
            buy_str = "BUY"
            buy_num = 1
            point_data = np.array([[dt, buy_str, buy_num]])
            self.manual_buy.append(point_data)

            print('Buy onpick point:', point)
        
    def sell_onpick(self, event):
        print(self.mode)
        if self.mode == 'sell':
            thisline = event.artist
            xdata = thisline.get_xdata()
            ydata = thisline.get_ydata()
            ind = event.ind
            point = tuple(zip(xdata[ind], ydata[ind]))

            dt = datetime.fromordinal(xdata[ind])
            sell_str = "SELL"
            sell_num = 0.5
            point_data = np.array([[dt, sell_str, sell_num]])
            self.manual_sell.append(point_data)

            print('Sell onpick point:', point)
        
    def Save(self, event):
        print('Would have saved data')  
        #Add functionality to save data 
        print('Saved')
        
    def mode_buy(self, event):
        self.mode = 'buy'
        print(self.mode)
        print('entering buy mode')
        self.pick1 = self.fig.canvas.mpl_connect('pick_event', buy_onpick)
        self.fig.canvas.mpl_disconnect(self.pick2)

        
    def mode_sell(self, event):
        self.mode = 'sell'
        print(self.mode)
        print('entering sell mode')
        self.pick2 = self.fig.canvas.mpl_connect('pick_event', sell_onpick)
        self.fig.canvas.mpl_disconnect(self.pick1)

        
    def mode_save(self, event):
        self.mode = 'save'
        print('entering save mode')
      

    
x = np.arange(0, 10, 1)
y = np.sin(x)

fig = plt.figure()
ax1 = fig.add_subplot(111)
plt.subplots_adjust(bottom=0.2)

callback = Buy_Sell(fig, x, y)
axbuy = plt.axes([0.59, 0.05, 0.1, 0.075])
axsell = plt.axes([0.7, 0.05, 0.1, 0.075])
axsave = plt.axes([0.81, 0.05, 0.1, 0.075])
bbuy = Button(axbuy, 'Buy')
bbuy.on_clicked(callback.mode_buy)
bsell = Button(axsell, 'Sell')
bsell.on_clicked(callback.mode_sell)
bsave = Button(axsave, 'Save')
bsave.on_clicked(callback.mode_save)

Key points:

  • Create function calls to disconnect buy and sell mode.
  • buy_onpick and sell_onpick only do picking data stuff, switching mode is managed by your button callback functions, in this example are mode_buy and mode_sell.
Jiadong
  • 1,822
  • 1
  • 17
  • 37