Cliff Wells wrote:
I haven't been following this thread much, but are you simply looking to
convert PIL images to wxImages?
Well, no, not at all, but I may very well need to do that some day, so
thanks anyway!
Geoff Gerrietts wrote:
> How do I draw to a Numeric Array???
Well, you need Numeric, obviously. You create a matrix that's x by y
by depth elements, then you do the math to draw onto that surface.
I'm really trying not to re-invent the wheel here, and I'm trying to get
a high performace solution. Writing my own drawing code in Python will
ackomplidh neither!
If your question is "how do I get a wx API call that draws a line to
draw on the numeric array" that I can't answer.
That would be nice, but I don't imagine it's easy.
My understanding of your problem is that the display of the bitmap is
really not particularly relevant, only the generation of a bitmap with
enough depth to it that you can identify an object by its colour
signature. As I said, I may be misunderstanding the problem domain.
NO, that's exactly it. But it's not just that I need enough depth, it's
that wxColors are all 24 bit (r,g,b), one byte each, so if you draw to a
lower depth bitmap, the color gets mapped to it's closest match. When you
then check the color, two different wxColors that you drew can end up
coming back as the same color, which will kill my scheme.
A lot of the strategy you choose in solving the problem will be a
question of how often your image is changing, your goals for the image
you're generating, etc.
yep. It's changing enough that I need the performace to be as good as
possible. I havn't compared drawing in PIL to a DC, but I suspect it's
slower. Enough suspicions... I need to profile!
It sounds like a lot of the suggestions I have to offer, are things
you've had some experience with, and I'm not opening any new doors for
you.
Some I have, and some not, I love hearing new ideas...
It sounds to me like the problems you've set for yourself are
very hard, and while my angle of approach would be to look outside the
wxWindows API for my image handling, and maybe that's wrongheaded.
I was going for an easy, clever hack. It just might not work (or not be
so easy)
Here's a sample of what I have. run it (on a 24 bit or better display)
and left click somewhere while watching the console window.
#!/usr/bin/env python2.1
from wxPython.wx import *
import math
from random import randint
import time
···
#---------------------------------------------------------------------------
_MinHitTestLineWidth = 3
WXFLOATCANVASEVENT = wxNewEventType()
def EVT_WXFLOATCANVASEVENT( window, function ):
"""Your documentation here"""
window.Connect( -1, -1, WXFLOATCANVASEVENT, function )
class wxCanvasObjectEvent(wxPyCommandEvent):
def __init__(self, WindowID,Object):
wxPyCommandEvent.__init__(self, WXFLOATCANVASEVENT, WindowID)
self.Object = Object
def Clone( self ):
self.__class__( self.GetId() )
class CanvasRectangle:
def __init__(self,x,y,width,height,FillColor,ID = -1):
self.X = x
self.Y = y
self.Width = width
self.Height = height
self.FillColor = FillColor
self.ID = ID
self.Pen = wxBLACK_PEN
self.Brush = wxBrush(FillColor)
def _Draw(self,dc,color = None):
X, Y = self.X, self.Y
Width, Height = self.Width, self.Height
if color:
dc.SetPen(wxPen(color))
dc.SetBrush(wxBrush(color))
else:
dc.SetPen(self.Pen)
dc.SetBrush(self.Brush)
dc.DrawRectangle(X,Y,Width,Height)
def _HitTest(self,x,y):
if ((x >= self.X and x <= self.X + self.Width) and
(y >= self.Y and y <= self.Y + self.Height)):
return 1
else:
return 0
class CanvasLine:
def __init__(self,Points,LineColor,LineWidth,ID = -1):
self.Points = Points
self.LineColor = LineColor
self.LineWidth = int(LineWidth)
self.ID = ID
self.Pen = wxPen(LineColor,LineWidth)
self.half_linewidth = max(1, (self.LineWidth + 1)/2) # add one
to round up
integer division,a nd make sure it's at least 1
def _Draw(self,dc,HitTestColor=None):
if HitTestColor:
dc.SetPen(wxPen(HitTestColor,max(self.LineWidth,_MinHitTestLineWidth)))
else:
dc.SetPen(self.Pen)
dc.DrawLines(self.Points)
def _HitTest(self,x,y):
## Note: This is what my memory of analytical geometry came up with
## I imagine there are faster algorithms
half_linewidth = self.half_linewidth
for segnum in range(len(self.Points)-1):
x1,y1 = self.Points[segnum]
x2,y2 = self.Points[segnum + 1]
# first check bounding box:
if ((x >= (min(x1,x2)-half_linewidth) and x <= (max(x1,x2) +
half_linewidth)) and
(y >= (min(y1,y2)-half_linewidth) and y <= (max(y1,y2) +
half_linewidth)) ):
# if line is horizontal or vertical, it has been hit
if x1 == x2 or y1==y2:
return 1
else:
#NOTE: if all lines are vertical or horizontal, you
don't need
this
# now check distance to line
m = float(y2-y1)/(x2-x1)
b = y1 - m*x1
d = abs((m*x - y + b) / (math.sqrt(m**2 +1)))
if d <= half_linewidth:
return 1
else:
return 0
class DrawCanvas(wxPanel):
def __init__(self, parent, id = -1, size = wxDefaultSize):
wxPanel.__init__(self, parent, id, wxPoint(0, 0), size, wxSUNKEN_BORDER)
EVT_PAINT(self, self.OnPaint)
EVT_LEFT_DOWN(self, self.LeftDown)
EVT_RIGHT_DOWN(self, self.RightDown)
EVT_RIGHT_UP(self, self.RightUp)
EVT_MOTION(self, self.MouseMoving)
EVT_WXFLOATCANVASEVENT(self, self.ObjectClicked)
self.SetBackgroundColour(wxNamedColor("WHITE"))
self.ObjectList =
self.RectList =
self.RectMoving = 0
self.LastPosition = 0
bitmap = wxEmptyBitmap(self.GetSize()[0],self.GetSize()[1],-1)
print "bitmap is %i by %i, with a depth of %i"%(bitmap.GetWidth(),
bitmap.GetHeight(), bitmap.GetDepth())
self._HitTestImage = wxMemoryDC()
self._HitTestImage.SelectObject(bitmap)
def OnPaint(self, event):
self.Draw()
def Draw(self):
dc = wxPaintDC(self)
dc.Clear()
self._HitTestImage.Clear()
i = 0
self.HitTestDict = {16777215 : None}
for object in self.ObjectList:
i += 8
color = wxColor(i&255, (i&65280) >> 8 , (i&16711680) >> 16)
object._Draw(dc)
self.HitTestDict[i] = object
object._Draw(self._HitTestImage,color)
dc.EndDrawing()
def HitTest(self,event):
color = self._HitTestImage.GetPixel(event.GetX(),event.GetY())
i = color.Red() + (color.Green() << 8) + (color.Blue() << 16)
if self.HitTestDict[i]:
print "Object %s has been hit"%(self.HitTestDict[i].ID)
def LeftDown(self,event):
start = time.clock()
self.HitTest(event)
print "Hit Test took %f seconds"%(time.clock()-start)
start = time.clock()
for object in self.ObjectList:
if object._HitTest(event.GetX(),event.GetY()):
event = wxCanvasObjectEvent( self.GetId(), object )
self.GetEventHandler().AddPendingEvent( event )
break
print "Hit Test2 took %f seconds"%(time.clock()-start)
def RightDown(self,event):
for rect in self.RectList:
if rect._HitTest(event.GetX(),event.GetY()):
self.RectMoving = 1
self.MovingRect = rect
self.StartMousePosition = ((event.GetX(),event.GetY()))
break
def RightUp(self,event):
if self.RectMoving:
self.MovingRect.X = self.MovingRect.X + event.GetX() -
self.StartMousePosition[0]
self.MovingRect.Y = self.MovingRect.Y + event.GetY() -
self.StartMousePosition[1]
self.RectMoving = 0
self.LastPosition = 0
self.Draw()
def MouseMoving(self,event):
if self.RectMoving:
x = self.MovingRect.X + event.GetX() - self.StartMousePosition[0]
y = self.MovingRect.Y + event.GetY() - self.StartMousePosition[1]
w, h = self.MovingRect.Width, self.MovingRect.Height
dc = wxClientDC(self)
dc.BeginDrawing()
dc.SetPen(wxPen(wxNamedColour('BLACK'), 1,))
dc.SetBrush(wxTRANSPARENT_BRUSH)
dc.SetLogicalFunction(wxINVERT)
if self.LastPosition:
dc.DrawRectangle(self.LastPosition[0],self.LastPosition[1],w,h)
dc.DrawRectangle(x,y,w,h)
dc.EndDrawing()
self.LastPosition = (x,y)
else:
pass
##self.HitTest(event)
def ObjectClicked(self,event):
print "Object %s was clicked"%event.Object.ID
class DrawFrame(wxFrame):
def __init__(self,parent, id,title,position,size):
wxFrame.__init__(self,parent, id,title,position, size)
EVT_CLOSE(self, self.OnCloseWindow)
self.Canvas = DrawCanvas(self,size = (800,800))
## for row in range(40):
## for column in range(40):
##
self.Canvas.ObjectList.append(CanvasRectangle(20*row,20*column,20,20,wxRED,`(row,column)`))
for i in range(50):
line =
color = wxColor(randint(0,254),randint(0,254),randint(0,254))
width = randint(1,5)
for j in range(20):
line.append((randint(0,799),randint(0,799)))
self.Canvas.ObjectList.append(CanvasLine(line,color,width,"Line
%i"%i))
self.Show(true)
return None
def OnCloseWindow(self, event):
self.Destroy()
class App(wxApp):
def OnInit(self):
frame = DrawFrame(NULL, -1, "Test of Hit Test
Code",wxDefaultPosition,wxSize(800,800))
self.SetTopWindow(frame)
return true
app = App(0)
app.MainLoop()
--
Christopher Barker, Ph.D.
Oceanographer
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception
Chris.Barker@noaa.gov