wxpython and updating matplotlib figure in panel - python

I am using a wx gui with basemap and matplotlib. I have two panels (left/right). I plot a map of the US in my right panel. This occurs no problem with the code below:
# Setup the Map for Display
self.figure = Figure(None,dpi=75)
self.canvas = FigureCanvas(self.MapPage, -1, self.figure)
self.axes = self.figure.add_axes([0,0,1,1],frameon=False)
self.SetColor( (255,255,255) )
#Sizer for Map Page Right Panel (Map Display)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL)
self.MapPage.SetSizer(sizer)
# Plot the Map
self.map = Basemap(llcrnrlon=-119, llcrnrlat=22, urcrnrlon=-64,
urcrnrlat=49, projection='lcc', lat_1=33, lat_2=45,
lon_0=-95, resolution='i', area_thresh=10000,ax=self.axes)
self.map.drawcoastlines()
self.map.drawcountries()
self.map.drawstates()
This works like a charm. However, eventually later in the code I would like to change the map to maybe only show a region. This is where I run into issues. Currently, I am doing this:
# Clear the previous figure
self.figure.clf()
self.figure.canvas.draw()
# Setup for the NEW Map to Display
self.figure = Figure(None,dpi=75)
self.canvas = FigureCanvas(self.MapPage, -1, self.figure)
self.axes = self.figure.add_axes([0,0,1,1],frameon=False)
self.SetColor( (255,255,255) )
#Sizer for NEW Map Page Right Panel (Map Display)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL)
self.MapPage.SetSizer(sizer)
# Plot the NEW Map
self.map = Basemap(llcrnrlon=newlong1, llcrnrlat=newlat1, urcrnrlon=newlong2,
urcrnrlat=newlat2, projection='lcc', lat_0=maplat,
lon_0=maplon, resolution='i', area_thresh=10000,ax=self.axes)
self.map.drawcoastlines()
self.map.drawcountries()
self.map.drawstates()
This code manages to clear my right panel and display the new map; HOWEVER, this new map is only in the top left corner of the panel. It no longer stretches across the entire panel. Any ideas on why this is? Or is there a better way for me to clear the Basemap figure to plot a new one in the same panel?
Any help would be appreciated! Thanks!

Related

How to refresh wx.Panel correctly?

I am creating a image viewer using wxPython. I want to view multiple images individually, so I wrote a code below (partially).
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figs = []
self.axes = []
self.canvases = []
self.panelsizers = []
def draw(self, data):
"""data is 2D numpy array"""
fig = Figure()
self.figs.append(fig)
ax = fig.add_subplot(111)
ax.imshow(data, interpolation='none')
self.axes.append(ax)
canvas = FigureCanvas(self, wx.ID_ANY, fig)
self.canvases.append(canvas)
panelsizer = wx.BoxSizer()
panelsizer.Add(canvas, 1, wx.GROW)
self.panelsizers.append(panelsizer)
self.SetSizer(panelsizer)
This works almost perfectly except a slight problem.
When I ran the code and open a image, the window looks like below.
enter image description here
This window consists of three wx.Panels and the center one is CanvasPanel. You can see that the size of CanvasPanel is a bit small even though proportion=1 and style=wx.GROW. Moreover, when I resize this window by dragging the corner of window, it looks like below.
enter image description here
The size of CanvasPanel changes correctly! Why? And how can I revise my code to fit the CanvasPanel in the viewer without resizing.

saving figure using matplotlib

I have multiple plots each displayed in each tab(each tab is a page in wx.Notebook). I have attached the code snippet used to plot the figure in each tab. Everything is fine till I try to save a figure using the option in navigation toolbar. When I try to save a figure, the figure in that plot gets messed up and the tab looks like the image attached. How can I fix this issue?
Any help is highly appreciated. Thanks.
self.fig = plt.figure(figsize=(hor_pix, ver_pix), dpi=self.dpi)
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.toolbar = NavigationToolbar(self.canvas)
line_ico = wx.Image('line.bmp', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.line_button = self.toolbar.AddSimpleTool(wx.ID_ANY, line_ico, "Line", "Extract using line Marker")
paste_ico = wx.Image('paste2.bmp', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.paste_button = self.toolbar.AddSimpleTool(wx.ID_ANY, paste_ico, "Paste gates", "Copy gates from other plots.")
self.toolbar.Realize()
self.SetSizer(self.vbox)
self.vbox.Fit(self)
self.axes.hist(datam3,bins,color='b',ec='b',fc='b')
self.canvas.draw()
self.Refresh()
self.Update()

How Do I Make a GUI Open with the Correct Size with wxPython?

I'm having trouble with the sizing of my window. When first opening the GUI, the initial size only shows a portion of what's in the window; its too small. Scroll bars are present so I can get to the information, but I want it to open with the correct size, rather than having to drag it diagonally so that all the information will be visible. Also, when expanding the window size, the appearance of the buttons glitches and the wording gets all choppy-like.
How can I change the sizers/scrolling so that the initial window will open to the correct size and all data will be shown?
Part of my code is listed below (a lot of information removed for simplicity), I know the form is bad, sorry. Thanks for the help!
class Part_1(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'CR Part 1')
global panel
panel = ScrolledPanel(self)
# LAYOUT -----------------------------------------------------
# Setting up different sizers
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(lbl, flag=wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL, border=10)
staticbox = wx.StaticBoxSizer(wx.StaticBox(panel, wx.ID_ANY, u"Part 1: Initiation (completed by initiator)"), wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
hbox1.Add((20,20), 1)
hbox1.Add(crnum)
hbox1.Add((0,0), 1)
hbox1.Add(crrev)
hbox1.Add((20,20), 1)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox2.Add((20,20), 1)
hbox2.Add(attachBtn)
hbox2.Add((20,20), 1)
staticbox.Add(hbox2, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border=25)
# Add sizers to the the main panel sizer
vbox.Add(hbox1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
vbox.Add(staticbox, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
panel.SetSizer(vbox)
panel.SetupScrolling()```
Just add the size when creating the Frame:
wx.Frame.__init__(self, None, -1, 'CR Part 1', size=(800, 600))

Python/wxWidgets: Vertically aligning text in a wrapped StaticText

I've seen a lot of posts about this around the Internet but none of the advice works.
Here is my test code:
import wx
class DisplayText(wx.Dialog):
def __init__(self, parent, text="", displayMode=0):
# Initialize dialog
wx.Dialog.__init__(self, parent, size=(480,320), style=( wx.DIALOG_EX_METAL | wx.STAY_ON_TOP ) )
# Freeze UI so user won't see stuff flashing
self.Freeze()
# (For Mac) Setup a panel
self.panel = wx.Panel(self,size=(480,320))
self.panel.SetBackgroundColour(wx.Colour(0,0,128))
# Setup sizer for vertical centering
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
# Create text field
self.txtField = wx.StaticText(self.panel,size=(448,-1),style=wx.ALIGN_CENTRE_HORIZONTAL)
self.txtField.SetLabel(text)
self.txtField.Wrap(448)
self.txtField.SetLabel(self.txtField.GetLabel())
self.txtField.SetFont(wx.Font(36, wx.DEFAULT, wx.BOLD, 0))
self.txtField.SetForegroundColour(wx.Colour(255,255,255))
# Add the static text to the sizer
self.sizer.Add(self.txtField,1, 0,15)
self.panel.SetSizer(self.sizer)
# Ensure layouts are applied
self.sizer.Layout()
# Thaw UI
self.Thaw()
# Center form
self.Center()
app = wx.App(False)
c = DisplayText(None, text="Now is the time for all good men to come to the aid of their country.")
c.Show()
import wx.lib.inspection
wx.lib.inspection.InspectionTool().Show()
app.MainLoop()
Based on my understanding of StaticText, the problem seems to be that when I call the Wrap() function, the control wraps the text but does not change the control's size. This means the sizer, unaware of the actual vertical size of the wrapped text, cannot properly reposition the static text.
One post I found suggested using wx.EXPAND, but this does not solve the problem. Allowing the sizer to expand the StaticText simply makes the StaticText fill the entire frame, and since StaticText doesn't vertically center in and of itself, the text will end up at the top of the form, not the center.
Another post suggested experimenting with wx.ALIGN_CENTER_VERTICAL in the sizer, but this didn't help either, because the same problem remains - the sizer can only work on the size of the control, and the control's size is NOT changing after the text is wrapped and re-rendered.
Various attempts have resulted in either the text wrapped but at the top of the form, only the first line being shown in the center of the form, only the first line being shown at the top of the form, and the text not being wrapped and thus flowing off the right hand edge of the form. But no combination of anything I've read has worked to vertically center the wrapped text on the form.
What I therefore would have to be able to do is somehow figure out the size of the text vertically after the wrapping has taken place. This can't be assumed because on different platforms, or even on the same platform, or even based on the text itself, the font's vertical pixel size may differ. So this needs to somehow be calculated.
I'm thinking if I can somehow figure out the actual size of the rendered text inside the StaticText control, I could then explicitly set the control's size and then let the sizer do its work.
This seems like it should be a relatively simple thing to accomplish...
Advice would be greatly appreciated!
The key was the static text style; you needed to enable the multi-line style for the wrap to work: wx.TE_MULTILINE.
I also set the layout on the panel itself, not the sizer but that might not be related to the problem.
On my machine, Windows 7 64bit, I needed to change the SetFont call; you might need to change it back.
import math
from threading import Thread
import time
import wx
e = lambda x: complex(math.cos(x), 0) + complex(0, math.sin(x))
r = lambda x: ((e(0*math.pi/3.0)*e(x)).real + 1)/2.0
g = lambda x: ((e(2*math.pi/3.0)*e(x)).real + 1)/2.0
b = lambda x: ((e(4*math.pi/3.0)*e(x)).real + 1)/2.0
colo = lambda rad: map(lambda x: int(128 * x(rad)), [r, g, b])
class DisplayText(wx.Dialog):
def __init__(self, parent, text="", displayMode=0):
# Initialize dialog
wx.Dialog.__init__(self, parent, size=(480, 320), style=( wx.DIALOG_EX_METAL | wx.STAY_ON_TOP ) )
# Freeze UI so user won't see stuff flashing
self.Freeze()
# (For Mac) Setup a panel
self.panel = wx.Panel(self, size=(480, 320))
self.panel.SetBackgroundColour(wx.Colour(0, 0, 128))
self.panel.SetBackgroundColour(wx.Colour(*colo(0)))
# Setup sizer for vertical centering
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel.SetSizer(self.sizer)
# Create text field
self.txtField = wx.StaticText(self.panel, size=(448, -1), style=(wx.ALIGN_CENTRE_HORIZONTAL | wx.TE_MULTILINE ))
self.txtField.SetFont(wx.Font(36, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD))
self.txtField.SetForegroundColour(wx.Colour(255, 255, 255))
self.txtField.SetLabel(text)
self.txtField.Wrap(448)
self.sizer.Add(self.txtField, 1, 0, 15)
# Add the static text to the sizer
# Ensure layouts are applied
self.panel.Layout()
self.panel.Refresh()
# Thaw UI
self.Thaw()
# Center form
self.Center()
self.angle = 0
self.angle_inc = (2*math.pi)/25.0
def change_colour(self):
t = Thread(target=self.epilepsy_mode)
t.start()
def epilepsy_mode(self):
while True:
time.sleep(0.02)
self.panel.SetBackgroundColour(wx.Colour(*colo(self.angle)))
self.panel.Refresh()
self.angle = (self.angle + self.angle_inc) % (2*math.pi)
app = wx.App(False)
c = DisplayText(None, text="Now is the time for all good men to come to the aid of their country.")
c.Show()
#import wx.lib.inspection
#wx.lib.inspection.InspectionTool().Show()
c.change_colour()
app.MainLoop()

Force a panel to be square in wx (python)

Does anybody know how i would go about forcing a panel to be square.
The case where this occurs is have a panel in which I have a horizontal BoxSizer with 2 slots, In the left slot i have a panel that I am going to be drawing to though wx.PaintDC, on the right I am going to have a a list control or some other widget.
What i am trying to achieve is to have the window realizable and to have the left panel always stay square, and to have the right hand content fill the rest of the space.
One of the solutions is to use EVT_SIZE to respond to window resizing and update panel size in the event function. Simple example code:
import wx
from wx.lib.mixins.inspection import InspectionMixin
class MyApp(wx.App, InspectionMixin):
def OnInit(self):
self.Init() # initialize the inspection tool
frame = wx.Frame(None)
sizer = wx.BoxSizer(wx.HORIZONTAL)
frame.SetSizer(sizer)
self.__squarePanel = wx.Panel(frame)
sizer.Add(self.__squarePanel, 0, wx.ALL | wx.EXPAND, 5)
frame.Bind(wx.EVT_SIZE, self.OnSize)
frame.Show()
self.SetTopWindow(frame)
return True
def OnSize(self, evt):
frame = evt.GetEventObject()
frameW, frameH = frame.GetSize()
targetSide = min(frameW, frameH)
self.__squarePanel.SetSize((targetSide, targetSide))
app = MyApp()
app.MainLoop()
You can bind to wx.EVT_SIZE to resize the panel when the window is resized. Partial code (untested, but someting like this):
self.panel = wx.Panel(self, -1, size=(200, 200))
self.Bind(wx.EVT_SIZE, self.resize_panel)
def resize_panel():
w, h = self.sizer.GetSize()
w = h
panel.SetSize(w, h)

Categories