PySimpleGUI中的交互式matplotlib图

问题描述

我正在尝试使RectangleSelector表单matplotlib.widgets与PySimpleGUI一起使用。 我将测试代码基于接受的答案on this question显示的RectangleSelector演示。

我正在PySimpleGUI中显示该图,但它不是交互式的。在PySimpleGUI中甚至有可能具有交互式matplotlib小部件?

import PySimpleGUI as sg
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import figureCanvasTkAgg
from matplotlib.widgets  import RectangleSelector
import matplotlib
matplotlib.use('TkAgg')

xdata = np.linspace(0,9*np.pi,num=301)
ydata = np.sin(xdata)

fig,ax = plt.subplots()
line,= ax.plot(xdata,ydata)


def draw_figure(canvas,figure):
    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


def line_select_callback(eclick,erelease):
    x1,y1 = eclick.xdata,eclick.ydata
    x2,y2 = erelease.xdata,erelease.ydata

    rect = plt.Rectangle( (min(x1,x2),min(y1,y2)),np.abs(x1-x2),np.abs(y1-y2) )
    ax.add_patch(rect)


rs = RectangleSelector(ax,line_select_callback,drawtype='Box',useblit=False,button=[1],minspanx=5,minspany=5,spancoords='pixels',interactive=True)
                       

layout = [[sg.Canvas(key="-CANVAS-")]]

window = sg.Window('test',layout,finalize=True,element_justification='center',font='Helvetica 16')
draw_figure(window["-CANVAS-"].TKCanvas,fig)
event,values = window.read()

编辑:感谢MikeyB的指针,我现在有了以下代码,该代码显示一个交互式绘图,但是仍然无法绘制矩形。回调函数似乎没有触发。下面的新代码

import PySimpleGUI as sg
import numpy as np
from matplotlib.widgets  import RectangleSelector
import matplotlib.figure as figure
from matplotlib.backends.backend_tkagg import figureCanvasTkAgg,NavigationToolbar2Tk

# instantiate matplotlib figure
fig = figure.figure()
ax = fig.add_subplot(111)
DPI = fig.get_dpi()
fig.set_size_inches(505 * 2 / float(DPI),707 / float(DPI))

# ------------------------------- This is to include a matplotlib figure in a Tkinter canvas
def draw_figure_w_toolbar(canvas,fig,canvas_toolbar):
    if canvas.children:
        for child in canvas.winfo_children():
            child.destroy()
    if canvas_toolbar.children:
        for child in canvas_toolbar.winfo_children():
            child.destroy()
    figure_canvas_agg = figureCanvasTkAgg(fig,master=canvas)
    figure_canvas_agg.draw()
    toolbar = Toolbar(figure_canvas_agg,canvas_toolbar)
    toolbar.update()
    figure_canvas_agg.get_tk_widget().pack(side='right',fill='both',expand=1)


def line_select_callback(eclick,np.abs(y1-y2) )
    print(rect)
    ax.add_patch(rect)


class Toolbar(NavigationToolbar2Tk):
    def __init__(self,*args,**kwargs):
        super(Toolbar,self).__init__(*args,**kwargs)


# ------------------------------- PySimpleGUI CODE

layout = [
    [sg.B('start',key='start')],[sg.Canvas(key='controls_cv')],[sg.Column(
        layout=[
            [sg.Canvas(key='fig_cv',# it's important that you set this size
                       size=(500 * 2,700)
                       )]
        ],background_color='#DAE0E6',pad=(0,0)
    )],]

window = sg.Window('Test',layout)

while True:
    event,values = window.read()
    print(event,values)
    if event == sg.WIN_CLOSED:
        break
    elif event == 'start':
        x = np.linspace(0,2 * np.pi)
        y = np.sin(x)
        line,= ax.plot(x,y)
        rs = RectangleSelector(ax,interactive=True)
        draw_figure_w_toolbar(window['fig_cv'].TKCanvas,window['controls_cv'].TKCanvas)

window.close()

解决方法

在PySimpleGUI中甚至有可能具有交互式matplotlib小部件吗?

是的

项目GitHub上的演示程序展示了如何制作交互式Matplotlib绘图。

https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Matplotlib_Embedded_Toolbar.py

您需要将控件嵌入到窗口中。

,

您需要添加

    fig.canvas.draw()

如果您希望在回调触发后更新绘图,请转到您的回调函数!

这是您的代码的更新版本,可以正常工作:

import PySimpleGUI as sg
import numpy as np
from matplotlib.widgets  import RectangleSelector
import matplotlib.figure as figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
import matplotlib.pyplot as plt

# instantiate matplotlib figure
fig = figure.Figure()
ax = fig.add_subplot(111)
DPI = fig.get_dpi()
fig.set_size_inches(505 * 2 / float(DPI),707 / float(DPI))

# ------------------------------- This is to include a matplotlib figure in a Tkinter canvas
def draw_figure_w_toolbar(canvas,fig,canvas_toolbar):
    if canvas.children:
        for child in canvas.winfo_children():
            child.destroy()
    if canvas_toolbar.children:
        for child in canvas_toolbar.winfo_children():
            child.destroy()
    figure_canvas_agg = FigureCanvasTkAgg(fig,master=canvas)
    figure_canvas_agg.draw()
    toolbar = Toolbar(figure_canvas_agg,canvas_toolbar)
    toolbar.update()
    figure_canvas_agg.get_tk_widget().pack(side='right',fill='both',expand=1)


def line_select_callback(eclick,erelease):
    x1,y1 = eclick.xdata,eclick.ydata
    x2,y2 = erelease.xdata,erelease.ydata

    rect = plt.Rectangle( (min(x1,x2),min(y1,y2)),np.abs(x1-x2),np.abs(y1-y2) )
    print(rect)
    ax.add_patch(rect)
    fig.canvas.draw()

class Toolbar(NavigationToolbar2Tk):
    def __init__(self,*args,**kwargs):
        super(Toolbar,self).__init__(*args,**kwargs)


# ------------------------------- PySimpleGUI CODE

layout = [
    [sg.B('start',key='start')],[sg.Canvas(key='controls_cv')],[sg.Column(
        layout=[
            [sg.Canvas(key='fig_cv',# it's important that you set this size
                       size=(500 * 2,700)
                       )]
        ],background_color='#DAE0E6',pad=(0,0)
    )],]

window = sg.Window('Test',layout)

while True:
    event,values = window.read()
    print(event,values)
    if event == sg.WIN_CLOSED:
        break
    elif event == 'start':
        x = np.linspace(0,2 * np.pi)
        y = np.sin(x)
        line,= ax.plot(x,y)
        rs = RectangleSelector(ax,line_select_callback,drawtype='box',useblit=False,button=[1],minspanx=5,minspany=5,spancoords='pixels',interactive=True)
        draw_figure_w_toolbar(window['fig_cv'].TKCanvas,window['controls_cv'].TKCanvas)

window.close()