Painting

In this part of the IronPython Mono Winforms tutorial, we will do some painting. Painting is used, when we want to change or enhance an existing control. Or if we are creating a custom control from scratch. To do the painting, we use the painting API provided by the Winforms library. The painting is done within a method, that we plug into the Paint event.

The System.Drawing namespace provides access to GDI+ basic graphics functionality. More advanced functionality is provided in the System.Drawing.Drawing2D, System.Drawing.Imaging, and System.Drawing.Text namespaces. The Graphics class provides methods for drawing on the form.

Lines

Our first example will draw lines on the Form control.

lines.py
#!/usr/bin/ipy

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form
from System.Drawing import Size, Pen, Color
from System.Drawing.Drawing2D import DashStyle

class IForm(Form):

    def __init__(self):
        self.Text = 'Lines'
        self.Size = Size(280, 270)
        self.Paint += self.OnPaint

        self.CenterToScreen()


    def OnPaint(self, event):

        g = event.Graphics

        pen = Pen(Color.Black, 1)
        pen.DashStyle = DashStyle.Dot
        
        g.DrawLine(pen, 20, 40, 250, 40)

        pen.DashStyle = DashStyle.DashDot
        g.DrawLine(pen, 20, 80, 250, 80)

        pen.DashStyle = DashStyle.Dash
        g.DrawLine(pen, 20, 120, 250, 120)

        pen.DashStyle = DashStyle.DashDotDot
        g.DrawLine(pen, 20, 160, 250, 160)

        pen.DashPattern = (6, 8, 1, 1, 1, 1, 1, 1)
        g.DrawLine(pen, 20, 200, 250, 200)
        
        pen.Dispose()
        g.Dispose()


Application.Run(IForm())

We draw five lines on the form. Each line has different DashStyle.

self.Paint += self.OnPaint

Paint events are delivered to the OnPaint() method.

def OnPaint(self, event):

This is the signature of the OnPaint() method.

g = event.Graphics

In order to paint on the form, we must get the Graphics object. Painting on a form is actually calling various methods of the Graphics object.

pen = Pen(Color.Black, 1)
pen.DashStyle = DashStyle.Dot
    
g.DrawLine(pen, 20, 40, 250, 40)

We create a Pen object. This object is used to draw outlines of shapes. Than we set a dotted DashStyle. Finally we draw the line with the DrawLine() method. The first parameter is the pen object. The next four values are x and y values of starting and ending points of the line.

pen.DashPattern = (6, 8, 1, 1, 1, 1, 1, 1)

There are several built-in DashStyle values. We can create our own style by using the DashPattern property. It may look difficult at the first sight. But the pattern is simply a tuple of fill and empty values.

pen.Dispose()
g.Dispose()

We release resources.

lines
Figure: Lines

Colours

A colour in Winforms library represents an ARGB (alpha, red, green, blue) colour. It is a combination of Alpha, Red, Green, and Blue (RGB) intensity values. There are also predefined colour names that we can use in painting.

colors.py
#!/usr/bin/ipy

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, ControlStyles
from System.Drawing import Size, Brushes

class IForm(Form):

    def __init__(self):
        self.Text = 'Colors'       
        self.Size = Size(360, 300)
        self.Paint += self.OnPaint
        
        self.CenterToScreen()


    def OnPaint(self, event):

        g = event.Graphics

        g.FillRectangle(Brushes.Sienna, 10, 15, 90, 60)
        g.FillRectangle(Brushes.Green, 130, 15, 90, 60)
        g.FillRectangle(Brushes.Maroon, 250, 15, 90, 60)
        g.FillRectangle(Brushes.Chocolate, 10, 105, 90, 60)
        g.FillRectangle(Brushes.Gray, 130, 105, 90, 60)
        g.FillRectangle(Brushes.Coral, 250, 105, 90, 60)
        g.FillRectangle(Brushes.Brown, 10, 195, 90, 60)
        g.FillRectangle(Brushes.Teal, 130, 195, 90, 60)
        g.FillRectangle(Brushes.Goldenrod, 250, 195, 90, 60)
        
        g.Dispose()


Application.Run(IForm())

We draw nine rectangles with nine different colours.

g.FillRectangle(Brushes.Sienna, 10, 15, 90, 60)

The FillRectagle() method fills a specified rectangle with a brush. A brush can be a colour or a pattern. There are some predefined colours available. We can get them from the Brushes object. The last four values are the x, y values of the topleft point and the width and height of the rectangle.

Colors
Figure: Colors

Hatches

The HatchBrush object is used to fill the interiors of the shapes. There are several built-in patterns that we can use.

hatches.py
#!/usr/bin/ipy

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form
from System.Drawing import Size, Color
from System.Drawing.Drawing2D import HatchBrush, HatchStyle

class IForm(Form):

    def __init__(self):
        self.Text = 'Hatches'
        self.Size = Size(360, 300)
        
        self.Paint += self.OnPaint
        
        self.CenterToScreen()


    def OnPaint(self, event):

        g = event.Graphics

        hb = HatchBrush(HatchStyle.Cross, Color.Black, self.BackColor)
        g.FillRectangle(hb, 10, 15, 90, 60)

        hb = HatchBrush(HatchStyle.Percent05, Color.Black, self.BackColor)
        g.FillRectangle(hb, 130, 15, 90, 60)

        hb = HatchBrush(HatchStyle.SolidDiamond, Color.Black, self.BackColor)
        g.FillRectangle(hb, 250, 15, 90, 60)

        hb = HatchBrush(HatchStyle.DiagonalBrick, Color.Black, self.BackColor)
        g.FillRectangle(hb, 10, 105, 90, 60)

        hb = HatchBrush(HatchStyle.Divot, Color.Black, self.BackColor)
        g.FillRectangle(hb, 130, 105, 90, 60)

        hb = HatchBrush(HatchStyle.Wave, Color.Black, self.BackColor)
        g.FillRectangle(hb, 250, 105, 90, 60)

        hb = HatchBrush(HatchStyle.ZigZag, Color.Black, self.BackColor)
        g.FillRectangle(hb, 10, 195, 90, 60)

        hb = HatchBrush(HatchStyle.Sphere, Color.Black, self.BackColor)
        g.FillRectangle(hb, 130, 195, 90, 60)

        hb = HatchBrush(HatchStyle.Shingle, Color.Black, self.BackColor)
        g.FillRectangle(hb, 250, 195, 90, 60)

        hb.Dispose()
        g.Dispose()


Application.Run(IForm())

This time we fill nine rectangles with nine different patterns, called hatches.

hb = HatchBrush(HatchStyle.Cross, Color.Black, self.BackColor)

Here we create a HatchBrush object. The parameters are the hatch style and the foreground and the background colours. The background colour is set to the colour of the form, so that it looks like we have drawn onto the form.

g.FillRectangle(hb, 10, 15, 90, 60)

We fill the rectangle with the specified hatch brush.

Hatches
Figure: Hatches

Basic objects

The following example draws some basic shapes on the form control.

basicshapes.py
#!/usr/bin/ipy

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form
from System.Drawing import Size, Rectangle, Brushes, Pens, Point
from System.Drawing.Drawing2D import SmoothingMode
from System import Array

class IForm(Form):

    def __init__(self):
        self.Text = 'Basic shapes'
        self.Size = Size(420, 280)
        
        self.Paint += self.OnPaint
        
        self.CenterToScreen()

    def OnPaint(self, event):

        g = event.Graphics
        g.SmoothingMode = SmoothingMode.AntiAlias
        
        g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)
        g.FillRectangle(Brushes.Gray, 180, 20, 80, 80)
        
        g.FillEllipse(Brushes.Gray, 30, 120, 100, 100)
        g.FillEllipse(Brushes.Gray, 160, 130, 100, 70)
        
        p1 = Point(300, 40)
        p2 = Point(340, 15)
        p3 = Point(380, 40)
        p4 = Point(380, 80)
        p5 = Point(340, 105)
        p6 = Point(300, 80)
 
        g.FillPolygon(Brushes.Gray, Array[Point]([p1, p2, p3, p4, p5, p6]))
        g.FillPie(Brushes.Gray, Rectangle(290, 130, 90, 90), 0, 315)

        g.Dispose()

Application.Run(IForm())

The code example draws six shapes on the form. A rectangle, a square, a circle, an ellipse, a polygon, and a pie.

g.SmoothingMode = SmoothingMode.AntiAlias

This makes the drawing smoother.

g.FillRectangle(Brushes.Gray, 20, 20, 120, 80)

This line fills a rectangle with gray colour. The parameters are, the brush colour, x, y coordinates of the upper-left corner of the rectangle and width and height of the rectangle.

g.FillPolygon(Brushes.Gray, Array[Point]([p1, p2, p3, p4, p5, p6]))

This line draws a polygon, consisting of six single points.

g.FillPie(Brushes.Gray, Rectangle(290, 130, 90, 90), 0, 315)

This line draws a pie. The last two parameters are the start angle and sweep angle. In degrees.

Basic shapes
Figure: Basic shapes

Drawing string

To draw string on the Winforms Form, we use the DrawString() method.

lyrics.py
#!/usr/bin/ipy

import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form
from System.Drawing import Size, Font, SolidBrush
from System.Drawing import PointF, Color

class IForm(Form):

    def __init__(self):
        self.Text = "You know I'm No Good"
        self.Size = Size(380, 450)
        
        self.Paint += self.OnPaint
        
        self.CenterToScreen()


    def OnPaint(self, event):

        g = event.Graphics

        ft = Font("Purisa", 10)
        br = SolidBrush(Color.Black)

        pt = PointF(20.0, 20.0)
        g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)

        pt = PointF(20.0, 50.0)
        g.DrawString("Your rolled up sleeves and your skull t-shirt", ft, br, pt)

        pt = PointF(20.0, 80.0)
        g.DrawString("You say why did you do it with him today?", ft, br, pt)

        pt = PointF(20.0, 110.0)
        g.DrawString("And sniffed me out like I was tanqueray", ft, br, pt)

        pt = PointF(20.0, 160.0)
        g.DrawString("Cause you're my fella, my guy", ft, br, pt)

        pt = PointF(20.0, 190.0)
        g.DrawString("Hand me your stella and fly", ft, br, pt)

        pt = PointF(20.0, 220.0)
        g.DrawString("By the time I'm out the door", ft, br, pt)

        pt = PointF(20.0, 250.0)
        g.DrawString("You tear me down like roger moore", ft, br, pt)

        pt = PointF(20.0, 300.0)       
        g.DrawString("I cheated myself", ft, br, pt)

        pt = PointF(20.0, 330.0)   
        g.DrawString("Like I knew I would", ft, br, pt)

        pt = PointF(20.0, 360.0)        
        g.DrawString("I told ya, I was trouble", ft, br, pt)

        pt = PointF(20.0, 390.0)       
        g.DrawString("You know that I'm no good", ft, br, pt)
        
        g.Dispose()


Application.Run(IForm())

In our example, we draw lyrics of a song on the Winforms form.

ft = Font("Purisa", 10)

We use the Purisa font, of 10 pts height.

pt = PointF(20.0, 20.0)   

To draw string on the form, we must use floating point values.

g.DrawString("Meet you downstairs in the bar and heard", ft, br, pt)

The DrawString() method takes the following parameters: text to draw, font, brush, and the PointF object.

Lyrics
Figure: Lyrics

Drawing image

In our last example we will draw an image on the Form control.

redrock.py
#!/usr/bin/ipy

import sys
import clr

clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form
from System.Drawing import Size, Bitmap, Rectangle


class IForm(Form):

    def __init__(self):
        self.Text = 'Red Rock'
        self.Size = Size(200, 150)

        self.loadImage()
        self.Size = Size(self.castle.Width, self.castle.Height)

        self.Paint += self.OnPaint
        self.CenterToScreen()

    def loadImage(self):
        try:
            self.castle = Bitmap("redrock.png")
        except Exception, e:
            print e.msg
            sys.exit(1)
    

    def OnPaint(self, event):
    
        g = event.Graphics
        r = Rectangle(1, 1, self.castle.Width, self.castle.Height)
        g.DrawImage(self.castle, r)
        
        g.Dispose()
        


Application.Run(IForm())

This code example draws an image of a castle on the form.

def loadImage(self):
    try:
        self.castle = Bitmap("redrock.png")
    except Exception, e:
        print e.msg
        sys.exit(1)

We load an image of a castle.

r = Rectangle(1, 1, self.castle.Width, self.castle.Height)

We determine the rectangle that we will draw.

g.DrawImage(self.castle, r)

This line actually draws the image.

Image
Figure: Image

In this chapter, we did some painting in Mono Winforms library.