Home  Contents

Drawing with Cairo II

نستكمل الرسم مع مكتبة كايرو

Donut

فى هذا المثال سننشئ شكل معقد بإستخدام مجموعة من القطع الناقصة

donut.py
 
#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program creates a donut
# with cairo library
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009


import gtk
import math

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        
        self.set_title("Donut")
        self.set_size_request(350, 250)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        darea = gtk.DrawingArea()
        darea.connect("expose-event", self.expose)
        self.add(darea)
        
        self.show_all()
    
    def expose(self, widget, event):

        cr = widget.window.cairo_create()

        cr.set_line_width(0.5)

        w = self.allocation.width
        h = self.allocation.height
       
        cr.translate(w/2, h/2)
        cr.arc(0, 0, 120, 0, 2*math.pi)
        cr.stroke()
         

        for i in range(36):
            cr.save()
            cr.rotate(i*math.pi/36)
            cr.scale(0.3, 1)
            cr.arc(0, 0, 120, 0, 2*math.pi)
            cr.restore()
            cr.stroke()
            

PyApp()
gtk.main()

فى هذا المثال ننشئ donut “دونة"

 cr.translate(w/2, h/2)
 cr.arc(0, 0, 120, 0, 2*math.pi)
 cr.stroke()

فى البداية هناك قطع

  for i in range(36):
      cr.save()
      cr.rotate(i*math.pi/36)
      cr.scale(0.3, 1)
      cr.arc(0, 0, 120, 0, 2*math.pi)
      cr.restore()
      cr.stroke()

بعد عدة rotation نجد قطعة الحلوى :D


Donut

Figure: Donut



Gradients

In computer graphics, gradient is a smooth blending of shades from light to dark or from one color to another. In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. (answers.com)

gradients.py
 
#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program works with
# gradients in cairo
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009

import gtk
import cairo

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        
        self.set_title("Gradients")
        self.set_size_request(340, 390)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        darea = gtk.DrawingArea()
        darea.connect("expose-event", self.expose)
        self.add(darea)

        self.show_all()
    
    def expose(self, widget, event):

        cr = widget.window.cairo_create()
        lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)
       
        count = 1

        i = 0.1    
        while i < 1.0: 
            if count % 2:
                lg1.add_color_stop_rgba(i, 0, 0, 0, 1)
            else:
                lg1.add_color_stop_rgba(i, 1, 0, 0, 1)
            i = i + 0.1
            count = count + 1      


        cr.rectangle(20, 20, 300, 100)
        cr.set_source(lg1)
        cr.fill()

        lg2 = cairo.LinearGradient(0.0, 0.0, 350.0, 0)
       
        count = 1
         
        i = 0.05    
        while i < 0.95: 
            if count % 2:
                lg2.add_color_stop_rgba(i, 0, 0, 0, 1)
            else:
                lg2.add_color_stop_rgba(i, 0, 0, 1, 1)
            i = i + 0.025
            count = count + 1        

        cr.rectangle(20, 140, 300, 100)
        cr.set_source(lg2)
        cr.fill()

        lg3 = cairo.LinearGradient(20.0, 260.0,  20.0, 360.0)
        lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1) 
        lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1) 
        lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1) 

        cr.rectangle(20, 260, 300, 100)
        cr.set_source(lg3)
        cr.fill()


PyApp()
gtk.main()

فى مثالنا لدينا 3 مستطيلات بجرادينتات مختلفة

lg1 = cairo.LinearGradient(0.0, 0.0, 350.0, 350.0)

هنا ننشئ نمط لجرادينت خطى.. والمعاملات هى السطر اللذى عليه نرسم الجرادينت.. فى حالتنا هنا هو خط رأسى

 
 lg3 = cairo.LinearGradient(20.0, 260.0,  20.0, 360.0)
 lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1) 
 lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1) 
 lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1) 

نحدد موقفات للون لإنشاء نمط الجرادينت وهنا يجمع بين الأسود والأصفر.. وبإضافة لونين اسود وتوقف اصفر ننشئ نمط جرادينت افقى.. ماذا تعنى تلك التوقفات حقيقة ؟فى حالتنا هذه نبدأ باللون الأسود اللذى يتوقف عند عشر مساحته ثم يبدأ برسم الأصفر تصاعديا التى ستتوج مركز الشكل.. ويتوقف الأصفر عند 9 من 10 المساحة حيث نعود نرسم الأسود مجددا وهكذا




Gradients

Figure: Gradients



Puff

فى المثال التالى سننشئ تأثير puff.. فى المثال نعرض نص فى المنتصف يكبر حتى حد معين ثم يتلاشى



puff.py
 
#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program creates a puff
# effect 
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009


import gtk
import glib
import cairo


class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        
        self.set_title("Puff")
        self.resize(350, 200)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        self.darea = gtk.DrawingArea()
        self.darea.connect("expose-event", self.expose)
        self.add(self.darea)
        
        self.timer = True
        self.alpha = 1.0
        self.size = 1.0

        glib.timeout_add(14, self.on_timer)
        
        self.show_all()
        
    def on_timer(self):
        if not self.timer: return False
    
        self.darea.queue_draw()
        return True

    
    def expose(self, widget, event):

        cr = widget.window.cairo_create()

        w = self.allocation.width
        h = self.allocation.height

        cr.set_source_rgb(0.5, 0, 0)
        cr.paint()

        cr.select_font_face("Courier", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)

        self.size = self.size + 0.8

        if self.size > 20:
            self.alpha = self.alpha - 0.01
        
        cr.set_font_size(self.size)
        cr.set_source_rgb(1, 1, 1)
        
        (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")

        cr.move_to(w/2 - width/2, h/2)
        cr.text_path("ZetCode")
        cr.clip()
        cr.stroke()
        cr.paint_with_alpha(self.alpha)

        if self.alpha <= 0:
            self.timer = False


PyApp()
gtk.main()

فى هذا المثال سننشئ نص يكبر ويتلاشى على النافذة.

 glib.timeout_add(14, self.on_timer)

يتم استدعاء التايمر -المؤقت- كل 14 ميلى ثانية

 def on_timer(self):
     if not self.timer: return False
  
     self.darea.queue_draw()
     return True     



فى الطريقة on_timer نستدعى الطريقة queue_draw خلال الرسم فى منطقة الرسم مما يطلق اشارة expose

 cr.set_source_rgb(0.5, 0, 0)
 cr.paint()

نجعل الخلفية احمر غامق

 self.size = self.size + 0.8

مع كل دورة يزيد حجم الخط بمقدار 0.8

 if self.size > 20:
     self.alpha = self.alpha - 0.01

الشحوب يبدأ عندما يزيد حجم الخط عن 20

 (x, y, width, height, dx, dy) = cr.text_extents("ZetCode")

نحصل على مقاييس النص



 cr.move_to(w/2 - width/2, h/2)

نستخدم مقاييس النص لسنترته -وضعه بالمنتصف- على النافذه

 cr.text_path("ZetCode")
 cr.clip()

نحصل على مسار النص ونحدد منطقة القصاصة الحالية

 cr.stroke()
 cr.paint_with_alpha(self.alpha)

نرسم المسار مع استخدام قيمة الفا


Puff

Figure: Puff



Reflection

فى المثال التالى سنعرض صورة منعكسة.. هذا التأثير الجميل يخلق وهم ان الصورة منعكسة فى الماء

reflection.cs

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program creates an
# image reflection
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009


import gtk
import cairo
import sys

class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        
        self.set_title("Reflection")
        self.resize(300, 350)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        darea = gtk.DrawingArea()
        darea.connect("expose-event", self.expose)
        self.add(darea)
        
        try:
            self.surface = cairo.ImageSurface.create_from_png("slanec.png")
        except Exception, e:
            print e.message
            sys.exit(1)
        
        
        self.imageWidth = self.surface.get_width()
        self.imageHeight = self.surface.get_height()
        self.gap = 40
        self.border = 20

        self.show_all()
    
    def expose(self, widget, event):

        cr = widget.window.cairo_create()

                   
        w = self.allocation.width
        h = self.allocation.height
          
        lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
        lg.add_color_stop_rgba(0, 0, 0, 0, 1)
        lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)

        cr.set_source(lg)
        cr.paint()
        
        cr.set_source_surface(self.surface, self.border, self.border)
        cr.paint()

        alpha = 0.7
        step = 1.0 / self.imageHeight
      
        cr.translate(0, 2 * self.imageHeight + self.gap)
        cr.scale(1, -1)
        
        i = 0
        
       
        while(i < self.imageHeight):
            cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1)

            i = i + 1
            
            cr.clip()
            cr.set_source_surface(self.surface, self.border, self.border)
            alpha = alpha - step
            cr.paint_with_alpha(alpha)
            cr.reset_clip()
        

PyApp()
gtk.main()

هذا المثال يظهر قلعة منعكسة

 lg = cairo.LinearGradient(w/2, 0, w/2, h*3)
 lg.add_color_stop_rgba(0, 0, 0, 0, 1)
 lg.add_color_stop_rgba(h, 0.2, 0.2, 0.2, 1)

 cr.set_source(lg)
 cr.paint()

الخلفية ممتلئة بجرادينت يمزج بين الأسود والرمادى الغامق

 cr.translate(0, 2 * self.imageHeight + self.gap)
 cr.scale(1, -1)

هذا الكود يعكس الصورة ويترجمها اسفل الصورة الأصلية.. عملية الترجمة مهمة لأن عملية الscaling تعكس الصورة اسفل لأعلى



 cr.rectangle(self.border, self.imageHeight-i, self.imageWidth, 1)

 i = i + 1
            
 cr.clip()
 cr.set_source_surface(self.surface, self.border, self.border)
 alpha = alpha - step
 cr.paint_with_alpha(alpha)
 cr.reset_clip()

الجزء الحاسم من الكود نجعل فيه الصورة الثانية شفافة -الشفافية ليست ثابته- حيث تتلاشى الصورة تصاعديا ويتم ذلك من خلال GradientPaint


Reflection

Figure: Reflection



Waiting

فى هذا المثال سنستخدم الشفافية لإنشاء "انتظار" فيه سنرسم 8 اسطر التى تشحب لخلق وهم بأن الخط يتحرك.. مثل هذه المؤثرات تستخدم لإعلام المستخدمين بأن مهمة طويلة تحدث فى الكواليس :)

waiting.py

#!/usr/bin/python

# ZetCode PyGTK tutorial 
#
# This program creates an
# waiting effect
#
# author: jan bodnar
# website: zetcode.com 
# last edited: February 2009


import gtk
import glib
import math
import cairo


trs = (
    ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
    ( 1.0, 0.0,  0.15, 0.30, 0.5, 0.65, 0.8, 0.9 ),
    ( 0.9, 1.0,  0.0,  0.15, 0.3, 0.5, 0.65, 0.8 ),
    ( 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5, 0.65 ),
    ( 0.65, 0.8, 0.9,  1.0,  0.0,  0.15, 0.3, 0.5 ),
    ( 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15, 0.3 ),
    ( 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0,  0.15 ),
    ( 0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0,  0.0, )
)


class PyApp(gtk.Window):

    def __init__(self):
        super(PyApp, self).__init__()
        
        self.set_title("Waiting")
        self.set_size_request(250, 150)
        self.set_position(gtk.WIN_POS_CENTER)

        self.connect("destroy", gtk.main_quit)

        self.darea = gtk.DrawingArea()
        self.darea.connect("expose-event", self.expose)
        self.add(self.darea)
        
        self.count = 0
        
        glib.timeout_add(100, self.on_timer)
        
        self.show_all()
        
    def on_timer(self):
        self.count = self.count + 1
        self.darea.queue_draw()
        return True

    
    def expose(self, widget, event):

        cr = widget.window.cairo_create()

        cr.set_line_width(3)
        cr.set_line_cap(cairo.LINE_CAP_ROUND)

        w = self.allocation.width
        h = self.allocation.height
       
        cr.translate(w/2, h/2)

        for i in range(8):
            cr.set_source_rgba(0, 0, 0, trs[self.count%8][i])
            cr.move_to(0.0, -10.0)
            cr.line_to(0.0, -40.0)
            cr.rotate(math.pi/4)
            cr.stroke()


PyApp()
gtk.main()

نرسم ال 8 اسطر بقيم الفا مختلفة

 glib.timeout_add(100, self.on_timer)

نستخدم التايمر لعمل الأنيميشن

trs = (
    ( 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 ),
    ...
)

هذه مصفوفة ثنائية الأبعاد مستخدمه فى هذا العرض.. هناك 8 صفوف كل منها لحالة.. كل من ال 8 اسطر يستخدمها

 cr.set_line_width(3)
 cr.set_line_cap(cairo.LINE_CAP_ROUND)

نجعل الأسطر اسمك، ليصبحو اكثر وضوحا.. نرسم

 cr.set_source_rgba(0, 0, 0, trs[self.count%8][i]

هنا نحدد الشفافية لسطر

 cr.move_to(0.0, -10.0)
 cr.line_to(0.0, -40.0)
 cr.rotate(math.pi/4)
 cr.stroke()

هذا الكود سيقوم برسم ال 8 سطور




Waiting

Figure: Waiting



فى هذا الفصل عملنا بعض الرسم المتقدم بإستخدام كايرو


Home ‡ Contents ‡ Top of Page