Search code examples
python-3.xmatplotlibwxpython

WxPython Layout with Graph and Grid


The layout is not working correctly. I want the screen split in equal parts. To the far left buttons in the middle a mathplotlib graph and to the right a grid. There are two problems mathplotlib graph does not appear centered and the grid is not to the far right.

I have tried changing the sizers and adding some flags to them.

import wx
import wx.grid as grid
from sympy import Symbol, sympify
from matplotlib.pyplot import plot, show, title, xlabel, ylabel
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np

h = float()
xi = float()
yi = float()
fx = Symbol('fx')
xistr = np.array([])
yistr = np.array([])
istr = np.array([])
hstr = np.array([])
state = float()


class MyPanel(wx.Panel):

    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        ###########
        main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        left_sizer = wx.BoxSizer(wx.VERTICAL)
        center_sizer = wx.BoxSizer(wx.VERTICAL)
        right_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(left_sizer, 1, wx.EXPAND)
        main_sizer.Add(center_sizer, 1, wx.EXPAND)
        main_sizer.Add(right_sizer, 1, wx.EXPAND)
        ###########
        self.runbn = wx.Button(self, label="Run", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.Run, self.runbn)
        ############
        self.functionin = wx.TextCtrl(self, size=(150, 30), style=wx.TE_CENTER)
        self.STRfxbn = wx.Button(self, -1, label="STR", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.STRfc, self.STRfxbn)
        ############
        self.yiin = wx.TextCtrl(self, size=(150, 30), style=wx.TE_CENTER)
        self.STRyibn = wx.Button(self, -1, label="STR", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.STRyi, self.STRyibn)
        ############
        self.xiin = wx.TextCtrl(self, size=(150, 30), style=wx.TE_CENTER)
        self.STRxibn = wx.Button(self, -1, label="STR", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.STRxi, self.STRxibn)
        ############
        self.hiin = wx.TextCtrl(self, size=(150, 30), style=wx.TE_CENTER)
        self.STRhibn = wx.Button(self, -1, label="STR", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.STRhi, self.STRhibn)
        ###########
        self.resultbn = wx.Button(self, label="Results", size=(60, 30))
        self.Bind(wx.EVT_BUTTON, self.showresults, self.resultbn)
        ############

        left_sizer.Add(wx.StaticText(self, -1, "f(x) for ODE"))
        left_sizer.Add(self.functionin, 0)
        left_sizer.Add(self.STRfxbn, 0)

        left_sizer.Add(wx.StaticText(self, -1, "Initial y value"))
        left_sizer.Add(self.yiin, 0)
        left_sizer.Add(self.STRyibn, 0)

        left_sizer.Add(wx.StaticText(self, -1, "Initial x value"))
        left_sizer.Add(self.xiin)
        left_sizer.Add(self.STRxibn)

        left_sizer.Add(wx.StaticText(self, -1, "Initial step h"))
        left_sizer.Add(self.hiin)
        left_sizer.Add(self.STRhibn)

        left_sizer.Add(self.runbn, 0)
        left_sizer.Add(self.resultbn, 0)
        self.SetSizer(main_sizer)

    def showresults(self, event):
        global istr
        global xistr
        global yistr
        global hstr
        resultgr = grid.Grid(self)
        resultgr.CreateGrid(30, 4)
        resultgr.SetColLabelValue(0, "Iteration")
        resultgr.SetColLabelValue(1, "X Value")
        resultgr.SetColLabelValue(2, "Y Value")
        resultgr.SetColLabelValue(3, "Step Size")
        r = 0
        i = 0
        for i in range(30):
            resultgr.SetCellValue(r, 0, str(round(istr[r], 4)))
            r = r + 1
            i = i + 1
        r = 0
        i = 0
        for i in range(30):
            resultgr.SetCellValue(r, 1, str(round(xistr[r], 4)))
            r = r + 1
            i = i + 1
        r = 0
        i = 0
        for i in range(30):
            resultgr.SetCellValue(r, 2, str(round(yistr[r], 4)))
            r = r + 1
            i = i + 1
        r = 0
        i = 0
        for i in range(30):
            resultgr.SetCellValue(r, 3, str(round(hstr[r], 4)))
            r = r + 1
            i = i + 1
        self.figure = Figure(figsize=(5, 4), dpi=100)
        self.axes = self.figure.add_subplot(111)
        self.canvas = FigureCanvas(self, -1, self.figure)
        self.axes.set_xlabel("x values")
        self.axes.set_ylabel("y values")
        self.axes.set_title("Prince-Dormand")
        self.axes.plot(xistr, yistr)
        main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        left_sizer = wx.BoxSizer(wx.VERTICAL)
        center_sizer = wx.BoxSizer(wx.VERTICAL)
        right_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(left_sizer, 2, wx.EXPAND)
        main_sizer.Add(center_sizer, 1, wx.EXPAND)
        main_sizer.Add(right_sizer, 1, wx.EXPAND)
        center_sizer.Add(self.canvas, 0, wx.LEFT, 200)
        right_sizer.Add(resultgr, 0, wx.RIGHT, 0)
        self.SetSizer(main_sizer)
        self.Fit()

... Following are button functions and main function which calculates values that go into graph and grid

class MainFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='Prince-Dormand Method', size=(1300, 600))
        panel = MyPanel(self)
        self.Show()


if __name__ == '__main__':
    app = wx.App(False)
    resultgr = grid.Grid()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()

What should happen would be that each of the elements would be in its portion of the screen.


Solution

  • You can find below a working example having the widget distribution that you want. The code below should be a good starting point for you to add all other widgets and methods as needed by your app.

    import wx
    import wx.grid as grid
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    from matplotlib.figure import Figure
    
    class MainFrame(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None, title='Prince-Dormand Method', size=(800, 600))
    
            ####---- Widgets
            self.panel = wx.Panel(self)
            # Left Sizer Widgets here
            self.runbn = wx.Button(self.panel, label="Run", size=(60, 30))
            # Matplotlib plot here
            self.figure = Figure(figsize=(5, 4), dpi=100)
            self.axes = self.figure.add_subplot(111)
            self.canvas = FigureCanvas(self.panel, -1, self.figure)
            self.axes.set_xlabel("x values")
            self.axes.set_ylabel("y values")
            self.axes.set_title("Prince-Dormand")
            # Grid here
            self.resultgr = grid.Grid(self)
            self.resultgr.CreateGrid(30, 4)
            self.resultgr.SetColLabelValue(0, "Iteration")
            self.resultgr.SetColLabelValue(1, "X Value")
            self.resultgr.SetColLabelValue(2, "Y Value")
            self.resultgr.SetColLabelValue(3, "Step Size")
            ####---- Sizers
            main_sizer = wx.BoxSizer(wx.HORIZONTAL)
            left_sizer = wx.BoxSizer(wx.VERTICAL)
            center_sizer = wx.BoxSizer(wx.VERTICAL)
            right_sizer = wx.BoxSizer(wx.VERTICAL)
    
            left_sizer.Add(self.runbn, 1, wx.ALIGN_CENTER)
            center_sizer.Add(self.canvas, 1, wx.EXPAND)
            right_sizer.Add(self.resultgr, 1, wx.EXPAND)
    
            main_sizer.Add(left_sizer, 1, wx.EXPAND)
            main_sizer.Add(center_sizer, 1, wx.EXPAND)
            main_sizer.Add(right_sizer, 1, wx.EXPAND)
    
            self.panel.SetSizer(main_sizer)
            main_sizer.Fit(self.panel)
        #---
    #---
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MainFrame()
        frame.Show()
        app.MainLoop()