Drawing on multiple layers

I am trying to develop an application using wxwidgets where i can load an image and paint on top of the image - basically like the Whyteboard app: http://code.google.com/p/whyteboard/

However, unlike whyteboard i need to be able to save the drawing in its own image so that i have a png file with the drawing of the user (this is actually a mask for the image)

So what i am trying to create is an annotation tool where user can load an image, draw a freehand shape and save the image of the freehandshape.

I would suspect this needs some sort of layering of panels, but i have not been able to find out how i can do this.

The code i have so far i seen below. What it does is that it creates an artificial image from a numpy array and displays it.
Also it captures mouse events so that it is possible to draw - The thing is that the mousecapture does not work when the image is loaded…it only works if i disable the part of the code enapsulated in ################################.

How do i change this so that i can drop on a transparent panel instead and store this panel as an image ?

I really hope for help on this one.

Thanks a lot.


import sys, os
import wx
import wx.lib.scrolledpanel as scrolled
import numpy as np
class ImgPanel(scrolled.ScrolledPanel):
def __init__(self, parent):
super(ImgPanel, self).__init__(parent, style = wx.SUNKEN_BORDER)
self.bitmap=wx.StaticBitmap(parent=self)
#image = wx.Bitmap('/home/ziebel/Desktop/G20mega.png')
self.bitmap=wx.StaticBitmap(parent=self)
array = np.zeros( (100, 100, 3),'uint8')
array[:,:,] = (255,127,0)
image = wx.EmptyImage(100,100)
image.SetData( array.tostring())
wxBitmap = image.ConvertToBitmap()

<details class='elided'>
<summary title='Show trimmed content'>&#183;&#183;&#183;</summary>

################################
        self.bitmap.SetBitmap(wxBitmap)
self.imgSizer = wx.BoxSizer(wx.VERTICAL) self.imgSizer.Add(self.bitmap, 1, wx.EXPAND)
self.SetSizer(self.imgSizer)
self.SetAutoLayout(1)
self.SetupScrolling() self.IsRectReady = False
self.newRectPara=[0,0,0,0]
################################

        self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.leftdown=False
prevpt = None def OnMotion(self, event=None):
if self.leftdown:
if self.prevpt == None:
self.prevpt = (event.X, event.Y)
dc = wx.PaintDC(self)
dc.DrawLine(self.prevpt[0],self.prevpt[1],event.X, event.Y)
self.prevpt = (event.X, event.Y)
def OnLeftDown(self, event=None):
self.leftdown=True
def OnLeftUp(self, event=None):
self.leftdown=False
self.prevpt = None
def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.Clear()
class DrawPanel(wx.Frame):
def __init__(self, *args, **kwargs):
wx.Frame.__init__(self, *args, **kwargs)
self.imgPanel = ImgPanel(self)
class App(wx.App):
def OnInit(self):
frame = DrawPanel(None, -1, "wxBitmap Test", wx.DefaultPosition,(550,200))
self.SetTopWindow(frame)
frame.Show(True)
return True
if __name__ == "__main__":
app = App(0)
app.MainLoop()

I am trying to develop an application using wxwidgets where i can load an
image and paint on top of the image - basically like the Whyteboard app:
Google Code Archive - Long-term storage for Google Code Project Hosting.

I would suspect this needs some sort of layering of panels, but i have not
been able to find out how i can do this.

I would not try to do that -- wx.Windows are not really designed to be
drawn on top of each other -- the compositing behavior is likely to be
different on different platfroms, etc.

What I would do is have your own drawing code that draws the background
image, then all your other stuff on to p of it. When you want only
the foreground image, you simple draw the same thing, except not draw
the background image.

If you double buffer it all the time, it's pretty easy

NOTE: if you want zooming and panning, take a look at wx.lib.floatcanvas --
it will do a lot for you, and almost work out of the box for this purpose:

Use a ScaledBitmap2 DrawObject for your background.

Before saving, call TheBackgroundImage.Hide()

note that I don't think there is currently a way to save the buffer image
without drawing it to the screen first, but that may be OK, or it wouldn't
be too hard to hack that in.

-Chris

···

On Tue, Jan 14, 2014 at 7:16 AM, Igor twoeyes <igortwoeyes@gmail.com> wrote:

The code i have so far i seen below. What it does is that it creates an
artificial image from a numpy array and displays it.
Also it captures mouse events so that it is possible to draw - The thing
is that the mousecapture does not work when the image is loaded...it only
works if i disable the part of the code enapsulated in
################################.

How do i change this so that i can drop on a transparent panel instead and
store this panel as an image ?

I really hope for help on this one.

Thanks a lot.

import sys, os
import wx
import wx.lib.scrolledpanel as scrolled
import numpy as np

class ImgPanel(scrolled.ScrolledPanel):
    def __init__(self, parent):
        super(ImgPanel, self).__init__(parent, style = wx.SUNKEN_BORDER)
        self.bitmap=wx.StaticBitmap(parent=self)
        #image = wx.Bitmap('/home/ziebel/Desktop/G20mega.png')

        self.bitmap=wx.StaticBitmap(parent=self)
        array = np.zeros( (100, 100, 3),'uint8')
        array[:,:,] = (255,127,0)
        image = wx.EmptyImage(100,100)
        image.SetData( array.tostring())
        wxBitmap = image.ConvertToBitmap()

        ################################
        self.bitmap.SetBitmap(wxBitmap)

        self.imgSizer = wx.BoxSizer(wx.VERTICAL)
        self.imgSizer.Add(self.bitmap, 1, wx.EXPAND)
        self.SetSizer(self.imgSizer)

        self.SetAutoLayout(1)
        self.SetupScrolling()

        self.IsRectReady = False
        self.newRectPara=[0,0,0,0]
        ################################

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_MOTION, self.OnMotion)
        self.leftdown=False
    prevpt = None
    def OnMotion(self, event=None):
        if self.leftdown:
            if self.prevpt == None:
                self.prevpt = (event.X, event.Y)
            dc = wx.PaintDC(self)

            dc.DrawLine(self.prevpt[0],self.prevpt[1],event.X, event.Y)
            self.prevpt = (event.X, event.Y)

    def OnLeftDown(self, event=None):
        self.leftdown=True

    def OnLeftUp(self, event=None):
        self.leftdown=False
        self.prevpt = None

    def OnPaint(self, event=None):
        dc = wx.PaintDC(self)
        dc.Clear()
class DrawPanel(wx.Frame):

    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.imgPanel = ImgPanel(self)

class App(wx.App):
    def OnInit(self):

        frame = DrawPanel(None, -1, "wxBitmap Test", wx.DefaultPosition,(550,200))
        self.SetTopWindow(frame)
        frame.Show(True)
        return True

if __name__ == "__main__":
    app = App(0)
    app.MainLoop()

--
You received this message because you are subscribed to the Google Groups
"wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

--

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

Chris.Barker@noaa.gov

Igor twoeyes wrote:

I am trying to develop an application using wxwidgets where i can load
an image and paint on top of the image - basically like the Whyteboard
app: Google Code Archive - Long-term storage for Google Code Project Hosting.

However, unlike whyteboard i need to be able to save the drawing in
its own image so that i have a png file with the drawing of the user
(this is actually a mask for the image)

So what i am trying to create is an annotation tool where user can
load an image, draw a freehand shape and save the image of the
freehandshape.

I would suspect this needs some sort of layering of panels, but i have
not been able to find out how i can do this.

No, you're not thinking about this in the right way. Remember that the
image the user sees is something that your application draws. There is
no "draw on an image" widget. Instead, you will be displaying a bitmap
that your application is managing.

So, you can certainly have one bitmap that is the original image, and
another bitmap that contains the accumulated graffiti drawn by the user,
then combine those images into a screen image and send that image to the
window. As the user does additional drawing, you update the graffiti
image, recombine them, and refresh the window. No layering magic
required, and you still have everything kept separately.

This is how Photoshop works, for example. As far as Windows is
concerned, it's just one window with a bitmap. The layering is all done
by code inside Photoshop.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

I like the idea, but is that really feasible.
I mean, consider mouse is being dragged, for every drag event the paint-image needs to be updated and merged with the background image and the entire display image needs to be replaced.
Will that not cause a lot of flickering?
I suppose i will at least need double buffering for this. How can i achieve that in wxpython?

···

On Tuesday, January 14, 2014 7:53:10 PM UTC+1, Tim Roberts wrote:

Igor twoeyes wrote:

I am trying to develop an application using wxwidgets where i can load

an image and paint on top of the image - basically like the Whyteboard

app: http://code.google.com/p/whyteboard/

However, unlike whyteboard i need to be able to save the drawing in

its own image so that i have a png file with the drawing of the user

(this is actually a mask for the image)

So what i am trying to create is an annotation tool where user can

load an image, draw a freehand shape and save the image of the

freehandshape.

I would suspect this needs some sort of layering of panels, but i have

not been able to find out how i can do this.

No, you’re not thinking about this in the right way. Remember that the

image the user sees is something that your application draws. There is

no “draw on an image” widget. Instead, you will be displaying a bitmap

that your application is managing.

So, you can certainly have one bitmap that is the original image, and

another bitmap that contains the accumulated graffiti drawn by the user,

then combine those images into a screen image and send that image to the

window. As the user does additional drawing, you update the graffiti

image, recombine them, and refresh the window. No layering magic

required, and you still have everything kept separately.

This is how Photoshop works, for example. As far as Windows is

concerned, it’s just one window with a bitmap. The layering is all done

by code inside Photoshop.


Tim Roberts, ti...@probo.com

Providenza & Boekelheide, Inc.

I like the idea, but is that really feasible.
I mean, consider mouse is being dragged, for every drag event the paint-image needs to be updated and merged with the background image and the entire display image needs to be replaced.
Will that not cause a lot of flickering?
I suppose i will at least need double buffering for this. How can i achieve that in wxpython?

As the others before have said…, this is done in the code, not layering windows on top of each other.

more specifically… something on these lines.
Basically
for example you should have a list of layers and layerinfo(each layers paint instructions) for each layer in that list that will be drawn in the paint event.

def OnPaint(self, event=None):
dc = wx.PaintDC(self)
dc.Clear()
# Draw the background image.
dc.DrawBitmap(bmp, 0, 0)
# Handle additional layers infos.
# Ex: maybe parse order as to what type of shapes/lines/etc…
# In order to preserve memory/performance, when the history list gets long enough, merge them together in a custom function possibly as a bitmap.
for layer in range(self.getLayerCount()):
self.DrawLayerInfos(dc, layer) # Custom DC Parser
# …or depending on how you want to handle this maybe simply 2 layers…
# lines would be the particular layer infos to draw.
dc.DrawLineList(self, lines, pens=None)


Something along this type of idea.
When saving, you should just parse visible layers to redraw anything visible and save
for example with something like wx.MemoryDC().GetSubBitmap.SaveFile()
or maybe something with PIL depending on if you like desire transparency with the final image.

···

On Wednesday, January 15, 2014 1:35:18 AM UTC-6, Igor twoeyes wrote:

wx.BufferedPaintDC
and
self.SetDoubleBuffered(True)
should work fine for this.

···

On Wednesday, January 15, 2014 1:35:18 AM UTC-6, Igor twoeyes wrote:

Will that not cause a lot of flickering?
I suppose i will at least need double buffering for this. How can i achieve that in wxpython?

I like the idea, but is that really feasible.
I mean, consider mouse is being dragged, for every drag event the paint-image needs to be updated and merged with the background image and the entire display image needs to be replaced.

Not necessarily, only the part that is changed between the last two mouse positions.

And blitting a bitmap to the screen is really fast anyway.

Note that as of a couple years ago, drawing a transparent image was pretty slow, so you were better off re-drawing the stuff on top than using a transparent bitmap. That may have changed.

Note that wx.lib.floatcanvas does all this already–it’s worth a look, either to use or get ideas from.

You should also look at the floatcanvas demos in the wxPython SVN–sorry, on a phone now, no link handy.

Chris

···

On Jan 14, 2014, at 11:35 PM, Igor twoeyes igortwoeyes@gmail.com wrote:

Will that not cause a lot of flickering?
I suppose i will at least need double buffering for this. How can i achieve that in wxpython?

On Tuesday, January 14, 2014 7:53:10 PM UTC+1, Tim Roberts wrote:

Igor twoeyes wrote:

I am trying to develop an application using wxwidgets where i can load

an image and paint on top of the image - basically like the Whyteboard

app: http://code.google.com/p/whyteboard/

However, unlike whyteboard i need to be able to save the drawing in

its own image so that i have a png file with the drawing of the user

(this is actually a mask for the image)

So what i am trying to create is an annotation tool where user can

load an image, draw a freehand shape and save the image of the

freehandshape.

I would suspect this needs some sort of layering of panels, but i have

not been able to find out how i can do this.

No, you’re not thinking about this in the right way. Remember that the

image the user sees is something that your application draws. There is

no “draw on an image” widget. Instead, you will be displaying a bitmap

that your application is managing.

So, you can certainly have one bitmap that is the original image, and

another bitmap that contains the accumulated graffiti drawn by the user,

then combine those images into a screen image and send that image to the

window. As the user does additional drawing, you update the graffiti

image, recombine them, and refresh the window. No layering magic

required, and you still have everything kept separately.

This is how Photoshop works, for example. As far as Windows is

concerned, it’s just one window with a bitmap. The layering is all done

by code inside Photoshop.


Tim Roberts, ti...@probo.com

Providenza & Boekelheide, Inc.

You received this message because you are subscribed to the Google Groups “wxPython-users” group.

To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

Igor twoeyes wrote:

I like the idea, but is that really feasible.
I mean, consider mouse is being dragged, for every drag event the
paint-image needs to be updated and merged with the background image
and the entire display image needs to be replaced.
Will that not cause a lot of flickering?

It's more than feasible. This is exactly what other drawing packages
do. No, you don't get flickering, and here's why. You would get
flickering if you were to paint the untarnished image and then paint the
graffiti over the top of it each, because you never know if the refresh
is going to catch the unmodified or modified image. But that's not what
I'm suggesting. If you combine the image and the graffiti in memory,
and then blit that combined image to the screen, there CAN'T be any
flickering.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Using FloatCanvas, I load a still image from a file and draw many stored shapes onto it using data stored in a database. I’ve never had any problems with speed or flicker. This way, users can manipulate the shapes, hiding them, changing colors or shape or line styles or line widths, as they desire to get the marked-up image just the way they want.

David

···

On Wed, Jan 15, 2014 at 12:05 PM, Tim Roberts timr@probo.com wrote:

Igor twoeyes wrote:

I like the idea, but is that really feasible.

I mean, consider mouse is being dragged, for every drag event the

paint-image needs to be updated and merged with the background image

and the entire display image needs to be replaced.

Will that not cause a lot of flickering?

It’s more than feasible. This is exactly what other drawing packages

do. No, you don’t get flickering, and here’s why. You would get

flickering if you were to paint the untarnished image and then paint the

graffiti over the top of it each, because you never know if the refresh

is going to catch the unmodified or modified image. But that’s not what

I’m suggesting. If you combine the image and the graffiti in memory,

and then blit that combined image to the screen, there CAN’T be any

flickering.

Tim Roberts, timr@probo.com

Providenza & Boekelheide, Inc.

You received this message because you are subscribed to the Google Groups “wxPython-users” group.

To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.