Robert Cunningham wrote:
I'd like to plot the set using
a simple "value" vs "channel #" bar graph.
I've looked at the existing demos, and either I'm too much of a newbie
to comprehend them, or they don't quite provide the template I need. Is
there some demo code available to help me create this simple plot?
Are you looking at just the stuff that comes with PythonCard or
wxPython? If so, be sure to check out the stuff in SVN:
http://svn.wxwidgets.org/viewvc/wx/wxPython/3rdParty/FloatCanvas/Demos/
MovingPlot.py and ScaleDemo.py may be helpful.
(Minor rant: Detailed FloatCanvas docs could keep me off the list...
Examples & demos tell me "what", but not "why".)
Well, yes, but someone's got to write them! Contributions accepted.
Or is FloatCanvas not the best tool for the job?
It's do the job, but as you've noticed does not have any built-in
support for axes, etc.
(My backup plan is to
use GnuPlot to generate an image then shove it into a canvas, but I'd
lose interactivity.)
I wouldn't do that. However, you might want to look at matplotlib, (and
wxmpl for helping put it into a wx app) performance will be worse that
FloatCanvas, but there is a lot of nifty plotting stuff supported.
There are also a couple other plotting widgets for wxPython -- one in
the wxPython distribution that is very limited, and couple others that
have been talked about on the mailing list.
Anyway, I did a bit, and worked up a demo that I think is a start of
what you want. It's enclosed, and now in FloatCanvas SVN (which is now
part of wxPython SVN) It's tested against SVN head, so it may not work
with an older FloatCanvas (though it may).
Note that I'm creating a rectangle object for each bar on the plot. That
requires that you loop through them all to re-set the values. For better
performance, it would be better to create a custom BarPlot DrawObject
that kept one data set, and it would draw the individual rectangles.
There is an issue -- If I put in 1024 data points, then each bar ends up
less than a pixel wide, which gets rounded down to zero, and nothing
gets drawn. It gets drawn fine when you zoom in, though. I thought we
could just add a "minimum width" parameter to FloatCanvas.Rectangle, but
it's not that simple, the truncation happens when WorldToPixel creates
an integer.
So, in this version, I wrote a new ScaleWorldToPixel method that rounds
up, and injected into FloatCanvas before using it -- Python is so cool!
I don't want to change it globally, as others might not want that behavior.
Another issue is zooming -- if you zoom in, you might want to zoom in to
the X axis, but re-scale the y axis so you can still see the entire
height of the bars. You could do that by writing a GUIMode that captures
the zoom and re-defines the projection function. Or you could do the
scaling in your BarPlot object, if you wrote one -- that might be the
better way to go.
In short, FloatCanvas has the infrastructure you need, but it's going to
take some work to get everything you probably want -- you're probably
better off with Matplotlib.
-Chris
--
Christopher Barker, Ph.D.
Oceanographer
Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
[EMAIL PROTECTED]
#!/usr/bin/env python
import wx
## import the installed version
#from wx.lib.floatcanvas import NavCanvas, FloatCanvas
## import a local version
import sys
sys.path.append("../")
from floatcanvas import NavCanvas, FloatCanvas
import numpy as N
from numpy import random as random
NumChannels = 200
MaxValue = 2**24
def YScaleFun(center):
"""
Function that returns a scaling vector to scale y data to same range as x data
This is used by FloatCanvas as a "projection function", so that you can have
a different scale for X and Y. With the default projection, X and Y are the same scale.
"""
# center gets ignored in this case
return N.array((1, float(NumChannels)/MaxValue), N.float)
def ScaleWorldToPixel(self, Lengths):
"""
This is a new version of a function that will get passed to the
drawing functions of the objects, to Change a length from world to
pixel coordinates.
This version uses the "ceil" function, so that fractional pixel get
rounded up, rather than down.
Lengths should be a NX2 array of (x,y) coordinates, or
a 2-tuple, or sequence of 2-tuples.
"""
return N.ceil(( (N.asarray(Lengths, N.float)*self.TransformVector) )).astype('i')
class DrawFrame(wx.Frame):
"""
A frame used for the FloatCanvas Demo
"""
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.CreateStatusBar()
# Add the Canvas
FloatCanvas.FloatCanvas.ScaleWorldToPixel = ScaleWorldToPixel
NC = NavCanvas.NavCanvas(self,-1,
size = (500,500),
BackgroundColor = "DARK SLATE BLUE",
ProjectionFun = YScaleFun,
)
self.Canvas = Canvas = NC.Canvas
#self.Canvas.ScaleWorldToPixel = ScaleWorldToPixel
FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove )
self.Values = random.randint(0, MaxValue, (NumChannels,))
self.Bars = []
self.BarWidth = 0.75
# add an X axis
Canvas.AddLine(((0,0), (NumChannels, 0 )),)
for x in N.linspace(1, NumChannels, 11):
Canvas.AddText("%i"%x, (x-1+self.BarWidth/2,0), Position="tc")
for i, Value in enumerate(self.Values):
bar = Canvas.AddRectangle(XY=(i, 0),
WH=(self.BarWidth, Value),
LineColor = None,
LineStyle = "Solid",
LineWidth = 1,
FillColor = "Red",
FillStyle = "Solid",
)
self.Bars.append(bar)
# Add a couple a button the Toolbar
tb = NC.ToolBar
tb.AddSeparator()
ResetButton = wx.Button(tb, label="Reset")
tb.AddControl(ResetButton)
ResetButton.Bind(wx.EVT_BUTTON, self.ResetData)
# PlayButton = wx.Button(tb, wx.ID_ANY, "Run")
# tb.AddControl(PlayButton)
# PlayButton.Bind(wx.EVT_BUTTON, self.RunTest)
tb.Realize()
self.Show()
Canvas.ZoomToBB()
Canvas.Draw(True)
def OnMove(self, event):
"""
Updates the status bar with the world coordinates
"""
channel, value = event.Coords
if 0 < channel < NumChannels :
channel = "%i"%(channel+1)
else:
channel = ""
if value >=0:
value = "%i"%value
else:
value = ""
self.SetStatusText("Channel: %s, Value: %s"%(channel, value))
def ResetData(self, event):
self.Values = random.randint(0, MaxValue, (NumChannels,))
for i, bar in enumerate(self.Bars):
bar.SetShape(bar.XY, (self.BarWidth, self.Values[i]))
self.Canvas.Draw(Force=True)
app = wx.App(False)
F = DrawFrame(None, title="FloatCanvas Demo App", size=(700,700) )
app.MainLoop()
_______________________________________________
FloatCanvas mailing list
[email protected]
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas