problems drawing on a DC

Hi

I would like to load an image from file, draw on top of the image then
display it in a frame. I based this code on some of the examples in
WPIA (rather crudely - apologies to the authors) but am now totally
confused about DC’s, panels, sizers etc. What I need to do (I
think) is

(a) load the image from file

(b) create an empty bitmap the same size as the image to use as a
buffer

© copy the image to the buffer

(d) draw on the buffer

(e) copy the buffer to the frame

(f) redraw (repaint?) the frame

The code is attached below and the image is (hopefully) attached.
The image loading and display in the frame (which seems to work OK) is
completely divorced from the DC stuff and I have no clue how to link the
two or get the DC back to the frame. Also, nothing seems to happen
when I draw on the DC. Again, your help and patience are much
appreciated. BTW the reason that I have used a sizer is that the
“proper” version of the code has a tree control in the top
frame where the image is displayed.

import wx

================

Class definition

================

Container class for model data

class ForecastData:

def __init__(self):

    # Create empty lists

    self.wells=[]

    self.platforms=[]

WELL Class definition

class Well:

# Constructor

def __init__(self, name, xp, yp):

    # Transfer arguments

    self.name=name

    self.xp=xp

    self.yp=yp

==================

Define main window

==================

class TopFrame(wx.Frame):

"""Frame class that acts as top level

window"""

# Constructor for main window

def __init__(self, data):

    self.data = data

    # Create a Frame

instance

    wx.Frame.__init__(self, None,

title=‘Test load map and wells’)

    # Create panel to hold

sizer

    self.panel =

wx.Panel(self)

    # Create the menu bar

    menuBar =

wx.MenuBar()

    menuFile = wx.Menu()

    file_open =

menuFile.Append(wx.ID_ANY, “&Initialise wells”)

    file_exit =

menuFile.Append(wx.ID_ANY, “E&xit”)

menuBar.Append(menuFile,"&File")

self.SetMenuBar(menuBar)

    # Create the status

bar

    self.CreateStatusBar()

    self.SetStatusText("Test

model")

    # Create a sizer to hold the

tree and image widgets

box=wx.BoxSizer(wx.HORIZONTAL)

    # Load the image, convert to a

bitmap and add bitmap to sizer

    bmp =

self.InitBuffer()

    box.Add(bmp, 0)

    # Add the sizer to the panel

and fit to window

self.panel.SetSizerAndFit(box)

    # Automatically resize the

window

    self.Fit()

    # Event handlers

    self.Bind(wx.EVT_MENU,

self.OnFileOpen, file_open)

    self.Bind(wx.EVT_MENU,

self.OnFileExit, file_exit)

    self.Bind(wx.EVT_PAINT,

self.OnPaint)

    bmp.Bind(wx.EVT_MOTION,

self.OnMove)

# Draw the buffer

def InitBuffer(self):

    img =

wx.Image(‘structure_map.jpg’, wx.BITMAP_TYPE_JPEG)

    bmp =

wx.StaticBitmap(self.panel, wx.ID_ANY, img.ConvertToBitmap())

    size = img.GetSize()

    self.buffer =

wx.EmptyBitmap(size.width, size.height)

    dc = wx.BufferedDC(None,

self.buffer)

    # Draw the wells

dc.SetPen(wx.Pen(‘red’))

    for well in

self.data.wells:

structure_map.jpg

···
        #

Convert from UTMs to pixels

        x =

int( 0.0566 * well.xp - 28175 )

        y =

int(-0.0567 * well.yp + 533462)

dc.DrawCircle(x, y, 10)

dc.DrawText(well.name, x+8, y+8)

    # Draw the platforms

dc.SetPen(wx.Pen(‘green’))

    for plat in

self.data.platforms:

        #

Convert from UTMs to pixels

        x =

int( 0.0566 * plat.xp - 28175)

        y =

int(-0.0567 * plat.yp + 533462)

dc.DrawRectangle(x-3, y-3, 6, 6)

dc.DrawText(plat.name, x+8, y+8)

    return bmp

# Copy from buffer to screen

def OnPaint(self, event):

    dc = wx.BufferedPaintDC(self,

self.buffer)

# ==============

# Event handlers

# ==============

# Quit the application

def OnFileExit(self, event):

    self.Destroy()

# Read forecast data from file (simplified

here)

def OnFileOpen(self, event):

    wells = [

(“N01”, 499972, 9403796),

(“N02”, 500153, 9405231),

(“N03”, 501742, 9406500),

(“N04”, 503362, 9405067),

(“N05”, 501697, 9405257),

(“N06”, 502318, 9404462),

(“S01”, 503695, 9404609),

(“S02”, 504680, 9402994),

(“S03”, 504484, 9401498),

(“S04”, 502532, 9400929),

(“S05”, 500790, 9401976),

(“S06”, 501410, 9403508),

(“S07”, 502833, 9403160),

(“S08”, 499760, 9402927),

(“N07”, 499654, 9403216) ]

    plats = [

(“main_plat”, 502500, 9403000), (“sat_north”, 501500,
9405250) ]

    # Create well/platform objects

and transfer to data object

    # (pretend platforms are wells

here for simplicity)

    for well in wells:

        name,

xp, yp = well

new_well = Well(name, xp, yp)

self.data.wells.append(new_well)

    for plat in plats:

        name,

xp, yp = plat

new_plat = Well(name, xp, yp)

self.data.platforms.append(new_plat)

    # Repaint the image

    self.InitBuffer()

# Display the location of the mouse

def OnMove(self, event):

    pos =

event.GetPosition()

    # Convert to pixels to

UTMs

    utme =  498048 + 17.677 *

float(pos.x)

    utmn = 9408000 - 17.636 *

float(pos.y)

    if utme >= 499000.0 and

utme <= 505000.0 and utmn >= 9399000.0 and utmn <=
9408000.0:

self.SetStatusText(“Cursor at %d, %d” % (int(utme), int(utmn))
)

    else:

self.SetStatusText(“Cursor outside model area”)

========================

Define application class

========================

class App(wx.App):

"""Application

class."""

def OnInit(self):

    # Define data object

    fcst_data =

ForecastData()

    # Display top level

window

    self.frame =

TopFrame(fcst_data)

    self.frame.Show()

self.SetTopWindow(self.frame)

    return True

def main():

app = App(False)

app.MainLoop()

if name == ‘main’:

main()

Alun Griffiths wrote:

I would like to load an image from file, draw on top of the image then display it in a frame. I based this code on some of the examples in WPIA (rather crudely - apologies to the authors) but am now totally confused about DC's, panels, sizers etc. What I need to do (I think) is

(a) load the image from file
(b) create an empty bitmap the same size as the image to use as a buffer
(c) copy the image to the buffer

You can do that in one step with:

wx.Bitmap(filename)

(d) draw on the buffer

yes. you need a wxMemoryDC for that.

(e) copy the buffer to the frame
(f) redraw (repaint?) the frame

These are one step.

The code is attached below and the image is (hopefully) attached. The image loading and display in the frame (which seems to work OK)

It doesn't work for me.

        # Create panel to hold sizer
        self.panel = wx.Panel(self)

you'd be better off making that Panel it's own subclass, that does all the drawing, etc. then put it on the MainFrame as you need to.

    def InitBuffer(self):
        img = wx.Image('structure_map.jpg', wx.BITMAP_TYPE_JPEG)
        bmp = wx.StaticBitmap(self.panel, wx.ID_ANY, img.ConvertToBitmap())

this could be:
           buffer = wx.Bitmap('structure_map.jpg')

        dc = wx.BufferedDC(None, self.buffer)

        # Draw the wells

        dc.SetPen(wx.Pen('red'))

        for well in self.data.wells:

            # Convert from UTMs to pixels

            x = int( 0.0566 * well.xp - 28175 )
            y = int(-0.0567 * well.yp + 533462)

            dc.DrawCircle(x, y, 10)
            dc.DrawText(well.name, x+8, y+8)

        # Draw the platforms
        dc.SetPen(wx.Pen('green'))

        for plat in self.data.platforms:

            # Convert from UTMs to pixels

            x = int( 0.0566 * plat.xp - 28175)
            y = int(-0.0567 * plat.yp + 533462)

            dc.DrawRectangle(x-3, y-3, 6, 6)
            dc.DrawText(plat.name, x+8, y+8)

        return bmp

    # Copy from buffer to screen

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer)

Here's your problem. You have a StaticBitmap with the image, then you try to draw directly on the Frame. you have two choices:

Change the bitmap in the StaticBitmap with StaticBitmap.SetBitmapLabel(), or don't use a Staticbitmap at all, and do the drawing in OnPaint, but in this case you want to draw on the Panel, not the Frame, so you want to catch its Paint event (which is why you should use a subclass of a Panel to do it. all in.

Take a look at the DoubleBuffer demo in the Wiki for an explanation of how all this works.

Also, take a look at wx.lib.floatcanvas -- it could do a lot of this for you, and give you zooming and panning to boot. Let me know if you want the latest version, and some additional demos. AS it happens, I'm using FloatCanvas for project now that is similar -- a base map that is a geo-referenced image, and drawing an interacting with objects drawn on top.

I've enclosed a re-writing of your code that works and uses some of these ideas.

-Chris

junk.py (5.56 KB)

···

--
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

Alun Griffiths wrote:

Hi

I would like to load an image from file, draw on top of the image then display it in a frame. I based this code on some of the examples in WPIA (rather crudely - apologies to the authors) but am now totally confused about DC's, panels, sizers etc. What I need to do (I think) is

(a) load the image from file
(b) create an empty bitmap the same size as the image to use as a buffer
(c) copy the image to the buffer
(d) draw on the buffer
(e) copy the buffer to the frame
(f) redraw (repaint?) the frame

The code is attached below and the image is (hopefully) attached. The image loading and display in the frame (which seems to work OK) is completely divorced from the DC stuff and I have no clue how to link the two or get the DC back to the frame. Also, nothing seems to happen when I draw on the DC.

You are combining two techniques, you only need one of them.

You are using a wx.StaticBitmap, which is a self contained widget. That's fine, but you are also drawing on the buffered DC and expecting it to show up in the frame's EVT_PAINT handler. That's fine too, but since the static bitmap and the panel are sitting on top of the frame, you can't see anything that is painted there.

I've attached a quick hack-up of your sample to use just the 2nd technique. Notice how it factors out everything needed for the map into a separate class, and then the frame just becomes a container/controller for that class (and the other things that you will put there.)

alun.py (5.31 KB)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!