0

My goal is to have multiple relatively small graphs being generated in the same wxPython panel. Additionally, I would like to manually resize the graphs and place them where I want. This causes issues because my graphs are displayed incorrectly. Changing the size of the graph does not change the graph itself, it only cuts off parts of the graph. It's easier to see what I mean if you run my code. As you can see, the graph on the right is fully displayed, but it is too big and I want to make it a bit smaller. However, the smaller graph on the left is missing half of the graph! Does anyone have an idea as to how this can be fixed?

    import wx
    import matplotlib as mpl
    mpl.use('WXAgg')

    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
    from matplotlib.backends.backend_wx import NavigationToolbar2Wx
    from matplotlib.figure import Figure

    class MainFrame(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, None, size=(1200,900))

            self.panel_1 = Panel_one(self)

    class Panel_one(wx.Panel):
        def __init__(self, parent):
            wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)

            self.temp_graph = Graph(self, position=(50, 100), size=(400,400))
            self.temp_graph = Graph(self, position=(550, 100), size=(700, 700))

    class Graph(wx.Panel):
        def __init__(self, parent, position, size):
            wx.Panel.__init__(self, parent, pos=position, size=size)
            self.figure = Figure(None)
            self.canvas = Canvas(self, -1, self.figure)
            self.axes = self.figure.add_subplot(111)



    if __name__ == "__main__":
        app = wx.App(redirect=False)
        frame = MainFrame(None)
        frame.Show()
        app.MainLoop()

I have found another thread with a similar problem, but the thread's solution did not work correctly and the graphs were still displayed incorrectly.

1 Answer 1

2

I respectfully disagree with the earlier answer. The problem is not that a sizer should be included in your Graph(). Using sizers instead of fixed positions is definitely a good idea, but not the cause of the problem here.

The problem you are seeing is that the default size of the the matplotlib Figure is larger than your smaller graph. To fix this, you should specify the figure size, and the dpi for the figure. One way to do that is to use the size given to Graph.__init__() to set the size of the Figure. For example:

import wx
import matplotlib
matplotlib.use('WXAgg')


from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure

class MainFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, None, size=(1200, 900))
        self.panel_1 = Panel_one(self)

class Panel_one(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)

        self.graph1 = Graph(self, position=(50, 100), size=(400, 400))
        self.graph2 = Graph(self, position=(500, 100), size=(700, 700))


class Graph(wx.Panel):
    def __init__(self, parent,  position, size, dpi=150):
        wx.Panel.__init__(self, parent, pos=position, size=size)

        # Note: here, set the figure size based on size of Graph 
        figsize = (size[0]*1.0/dpi, size[1]*1.0/dpi)
        self.figure = Figure(figsize, dpi=dpi)
        self.canvas = Canvas(self, -1, self.figure)
        self.axes = self.figure.add_subplot(111)


if __name__ == "__main__":
    app = wx.App(redirect=False)
    frame = MainFrame(None)
    frame.Show()
    app.MainLoop()

which will scale both Graph()s appropriately.

I would definitely suggest not using absolute position, but rather using sizers, for example

class Panel_one(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)

        self.graph1 = Graph(self, size=(400, 400))
        self.graph2 = Graph(self, size=(700, 700))

        # Note: here create a sizer and add your graphs to it
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.graph1, 0, wx.LEFT, 5)
        sizer.Add(self.graph2, 0, wx.LEFT, 5)
        self.SetSizer(sizer)


class Graph(wx.Panel):
    def __init__(self, parent,  size, dpi=150):
        wx.Panel.__init__(self, parent, size=size)
        fsize = (size[0]*1.0/dpi, size[1]*1.0/dpi)
        self.figure = Figure(fsize, dpi=dpi)
        self.canvas = Canvas(self, -1, self.figure)
        self.axes = self.figure.add_subplot(111)

Finally, allow me to also suggest using wxmplot (https://newville.github.io/wxmplot/), which has a PlotPanel class that puts a matplotlib plot on a wxPython Panel, for example:

from wxmplot import PlotPanel

class Panel_one(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)

        # Note: here use wxmplot.PlotPanel instead of your Graph class
        self.graph1 = PlotPanel(self, size=(400, 400))
        self.graph2 = PlotPanel(self, size=(700, 700))

        sizer = wx.BoxSizer(wx.HORIZONTAL) 
        sizer.Add(self.graph1, 0, wx.LEFT, 5)
        sizer.Add(self.graph2, 0, wx.LEFT, 5)
        self.SetSizer(sizer)
Sign up to request clarification or add additional context in comments.

6 Comments

I looked for a quick fix and the sizers worked for this example. This answer is more robust than mine, although you might want to add import wx
You are a life saver. I have been trying to get my graphs to work all of last night and I just couldn't figure it out (evidently, I'm quite new to Python). Your suggestions work and I very much appreciate your help. I have looked into wxmplot and will start using that instead. Thanks!
@JohnDoe Glad it helped. If the answer is acceptable, I think the SO convention is that you accept it.
Sorry, I'm new to SO and did not know you could accept answers. I have a quick question about wxmplot and was about to make a new thread but I might as well ask you first since you might be familiar with it. I am reading the documentation and I am trying to call the update_line function. However, I'm not entirely sure what it wants as an input. If I give it two lists for x and y coordinates, it still wants a third input.
@JohnDoe see cars.uchicago.edu/software/python/wxmplot/… The first argument is the index of the line to be updated, then new x and y arrays.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.