Basic drawing in PyCairo
In this part of the PyCairo tutorial, we will draw some basic primitives. We will draw simple lines, use fill and stroke operations, we will talk about dashes, line caps and line joins.Lines
Lines are very basic vector objects. To draw a line, we use two method calls. The starting point is specified with themove_to()
call. The ending point of a line is specified with the line_to()
call. #!/usr/bin/pythonIn our example, we click randomly on the window with a left mouse button. Each click is stored in a list. When we right click on the window, all points are connected with every other point in the list. This way we can create some interesting objects. Additional right click clears the window and we can create another object.
'''
ZetCode PyCairo tutorial
In this program, we connect all mouse
clicks with a line.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
from gi.repository import Gtk, Gdk
import cairo
class MouseButtons:
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
self.darea = Gtk.DrawingArea()
self.darea.connect("draw", self.on_draw)
self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)
self.add(self.darea)
self.coords = []
self.darea.connect("button-press-event", self.on_button_press)
self.set_title("Lines")
self.resize(300, 200)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_source_rgb(0, 0, 0)
cr.set_line_width(0.5)
for i in self.coords:
for j in self.coords:
cr.move_to(i[0], i[1])
cr.line_to(j[0], j[1])
cr.stroke()
del self.coords[:]
def on_button_press(self, w, e):
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.LEFT_BUTTON:
self.coords.append([e.x, e.y])
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
class MouseButtons:The GTK documentation simply states, that the left mouse button has number 1, right mouse button number 3. We create a custom class to have some indentifiers for the mouse buttons.
LEFT_BUTTON = 1
RIGHT_BUTTON = 3
self.darea.set_events(Gdk.EventMask.BUTTON_PRESS_MASK)Some events are not enabled by default. Mouse press events are among them. Therefore, we need to enable mouse press events.
self.darea.connect("button-press-event", self.on_button_press)In this code example, we will react to mouse press events.
cr.set_source_rgb(0, 0, 0)The lines will be drawn in black ink and will be 0.5 points wide.
cr.set_line_width(0.5)
for i in self.coords:We connect every point from the list to every other point. The
for j in self.coords:
cr.move_to(i[0], i[1])
cr.line_to(j[0], j[1])
cr.stroke()
stroke()
call draws the lines. del self.coords[:]In the end, all the coordinates are deleted. We can now create another object.
def on_button_press(self, w, e):If we press a left mouse button, we add its x, y coordinates to the self.coords list.
if e.type == Gdk.EventType.BUTTON_PRESS \
and e.button == MouseButtons.LEFT_BUTTON:
self.coords.append([e.x, e.y])
...
if e.type == Gdk.EventType.BUTTON_PRESS \In case of a right mouse button press, we call the
and e.button == MouseButtons.RIGHT_BUTTON:
self.darea.queue_draw()
queue_draw()
method which redraws the drawing area. All the points are connected with lines. Figure: Lines
Fill and stroke
The stroke operation draws the outlines of shapes and the fill operation fills the insides of shapes.#!/usr/bin/pythonIn our example, we will draw a circle and fill it with a solid color.
'''
ZetCode PyCairo tutorial
This code example draws a circle
using the PyCairo library.
author: Jan Bodnar
website: zetcode.com
last edited: August 2012
'''
from gi.repository import Gtk
import cairo
import math
class Example(Gtk.Window):
def __init__(self):
super(Example, self).__init__()
self.init_ui()
def init_ui(self):
darea = Gtk.DrawingArea()
darea.connect("draw", self.on_draw)
self.add(darea)
self.set_title("Fill & stroke")
self.resize(230, 150)
self.set_position(Gtk.WindowPosition.CENTER)
self.connect("delete-event", Gtk.main_quit)
self.show_all()
def on_draw(self, wid, cr):
cr.set_line_width(9)
cr.set_source_rgb(0.7, 0.2, 0.0)
w, h = self.get_size()
cr.translate(w/2, h/2)
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.stroke_preserve()
cr.set_source_rgb(0.3, 0.4, 0.6)
cr.fill()
def main():
app = Example()
Gtk.main()
if __name__ == "__main__":
main()
import mathThis module is needed for the
pi
constant which is used to draw a circle. cr.set_line_width(9)We set a line width with the
cr.set_source_rgb(0.7, 0.2, 0.0)
set_line_width()
method. We set the source to some dark red colour using the set_source_rgb()
method. w, h = self.get_size()Here we get the width and height of the window. We will need these values to center the circle on the window.
cr.translate(w/2, h/2)With the
cr.arc(0, 0, 50, 0, 2*math.pi)
cr.stroke_preserve()
translate()
method, we move the drawing origin to the center of the window. We want our circle to be centered. The arc()
method adds a new circular path to the cairo drawing context. Finally, the stroke_preserve()
method draws the outline of the circle. Unlike the stroke()
method, it also preserves the shape for later drawing. cr.set_source_rgb(0.3, 0.4, 0.6)We change a colour for drawing and fill the circle with a new colour using the
cr.fill()
fill()
method. Figure: Fill & stroke
Pen dashes
Each line can be drawn with a different pen dash. A pen dash defines the style of the line. The dash pattern is specifed by theset_dash()
method. The pattern is set by the dash list which is a list of floating values. They set the on and off parts of the dash pattern. The dash is used by the stroke()
method to create a line. If the number of dashes is 0, dashing is disabled. If the number of dashes is 1, a symmetric pattern is assumed with alternating on and off portions of the size specified by the single value in dashes. def on_draw(self, wid, cr):We draw three lines in three different pen dashes.
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(2)
cr.set_dash([4.0, 21.0, 2.0])
cr.move_to(40, 30)
cr.line_to(250, 30)
cr.stroke()
cr.set_dash([14.0, 6.0])
cr.move_to(40, 50)
cr.line_to(250, 50)
cr.stroke()
cr.set_dash([1.0])
cr.move_to(40, 70)
cr.line_to(250, 70)
cr.stroke()
cr.set_dash([4.0, 21.0, 2.0])We have a pattern of three numbers. We have 4 points drawn, 21 not drawn and 2 drawn. Then 4 points not drawn, 21 points drawn and 2 not drawn. This pattern takes turns until the end of the line.
cr.set_dash([14.0, 6.0])In this pattern, we have always 14 points drawn, 6 not drawn.
cr.set_dash([1.0])Here we create a pen dash of a symmetric pattern of alternating single on and off points.
Figure: Pen dashes
Line caps
The line caps are endpoints of lines.- cairo.LINE_CAP_BUTT
- cairo.LINE_CAP_ROUND
- cairo.LINE_CAP_SQUARE
Figure: Square, round and butt caps
cairo.LINE_CAP_SQUARE
cap will have a different size than a line with a cairo.LINE_CAP_BUTT
cap. If a line is x units wide, the line with a cairo.LINE_CAP_SQUARE
cap will be exactly x units greater in size. x/2 units at the beginning and x/2 units at the end. def on_draw(self, wid, cr):The example draws three lines with three different line caps. It will also graphically demonstrate the differences in size of the lines by drawing three additional thin vertical lines.
cr.set_source_rgba(0, 0, 0, 1)
cr.set_line_width(12)
cr.set_line_cap(cairo.LINE_CAP_BUTT)
cr.move_to(30, 50)
cr.line_to(150, 50)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
cr.set_line_cap(cairo.LINE_CAP_SQUARE)
cr.move_to(30, 130)
cr.line_to(150, 130)
cr.stroke()
cr.set_line_width(1.5)
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
cr.move_to(150, 35)
cr.line_to(150, 145)
cr.stroke()
cr.move_to(155, 35)
cr.line_to(155, 145)
cr.stroke()
cr.set_line_width(12)Our lines will be 12 units wide. The default line width is 2.
cr.set_line_cap(cairo.LINE_CAP_ROUND)Here we draw a horizontal line with a
cr.move_to(30, 90)
cr.line_to(150, 90)
cr.stroke()
cairo.LINE_CAP_ROUND
cap. cr.set_line_width(1.5)This is one of the three vertical lines used to demostrate the differences in size.
cr.move_to(30, 35)
cr.line_to(30, 145)
cr.stroke()
Figure: Line caps
Line joins
The lines can be joined using three different join styles.- cairo.LINE_JOIN_MITER
- cairo.LINE_JOIN_BEVEL
- cairo.LINE_JOIN_ROUND
Figure: Bevel, Round, Miter line joins
def on_draw(self, wid, cr):In this example, we draw three thick rectangles with various line joins.
cr.set_line_width(14)
cr.rectangle(30, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
cr.rectangle(160, 30, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_BEVEL)
cr.stroke()
cr.rectangle(100, 160, 100, 100)
cr.set_line_join(cairo.LINE_JOIN_ROUND)
cr.stroke()
cr.set_line_width(14)The lines are 14 units wide.
cr.rectangle(30, 30, 100, 100)Here we draw a rectangle with
cr.set_line_join(cairo.LINE_JOIN_MITER)
cr.stroke()
cairo.LINE_JOIN_MITER
join style. Figure: Line joins
In this chapter of the PyCairo tutorial, we did some basic drawing.
0 comments:
Post a Comment