2

I have the following code (based on Matplotlib plot zooming with scroll wheel). I have tried to embed it into a PySimpleGUI window tab, so far I have not been able to do so.

I managed to make it work with a Tkinter window, but my main GUI is mainly PySImpleGUI.

from matplotlib.pyplot import figure, show 
import numpy import PySimpleGUI as sg 
import matplotlib.pyplot as plt

class ZoomPan:
    def __init__(self):
        self.press = None
        self.cur_xlim = None
        self.cur_ylim = None
        self.x0 = None
        self.y0 = None
        self.x1 = None
        self.y1 = None
        self.xpress = None
        self.ypress = None

    def zoom_factory(self, ax, base_scale = 2.):
        def zoom(event):
            cur_xlim = ax.get_xlim()
            cur_ylim = ax.get_ylim()

            xdata = event.xdata # get event x location
            ydata = event.ydata # get event y location

            if event.button == 'up':
                # deal with zoom in
                scale_factor = 1 / base_scale
            elif event.button == 'down':
                # deal with zoom out
                scale_factor = base_scale
            else:
                # deal with something that should never happen
                scale_factor = 1
                print (event.button)

            new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
            new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

            relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
            rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])

            ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
            ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
            ax.figure.canvas.draw()

        fig = ax.get_figure() # get the figure of interest
        fig.canvas.mpl_connect('scroll_event', zoom)

        return zoom

    def pan_factory(self, ax):
        def onPress(event):
            if event.inaxes != ax: return
            self.cur_xlim = ax.get_xlim()
            self.cur_ylim = ax.get_ylim()
            self.press = self.x0, self.y0, event.xdata, event.ydata
            self.x0, self.y0, self.xpress, self.ypress = self.press

        def onRelease(event):
            self.press = None
            ax.figure.canvas.draw()

        def onMotion(event):
            if self.press is None: return
            if event.inaxes != ax: return
            dx = event.xdata - self.xpress
            dy = event.ydata - self.ypress
            self.cur_xlim -= dx
            self.cur_ylim -= dy
            ax.set_xlim(self.cur_xlim)
            ax.set_ylim(self.cur_ylim)

            ax.figure.canvas.draw()

        fig = ax.get_figure() # get the figure of interest

        # attach the call back
        fig.canvas.mpl_connect('button_press_event',onPress)
        fig.canvas.mpl_connect('button_release_event',onRelease)
        fig.canvas.mpl_connect('motion_notify_event',onMotion)

        #return the function
        return onMotion

"FIGURE"
plt.figure(1) 
fig = plt.gcf()   
ax = fig.add_subplot(111) 
ax.clear()
ax.set_title('') 

"RANDOM PLOT"
x,y,s,c = numpy.random.rand(4,200) s *= 200   
ax.scatter(x,y,s,c, label='label')

"FIGURE CONSTANT"
ax.grid() 
ax.set_xlabel('COORDENADA ESTE', size=15) 
ax.set_ylabel('COORDENADA NORTE', size=15) 
plt.tight_layout(pad=0.05, h_pad=0.05, w_pad=0.05, rect=(0.05,-0.05,0.95,0.95)) 
ax.legend(loc=2, bbox_to_anchor=(-0.15, 1)) 
mng = plt.get_current_fig_manager() 
mng.window.showMaximized()

"PLOT"
zp = ZoomPan() 
figZoom = zp.zoom_factory(ax, base_scale = 1.1) 
figPan= zp.pan_factory(ax) 
show()
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
MBV
  • 591
  • 3
  • 17

1 Answers1

2

You began your plot as you would normally do, then you have to create the actual fig for the pysimplegui window canvas as follows:

def draw_figure(canvas, figure, loc=(0, 0)):
    figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
    figure_canvas_agg.draw()
    figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
    return figure_canvas_agg

such as:

fig_canvas_agg = draw_figure(window['canvas'].TKCanvas, fig)

with fig= plt.figure() and window['canvas']=sg.Canvas(key='canvas') on the window layout for PYSImpleGUI, and then you apply the ZoomPan class as follows:

zp = plot_func()
figZoom = zp.zoom_factory(ax1, base_scale=1.7)
figPan = zp.pan_factory(ax1)
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
MBV
  • 591
  • 3
  • 17
  • 1
    Glad you solved it. There is a demo program that has this function as well as a detailed explanation as to how to integrate your code. It has this "helper" function in it to draw the figure. There are comments like "paste your code here". It's one of several matplotlib integration demos. The most basic is here: https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib.py – Mike from PSG Feb 22 '20 at 21:41
  • Were you able to use the zoom with the figure embedded in the PySimpleGUI window itself or only when the matplotlib window is showing separately from the PySimpleGUI window? – Mike from PSG Feb 22 '20 at 21:47
  • Hi @MikeyB , I was able to use it within the PySimpleGUI canvas, as this figure https://drive.google.com/open?id=1LM8YDx4XIpmGSzXbJrOJMrSyfIskD1Mx. If you need any more information I will be glad to clarify. – MBV Apr 13 '20 at 19:27