Matplotlib figures gets bigger when updated in PySimpleGUI - python

I'm new in PySimpleGui and I want to plot a function which takes a variable in a slider. My problem is that when I update the value of the variable through the slider, the plot window becomes bigger each time. Any solution for that?
Here is my minimal working code:
import numpy as np
import matplotlib.pyplot as plt
import PySimpleGUI as sg
import math
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,\
NavigationToolbar2Tk
def fun(nu):
x=np.arange(1,10,0.1)
plt.plot(x,nu*x)
return plt.gcf()
sg.theme('SandyBeach')
layout = [
[sg.Text('nu_min', size =(15, 1)), sg.InputText(0.2,key='-nu_min-')],
[sg.Text('nu_max', size =(15, 1)), sg.InputText(1,key='-nu_max-')],
[sg.Text('nu_step', size =(15, 1)), sg.InputText(0.1,key='-nu_step-')],
[sg.Submit('Run'), sg.Cancel()]
]
window = sg.Window('Simple data entry window', layout)
event, values = window.read()
window.close()
nu_min=float(values['-nu_min-'])
nu_max=float(values['-nu_max-'])
nu_step=float(values['-nu_step-'])
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 delete_figure_agg(figure_agg):
figure_agg.get_tk_widget().forget()
plt.close('all')
figure_w, figure_h = 400, 400
layout = [[sg.Text('Graph Element Combined with Math!', justification='center', relief=sg.RELIEF_SUNKEN, expand_x=True, font='Courier 18')],
[sg.Button('Plot'), sg.Cancel(), sg.Button('Popup')],
[sg.Canvas(size=(figure_w, figure_h), key='-CANVAS-')],
[sg.Text('nu', font='Courier 14'), sg.Slider((nu_min,nu_max),default_value=nu_min,resolution=nu_step, orientation='h', enable_events=True, key='-slider_nu-', expand_x=True)]]
window = sg.Window('', layout, finalize=True)
figure_agg = None
while True:
event, values = window.read()
if figure_agg:
# ** IMPORTANT ** Clean up previous drawing before drawing again
delete_figure_agg(figure_agg)
fig=fun(values['-slider_nu-'])
figure_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig)
window.close()

I ran your code, and see that your Y values shrink and grow as the slider is changed. Is this what you mean when you say, "the plot window becomes bigger..."
If so, try setting the Xlim and Ylim values to limit your plot values. When I inserted this into your code, the Y values are fixed at 10, and the plot updates as I move the slider.
def fun(nu):
x=np.arange(1,10,0.1)
plt.xlim(10)
plt.ylim(10)
plt.plot(x,nu*x)
return plt.gcf()

Related

PySimpleGUI with slider and DataFrame

I am trying to make a simple interactive plot, where a value of single variable can be changed by the slider. I already have non-interactive code which is working, where I use pandas DataFrame. I am using PySimpleGUI, to which I am completely new. I have managed to get a window with the slider, but no plot which changes. Where do I do an error in my code?
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import copy as copy
import PySimpleGUI as sg
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
V_A=5
dfN_A=np.array([[-1, 1], [0, 3], [1, -1]])
dfN=copy.deepcopy(dfN_A)
dfN[:,-1]=dfN_A[:,-1]*V_A
DC = pd.DataFrame(dfN,
columns=['X','value'])
_VARS = {'window': False,
'fig_agg': False,
'pltFig': False,
'V_A': 5}
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
AppFont = 'Any 16'
SliderFont = 'Any 14'
sg.theme('black')
layout = [[sg.Canvas(key='figCanvas', background_color='#FDF6E3')],
# pad ((left, right), (top, bottom))
[sg.Text(text="V_A :",
font=SliderFont,
background_color='#FDF6E3',
pad=((0, 0), (10, 0)),
text_color='Black'),
sg.Slider(range=(-10,10), size=(60, 10),
orientation='h', key='-SLIDERV_A-')],
[sg.Button('Exit', font=AppFont, pad=((540, 0), (0, 0)))]]
_VARS['window'] = sg.Window('Simple GUI', layout, finalize=True,resizable=True, element_justification='center', font='Helvetica 18')
def drawChart():
_VARS['pltFig'] = plt.figure()
fig = plt.gcf()
DC.plot(y='value',x='X')
_VARS['fig_agg'] = draw_figure(
_VARS['window']['figCanvas'].TKCanvas, _VARS['pltFig'])
def updateChart():
_VARS['fig_agg'].get_tk_widget().forget()
plt.cla()
plt.clf()
_VARS['fig_agg'] = draw_figure(
_VARS['window']['figCanvas'].TKCanvas, _VARS['pltFig'])
def updateDataV_A(val):
_VARS['V_A'] = val
updateChart()
drawChart()
while True:
event, values = _VARS['window'].read(timeout=2000)
if event == sg.WIN_CLOSED or event == 'Exit':
break
elif event == '-SliderV_A-':
updateDataV_A(int(values['-SliderV_A-']))
_VARS['window'].close()
There're some issues in your code. Try not to say much about those issues, for easily, so a different code here for you.
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
# 1. Define the class as the interface between matplotlib and PySimpleGUI
class Canvas(FigureCanvasTkAgg):
"""
Create a canvas for matplotlib pyplot under tkinter/PySimpleGUI canvas
"""
def __init__(self, figure=None, master=None):
super().__init__(figure=figure, master=master)
self.canvas = self.get_tk_widget()
self.canvas.pack(side='top', fill='both', expand=1)
# 2. Define a function to draw the figure
def plot_figure(var):
ax.cla()
ax.plot(x0, y0*var)
canvas.draw() # Rendor figure into canvas
# 3. Initial Values
var = 5
x0, y0 = np.array([-1, 0, 1]), np.array([1, 3, -1])
# 4. create PySimpleGUI window
sg.theme('DarkBlue3')
layout = [
[sg.Canvas(size=(640, 480), key='Canvas')],
[sg.Text(text="Var"),
sg.Slider(range=(-10, 10), default_value=var, size=(10, 20), expand_x=True, enable_events=True, orientation='h', key='Slider')],
[sg.Push(), sg.Button('Exit'), sg.Push()],
]
window = sg.Window('Simple GUI', layout, finalize=True, resizable=True)
# 5. Create a matplotlib canvas under sg.Canvas or sg.Graph
fig = Figure()
ax = fig.add_subplot()
canvas = Canvas(fig, window['Canvas'].Widget)
# 6. initial for figure
plot_figure(var)
# 7. PySimpleGUI event loop
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Slider':
var = values[event]
plot_figure(var)
# 8. Close window to exit
window.close()

Refreshing Figure (Bar chart) in Canvas

I was writing a code that Ploting variable input into a Bar Chart. the idea is: on each click, the code reads its input and refresh the ploted Bar chart to a new one, like the values increases on each click. here is the output:
enter image description here
After the second click (which is the supposed the second test), the canvas fiure doesn't refresh, but it adds another figure below.
enter image description here
How can keep display all the results in the same figure, which mean refreshing the same figure each time I click. I tried the creat a refresh function ,and I used delete(), clear()... and nothing is work properly (or may be I didn't use them right).
import time
import PySimpleGUI as sg
import pandas as pd
import tkinter as tk
from matplotlib.ticker import NullFormatter
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import *
import matplotlib
import csv
from protoHODReader import main
from result import TestResult
matplotlib.use('TkAgg')
SW_ToT = 0 #my_variable
SW_OK = 0 #my_variable
SW_NOK = 0 #my_variable
test_result = TestResult.get_instance() #the input from another py code.
def plot_bar(plt_):#creating the plot
plt_.clf()
bar_values = (test_result.sw_total, test_result.sw_ok, test_result.sw_nok)
ind = np.arange(len(bar_values))
width = 0.4
p1 = plt_.bar(ind, bar_values, width)
plt_.ylabel('Quantity')
plt_.title('Test Results')
plt_.xticks(ind, ('Number Total of SW', 'SW oK', 'SW NOK'))
plt_.yticks(np.arange(0, 81, 10))
plt_.legend((p1[0],), ('Data Group 1',))
plt_.gcf()
def draw_figure(canvas, figure):#creating the 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 refresh(plt_, window_):#my attempt_to_refresh
plot_bar(plt_)
fig_ = plt_.gcf()
figure_x_, figure_y_, figure_w_, figure_h_ = fig_.bbox.bounds
fig_photo_ = draw_figure(window_['-CANVAS-'].TKCanvas, fig_)
canvas_bar = fig_photo_
#fig = matplotlib.figure.Figure(figsize=(5, 4))
plot_bar(plt)
sg.theme('DarkAmber')
fig = plt.gcf()
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds
layout = [[sg.Text('test', font='Any 18')],
[sg.Text('Please Scan the SW ID', size=(20, 1)), sg.InputText(key='PN'), sg.Button("LOAD")],
[sg.Canvas(size=(figure_w, figure_h), key='-CANVAS-')],
[sg.Button("Protoreader", size=(12, 3)), sg.Button("Print QR", size=(6, 3)), sg.Push(),
sg.Exit(size=(6, 3))]]
window = sg.Window('Local Application', layout, force_toplevel=True, finalize=True)
#fig_photo = draw_figure(window['-CANVAS-'].TKCanvas, fig)
event, values = window.read()
while True:
event, values = window.read()
if event == 'Exit' or event == sg.WIN_CLOSED:
break
# if event == 'LOAD':
if event == 'Protoreader':
exec(open('protoreader.py').read())
print(test_result.sw_nok)
print(test_result.sw_ok)
test_result.sw_total = test_result.sw_nok + test_result.sw_ok
print(test_result.`enter code here`sw_total)
refresh(plt, window)
test_result.saveAsCsv()
window.close()

Delete the canvas in Pysimplegui

I'm new to programming, and I tried to find the solution to related questions here, but it get me nowhere and I'm starting to bang the walls with my head now.
The problem is the following: I need to create a program with GUI for a university project. The idea is that I take data from big datasets, then a user can input a country name, and data for the selected countries will form a plot. I have 2 plots with different data I want to use, and I have 2 separate buttons for each type:
```
#function that graphs the first plot
def vaccine_cases_plot(vaccine_list, cases_list):
plt.scatter(
y = vaccine_list,
x = cases_list)
plt.ylabel("Percentage of fully vaccinated people")
plt.xlabel("Number of new cases per Million as of 01.12.2021")
plt.ioff()
return plt.gcf()
#function that graphs the second plot
def vaccine_gdp_plot(vaccine_list, gdp_list):
plt.scatter(
y = vaccine_list,
x = gdp_list)
plt.ylabel("Percentage of fully vaccinated people")
plt.xlabel("GDP per capita in 2019(USD)")
plt.ioff()
return plt.gcf()
#helper function to display plot on canvas
plt.gcf().subplots_adjust(left=0.15)
matplotlib.use("TkAgg")
def draw_figure(canvas, figure):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.get_tk_widget().pack(side="right", fill="both", expand=1)
figure_canvas_agg.draw()
return figure_canvas_agg
#empty lists where data for countries, selected by user, will be stored
vaccines_to_plot = []
cases_to_plot = []
gdp_to_plot = []
#pysimplegui interface layout
interface_column = [
[
sg.Text("Select a countries you would like to see on the graph:", font=("Arial", 14))
],
[
sg.In(size =(25, 1), enable_events=True, key="country_selected"),
sg.Button("Add country")
],
[
sg.Text("Selected countries are: ", size=(20,10), font=("Arial", 14), key = "selected_countries")
],
[
sg.Button("Clear selection", key = "clear")
],
[
sg.Button("GDP vs. Vaccination rate", key = "gdp_vaccine"),
sg.Button("Vaccination vs. new cases per day", key = "vaccine_cases")
],
[
sg.Text(font=("Arial", 12), key="warning_message")
]
]
graph_column = [
[sg.Canvas(size=(500,500), key="canvas")]
]
layout = [
[
sg.Column(interface_column),
sg.VSeperator(),
sg.Column(graph_column),
]
]
window = sg.Window("Vaxxi-nation", layout, margins=(50, 50), finalize=True)
canv = window["canvas"].TKCanvas
#main loop
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
#User inputs name of the country, respective data goes to the lists, name of the country is
displayed
if event == "Add country":
selected_country = values["country_selected"]
if selected_country in new_cases_per_m and selected_country in fully_vaccinated and
selected_country in countries_gdp:
try:
vaccines_to_plot.append(float(fully_vaccinated[selected_country]))
cases_to_plot.append(float(new_cases_per_m[selected_country]))
gdp_to_plot.append(round(float(countries_gdp[selected_country]), 2))
display = window["selected_countries"]
display.update(display.get() + selected_country + '\n')
except:
window["warning_message"].update("One of the datasets doesn't have data for this country")
else:
window["warning_message"].update("One of the datasets doesn't have data for this country")
#Button to graph the first plot
if event == "gdp_vaccine":
draw_figure(canv, vaccine_gdp_plot(vaccines_to_plot, gdp_to_plot))
#button to graph the second plot
if event == "vaccine_cases":
draw_figure(canv, vaccine_cases_plot(vaccines_to_plot, cases_to_plot))
if event == "clear":
display.update('')
vaccines_to_plot = []
cases_to_plot = []
gdp_to_plot = []
event, values = window.read()
window.close()
```
Now, when I press one of the buttons for the first time, the plot displays on canvas just as I want it to be. But if I'd like to display the other plot, instead of being rewritten over the previous one, it creates a new plot, replaces the old one with it, and makes a copy of a new plot on the right.
What I want to have, is when a new button is pressed (or the old one is pressed again) the new plot should replace the old one. I looked up for many ways to do that, but I have a feeling that there should be something simple and obvious that I fail to see here. The last thing I tried and gave up after was to delete the canvas each time the button is pressed and then draw it anew. I tried to do it like this:
#first button is clicked
if event == "gdp_vaccine":
canv.TKCanvas.delete("all")
draw_figure(canv, vaccine_gdp_plot(vaccines_to_plot, gdp_to_plot))
But it literally did nothing (although no error was thrown). I will really appreciate any help here since I've been struggling with it for the whole day already and there's no way this thing should be that complicated.
Following code just to delete all items in sg.Canvas, not canvas of matplotlib figure in sg.Canvas.
canv.TKCanvas.delete("all")
It is not easy to read your long and incomplete code.
Here's my code to demo two graphs and redraw again and again, maybe it can help you.
import math
from matplotlib import use as use_agg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import PySimpleGUI as sg
def pack_figure(graph, figure):
canvas = FigureCanvasTkAgg(figure, graph.Widget)
plot_widget = canvas.get_tk_widget()
plot_widget.pack(side='top', fill='both', expand=1)
return plot_widget
def plot_figure(index, theta):
fig = plt.figure(index) # Active an existing figure
ax = plt.gca() # Get the current axes
x = [degree for degree in range(1080)]
y = [math.sin((degree+theta)/180*math.pi) for degree in range(1080)]
ax.cla() # Clear the current axes
ax.set_title(f"Sensor Data {index}")
ax.set_xlabel("X axis")
ax.set_ylabel("Y axis")
ax.set_xscale('log')
ax.grid()
plt.plot(x, y) # Plot y versus x as lines and/or markers
fig.canvas.draw() # Rendor figure into canvas
# Use Tkinter Agg
use_agg('TkAgg')
layout = [[sg.Graph((640, 480), (0, 0), (640, 480), key='Graph1'), sg.Graph((640, 480), (0, 0), (640, 480), key='Graph2')]]
window = sg.Window('Matplotlib', layout, finalize=True)
# Initial
graph1 = window['Graph1']
graph2 = window['Graph2']
plt.ioff() # Turn the interactive mode off
fig1 = plt.figure(1) # Create a new figure
ax1 = plt.subplot(111) # Add a subplot to the current figure.
fig2 = plt.figure(2) # Create a new figure
ax2 = plt.subplot(111) # Add a subplot to the current figure.
pack_figure(graph1, fig1) # Pack figure under graph
pack_figure(graph2, fig2)
theta1 = 0 # theta for fig1
theta2 = 90 # theta for fig2
plot_figure(1, theta1)
plot_figure(2, theta2)
while True:
event, values = window.read(timeout=10)
if event == sg.WINDOW_CLOSED:
break
elif event == sg.TIMEOUT_EVENT:
theta1 = (theta1 + 40) % 360
plot_figure(1, theta1)
theta2 = (theta2 + 40) % 260
plot_figure(2, theta2)
window.close()

How to resize PySimpleGUI canvas with matplotlib

I'm trying to change the size of my bar chart and having some difficulty. The bars are plotting correctly from my data, but the canvas does not increase in size when I adjust the size argument. I'm not getting any errors, so I'm not sure what I'm missing.
# define the window layout
layoutMain = [[sg.Text('Member Access')],
[sg.Menu(mainmenu_def, pad=(0,0))],
[sg.Button('Store To Inventory', size = (17,1)), sg.Button('Retrieve From Inventory', size = (17,1)), sg.Button('Inventory Details', size = (17,1)), sg.Button('Exit to Home', button_color = '#36454f', size = (17,1))],
[sg.Text('E-Stock', font='Any 18')],
[sg.Canvas(size=(100, 100), key='-CANVAS-')]]
windowMain = sg.Window('E-Stock', layoutMain, no_titlebar=False, size=(1000,600), finalize=True, resizable=True)
windowMain.maximize()
# add the plot to the window
fig_photo = draw_figure(windowMain['-CANVAS-'].TKCanvas, fig)
Here's example, I define the size of sg.Canvas by the figsize and dpi in matplolib figure.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
matplotlib.use('TkAgg')
w, h = figsize = (5, 3) # figure size
fig = matplotlib.figure.Figure(figsize=figsize)
dpi = fig.get_dpi()
size = (w*dpi, h*dpi) # canvas size
t = np.arange(0, 3, .01)
fig.add_subplot(111).plot(t, 2 * np.sin(2 * np.pi * t))
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
layout = [[sg.Text('Plot test')],
[sg.Canvas(size=size, key='-CANVAS-')],
[sg.Button('Ok')]]
window = sg.Window('Embedding Matplotlib', layout, finalize=True, element_justification='center', font='Helvetica 18')
fig_canvas_agg = draw_figure(window['-CANVAS-'].TKCanvas, fig)
event, values = window.read()
window.close()

New Graph not getting displayed on Canvas PySimpleGUI and Matplotlib

I need to reset the graph based on two buttons. It displays the first graph if button1 pressed but no the second one after pressing button2.
First graph is shown properly but need to clear the canvas
Code below:
import matplotlib.pyplot as plt
import matplotlib
import numpy
import numpy as np
import PySimpleGUI as psg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')
def make_graph_and_put_on_canvas(x, y, xlabel, ylabel, graph_title, canvas):
figure,ax=plt.subplots()
ax.plot(x, y)
ax.set(xlabel=xlabel, ylabel=ylabel,
title=graph_title)
ax.grid()
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
if __name__ == '__main__':
layout = [[psg.B("Button1"),psg.B("Button2")],[psg.Canvas(key="canvas")]]
Graph = psg.Window(title="Graph", layout=layout, size=(500, 500))
while (True):
event, Value = Graph.read()
if event == psg.WINDOW_CLOSED:
Graph.close()
break
if event=="Button1":
#Make the first graph for y=2x
x=[0,1,2,3]
y=[0,2,4,6]
make_graph_and_put_on_canvas(x, y, "x", "y", "title",Graph["canvas"].TKCanvas)
if event=="Button2":
# Make the first graph for y=3x
x = [0, 1, 2, 3]
y = [0, 3, 6, 9]
make_graph_and_put_on_canvas(x, y, "x", "y", "title",Graph["canvas"].TKCanvas)
FigureCanvasTkAgg(figure, canvas) will create a new canvas in Graph["canvas"].TKCanvas.
With option side='top' when pack, you will get the new canvas from top to bottom of Graph["canvas"].TKCanvas. Second Button did make second graph but on bottom of your window, invisible for size = (500, 500) in your window.
call following code once if you want new graph show on same figure,
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
clear figure or axis by
clf() # clear figure
cla() # clear axis
update figure on canvas after all plots on figure done
figure_canvas_agg.draw()
Following code just work, not optimized.
import matplotlib.pyplot as plt
import matplotlib
import numpy
import numpy as np
import PySimpleGUI as psg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')
def make_graph_and_put_on_canvas(x, y, xlabel, ylabel, graph_title, canvas):
ax.cla()
ax.plot(x, y)
ax.set(xlabel=xlabel, ylabel=ylabel, title=graph_title)
ax.grid()
figure_canvas_agg.draw()
return figure_canvas_agg
if __name__ == '__main__':
layout = [[psg.B("Button1"),psg.B("Button2")],[psg.Canvas(key="canvas")]]
Graph = psg.Window(title="Graph", layout=layout, size=(500, 500), finalize=True)
figure, ax = plt.subplots()
canvas = Graph["canvas"].Widget
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
while (True):
event, Value = Graph.read()
if event == psg.WINDOW_CLOSED:
break
if event=="Button1":
# Make the first graph for y=2x
x=[0,1,2,3]
y=[0,2,4,6]
make_graph_and_put_on_canvas(x, y, "x", "y", "title1", canvas)
if event=="Button2":
# Make the first graph for y=3x
x = [0, 1, 2, 3]
y = [0, 3, 6, 9]
make_graph_and_put_on_canvas(x, y, "x", "y", "title2", canvas)
Graph.close()

Categories

Resources