Create  Edit  FrontPage  Index  Search  Changes  History  RSS  Back  Login

tut-gtk2-dancr-rbcatut-intro Diff - Ruby-GNOME2 Project Website

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

= (12.3) Ruby Cairo Tutorial
{{link "tut-gtk2-dancr-wr-dapxb2file", "tut-gtk2-dancr", "tut-gtk", "tut-gtk2-dancr-rbcatut-crdrmd"}}



# = Sorry, this sub-chapter is still under construction

# (12.3) [current file: ... tut-gtk2-dancr-rbcatut-intro]
#        [next file: ...... tut-gtk2-dancr-rbcatut-crdrmd]


##  * 12.3           Ruby Cairo Tutorial (this page)
##    * 12.3.0         Intro To Ruby Cairo Tutorial
##      * 12.3.0.1       Establishing Gtk-Cairo Framework To Run the Tutorial Example Code
##        * 12.3.0.1.1     Cairo Backends
##          * 12.3.0.1.1.1     Programmer's Note
##          * 12.3.0.1.1.2     Student's Note
##        * 12.3.0.1.2     The Template Ruby File To Run Arbitrary Cairo Code
##        * 12.3.0.1.3     A Real Life Example With Runing Cairo Code
##
##      * 12.3.0.2       Running Cairo Code From Non-Gtk Ruby Environment
##
##      * 12.3.0.3       Hiki Gtk With Cairo Module Download File

:Contents of this chapter:


  * 12.3 ((<Ruby Cairo Tutorial|tut-gtk2-dancr-rbcatut-intro>)) (this page)
    * 12.3.0 ((<Intro To Ruby Cairo Tutorial|tut-gtk2-dancr-rbcatut-intro#Intro To Ruby Cairo Tutorial>))
      * 12.3.0.1 ((<Establishing Gtk-Cairo Framework To Run the Tutorial Example Code|tut-gtk2-dancr-rbcatut-intro#Establishing Gtk-Cairo Framework To Run the Tutorial Example Code>))
        * 12.3.0.1.1 ((<Cairo Backends|tut-gtk2-dancr-rbcatut-intro#Cairo Backends>))
          * 12.3.0.1.1.1 ((<Programmer's Note|tut-gtk2-dancr-rbcatut-intro#Programmer's Note:>))
            * 12.3.0.1.1.1.1 ((<Student's Note|tut-gtk2-dancr-rbcatut-intro#Student's Note:>))
        * 12.3.0.1.2 ((<The Template Ruby File To Run Arbitrary Cairo Code|tut-gtk2-dancr-rbcatut-intro#The Template Ruby File To Run Arbitrary Cairo Code:>))
          * 12.3.0.1.2.1 ((<A Real Life Example With Runing Cairo Code|tut-gtk2-dancr-rbcatut-intro#A Real Life Example With Runing Cairo Code>))

        * 12.3.0.2 ((<Running Cairo Code From Non-Gtk Ruby Environment|tut-gtk2-dancr-rbcatut-intro#Running Cairo Code From Non-Gtk Ruby Environment>))
          * 12.3.0.2.1 ((<Pure Ruby Cairo Library - rcairo API|tut-gtk2-dancr-rbcatut-intro#Pure Ruby Cairo Library - rcairo API>))

        * 12.3.0.3 ((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>))



    {{br}}
    * 12.3.1 ((<Cairo's Drawing Model|tut-gtk2-dancr-rbcatut-crdrmd>))
      * 12.3.1.1 ((<Nouns|tut-gtk2-dancr-rbcatut-crdrmd#Nouns>))
        * 12.3.1.1.1 ((<Destination|tut-gtk2-dancr-rbcatut-crdrmd#Destination>))
        * 12.3.1.1.2 ((<Source|tut-gtk2-dancr-rbcatut-crdrmd#Source>))
        * 12.3.1.1.3 ((<Mask|tut-gtk2-dancr-rbcatut-crdrmd#Mask>))
        * 12.3.1.1.4 ((<Path|tut-gtk2-dancr-rbcatut-crdrmd#Path>))
        * 12.3.1.1.5 ((<Context|tut-gtk2-dancr-rbcatut-crdrmd#Context>))

      * 12.3.1.2 ((<Verbs|tut-gtk2-dancr-rbcatut-crdrmd#Verbs>))
        * 12.3.1.2.1 ((<Stroke|tut-gtk2-dancr-rbcatut-crdrmd#Stroke>))
          * 12.3.1.2.1.A1 ((<Scale|tut-gtk2-dancr-rbcatut-crdrmd#Scale>))
          * 12.3.1.2.1.A2 ((<Default Scale Example|tut-gtk2-dancr-rbcatut-crdrmd#Default Scale Example>))
          * 12.3.1.2.1.A3 ((<Rule of thumb|tut-gtk2-dancr-rbcatut-crdrmd#Rule of thumb>))

        * 12.3.1.2.2 ((<Fill|tut-gtk2-dancr-rbcatut-crdrmd#Fill>))
        * 12.3.1.2.3 ((<Show Text - Glyphs|tut-gtk2-dancr-rbcatut-crdrmd#Show Text - Glyphs>))
        * 12.3.1.2.4 ((<Paint|tut-gtk2-dancr-rbcatut-crdrmd#Paint>))
          * 12.3.1.2.4.A1 ((<Paint And Colour Transparency In Ruby Cairo Library|tut-gtk2-dancr-rbcatut-crdrmd#Paint And Colour Transparency In Ruby Cairo Library>))
        
        * 12.3.1.2.5 ((<Mask|tut-gtk2-dancr-rbcatut-crdrmd#Mask>))
          * 12.3.1.2.5.A1 ((<Ruby Gtk-Cairo Library Fails To Support Gradient Patterns|tut-gtk2-dancr-rbcatut-crdrmd#Ruby Gtk-Cairo Library Fails To Support Gradient Patterns>))


    {{br}}
    * 12.3.2 ((<Drawing with Cairo|tut-gtk2-dancr-rbcatut-dwc>))
      * 12.3.2.1 ((<Preparing and Selecting a Source|tut-gtk2-dancr-rbcatut-dwc#Preparing and Selecting a Source>))
      * 12.3.2.2 ((<Creating a Path|tut-gtk2-dancr-rbcatut-dwc#Creating a Path>))

    * 12.3.3 ((<Understanding Text|tut-gtk2-dancr-rbcatut-dwc#Understanding Text>))
    * 12.3.4 ((<Working with Transforms|tut-gtk2-dancr-rbcatut-dwc#Working with Transforms>))
    * 12.3.5 ((<Where to Go Next|tut-gtk2-dancr-rbcatut-dwc#Where to Go Next>))

    * 12.3.6 ((<Tips and Tricks|tut-gtk2-dancr-rbcatut-dwc#Tips and Tricks>))
      * 12.3.6.1 ((<Line Width|tut-gtk2-dancr-rbcatut-dwc#Line Width>))
      * 12.3.6.2 ((<Text Alignment|tut-gtk2-dancr-rbcatut-dwc#Text Alignment>))



{{br}}

== Intro To Ruby Cairo Tutorial
(12.3.0){{br}}

This tutorial is derived primarily from ((<the official Cairo Tutorial|URL:http://www.cairographics.org/tutorial/>)), which in turn was derived from from  ((<Michael Urman's cairo tutorial for python programmers|URL:http://www.tortall.net/mu/wiki/CairoTutorial>)), and the original code snippets have been translated to C, the text has only been changed as much as necessary. All that has been said here for the 'official Cairo Tutorial' holds also for my 'Ruby Cairo Tutorial', except that the original code snippets in either of the above, have now been translated to Ruby. I also have reused the diagrams developed for the original Cairo tutorials. These diagrams were created by Donn Ingle in SVG. They require 'Inkscape' (or similar) to view, as well as two specific fonts for correct appearance. Donn requests that you download and share the diagrams on your own website if you find them useful. (((<You can find the files to download on this link|URL:https://docs.google.com/leaf?id=0ByXVQD8awBcLYjc2ZWVmZmItMDlmMi00MWUzLThkMTUtMzNhNTU0YjI3M2M2&amp;hl=en>)).)

# As you know Cairo 2D vector graphic library became part of GTK+ release 2.8 distribution.



{{br}}

=== Establishing Gtk-Cairo Framework To Run The Tutorial Example Code
(12.3.0.1){{br}}

A great number of the examples that introduce Cairo code at the beginning of almost all contemporary Cairo tutorials try to avoid unnecessarily discussing how Cairo cooperates with the X, or any other Window system. Plain C/GTK Cairo library includes a number of functions which allow you create ((*cairo context*)) independently of any particular back-end, and then bind it to a backend of your choice, however, currently you can not do the same Ruby Gtk/Cairo. Before we continue this discussion let's see what these backends are.

:Cairo Backends

    (12.3.0.1.1){{br}}
    Plain C/GTK Cairo library Cairo supports various backends. Unfortunately currently, Ruby Gtk/Cairo library does not! Nevertheless, as we will see in the next paragraph (12.3.0.2) "((<Running Cairo Code From Non-Gtk Ruby Environment|tut-gtk2-dancr-rbcatut-intro#Running Cairo Code From Non-Gtk Ruby Environment>))", it is possible to harness entire Cairo library power in Ruby, hence we may very well provide a short description of what these backends are. Namely, they represent different output devices or media for displaying the created graphics. Followig is the short list:

    :List of backends
        * Mac OS X Quartz
        * Win32 GDI
        * X Window System
        * PDF
        * PNG
        * PostScript
        * SVG

    This means, providing we use the Gtk/Cairo library version, supporting these backends, we can draw on windows on OSX, Windows, Linux/BSDs as well as that we can use this library to create PNG or JPG image files, PDF files, PostScript files and SVG files.



{{br}}
{{br}}
(12.3.0.1 /continued/){{br}}

The fact that in Ruby, Gtk/Cairo library does not provide functionality to create((*cairo context*))that could exist independently without being tied to an active Gtk Window, requires us to always run programs requiring Cairo functionality within Gtk main-loop. This makes it difficult to replicate the tutorial example code, that do not require Gtk Window and main loop. The best way to emulate the program examples from these tutorials is to provide some((*"Ruby Gtk/Cairo Framework"*))which will hide the presence of Gtk main loop and its infrastructure away as much as possible. The trouble is, in order to do that you have to use cairo library, of which at this point our students are still rather ignorant.

Though, once you will learn the basics of cairo library this((*Ruby Gtk/Cairo Framework*))will become a rather important programming device for all Ruby Gtk programmers, I should ask you not to worry about its inner workings at this point, and postpone your desire to understand it all before you get through a few program examples, that explain features used to build this framework. All you need to know at this point is to require the 'hiki2-gtk-w-cairo.rb' file and include 'GtkHiki' module it defines, and add the wrapper code as shown in the following minimal ruby cairo program:

# (12.3.0.1.1)
:Programmer's Note:

    (12.3.0.1.1.1)
    Note, we are switching the module file here. See "Student's Note" (12.3.0.1.1.1) below! The new module file 'hiki2-gtk-w-cairo.rb' is listed and explained at the end of this chapter in section 12.3.0.3 "((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>))". It can be copied from there.


# (12.3.0.1.1.1.1)
    :Student's Note:

        (12.3.0.1.1.1.1)
        For use with cairo program examples we are introducing new ((*HikiGtk*)) module ((*'hiki2-gtk-w-cairo.rb'*)). Note, the module filename has changed, while the module name (HikiGtk) stays the same!

        You should copy 'hiki2-gtk-w-cairo.rb' file into your program directory (folder), or into another directory of your choice. In any event you should make sure the directory in which the module file resides, is be included in Ruby's load path, just as was required earlier, and as originally explained in section:  12.3.1.1 [((<Time To Start Using Object-Oriented Programming Paradigm|tut-gtk2-dnd-intro#Time To Start Using Object-Oriented Programming Paradigm>))].




    ((*minimal-rb-cr.rb*))

    {{image_right("1203-min-cr-rb-prg-s1.png")}}
     #!/usr/bin/env ruby

     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk

     window=CairoWindow.new("Minimal Cairo Program")
     window.show_all
     Gtk.main

    As you can see, if you run this program it displays an empty grey cairo drawing area.

# ((*"Establishing Gtk/Cairo Framework To Run The Tutorial Example Code"*))

    However, as the title of this paragraph (12.3.0.1) suggests, we need the Gtk/Cairo framework because we would like to run some cairo code of our own. It would be desirable, if we had a template file into which we could always include the new code. This is what we will explain next.

# (12.3.0.1.2)
:The Template Ruby File To Run Arbitrary Cairo Code:
    (12.3.0.1.2){{br}}

    Indeed, to create the template we are going to use the code shown in the above 'minimal-rb-cr.rb'program. Eventually, the new template will act as wrapper code, with a stub method called((*draw(cr, drawing_area).*)) Basically, the template is an executable program in which you create a new class inheriting directly from the CairoWindow, and overriding its((*draw*))method. This 'draw' method is where you will place your cairo code. Lets look at the empty ready to use template:

     #!/usr/bin/env ruby
     $: << '~/work/HikiLib'
     require 'hiki2-gtk-w-cairo.rb'
     include HikiGtk
     class MyCairoCodeClass < CairoWindow            # Your own CairoWindow class
       def draw(cr, drawing_area)                    # Override the draw method
         width, height = drawing_area.window.size    # You will likely need w, h
         # Your code comes here:                     #
         # ---------------------                     #
               o o o                                 # Your code comes here.
         # ---------------------                     #
       end                                           #
     end                                             #
     window=MyCairoCodeClass.new        # Replace 'CairoWindow' with your new class name.
                                        # Optionally, supply the your name to the constructor.
     window.show_all
     Gtk.main

    The lines containing comments are the additions that turn the above 'minimal-rb-cr.rb' program into our template file. Note, your cairo code will go between the two dashed lines. Also do not overlook the following line:

         width, height = drawing_area.window.size    # You will likely need w, h

    As the comment suggests, you are likely to need the size of the cairo drawing area, especially, when you would want the x,y coordinates depend on the size of the cairo "canvas". I suggest, you make it a habit to always include this line, hence it should be part of the template code.

    :A Real Life Example With Runing Cairo Code  

        (12.3.0.1.2.1)    {{image_right("1203-smiley-face-s1.png")}}
        In Michael Urman's Cairo tutorial called ((<Cairo Tutorial for PyGTK Programmers|URL:http://www.tortall.net/mu/wiki/PyGTKCairoTutorial>)), where Michael creates a similar Gtk/Cairo framework as is ours, you can find a neat piece of Python cairo code generating a((*smiley face.*)) I translated his smiley face Python code to Ruby, and run it in our Ruby Gtk/Cairo wrapper.

        ((*smiley-face.rb*))

         #!/usr/bin/env ruby
         $: << '~/work/HikiLib'
         require 'hiki2-gtk-w-cairo.rb'
         include HikiGtk

         class SmileyFace < CairoWindow
           def draw(cr, drawing_area)

             width, height = drawing_area.window.size

             # Your code comes here:
             # ---------------------
             # Fill the background with a colour of your choice
             cr.set_source_rgb(0.5, 0.5, 0.9)
             cr.rectangle(0, 0, width, height)
             cr.fill
             # draw a rectangle
             cr.set_source_rgb(1.0, 1.0, 1.0)
             cr.rectangle(10, 10, width - 20, height - 20)
             cr.fill
             # and a circle
             cr.set_source_rgb(1.0, 0.0, 0.0)
             radius = [width, height].min
             cr.arc(width / 2.0, height / 2.0, radius / 2.0 - 20, 0, 2 * Math::PI)
             cr.stroke
             cr.arc(width / 2.0, height / 2.0, radius / 3.0 - 10, Math::PI / 3, 2 * Math::PI / 3)
             cr.stroke
             # draw lines
             cr.set_source_rgb(0.0, 0.0, 0.8)
             cr.move_to(width / 3.0, height / 3.0)
             cr.rel_line_to(0, height / 6.0)
             cr.move_to(2 * width / 3.0, height / 3.0)
             cr.rel_line_to(0, height / 6.0)
             cr.stroke
             # ---------------------
           end
         end

         w=SmileyFace.new("Smiley Face")
         w.show_all
         w.set_size_request(75, 50)
         Gtk.main

Now, all you need to run these programs above is to copy the((*'hiki2-gtk-w-cairo.rb'*))module file into your working directory. You will find it near the end of this chapter (look for the section 12.3.0.3 ((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>)).

{{br}}
{{br}}





=== Running Cairo Code From Non-Gtk Ruby Environment
(12.3.0.2){{br}}

One reason I separated this paragraph from the rest of the tutorial is to stress the difference between using 'the into Ruby Gtk integrated Cairo Library' from the original pure (you can say stand-alone) Cairo Library. The first, integrated one, is available to Gtk programmers automatically, i.e. requiring Gtk module is all that is needed. But, if you want the full featured Cairo library, you need to((*require 'cairo'*)) yourself. Indeed, you can do that even if you are not running Gtk at all, that is you do not need to((*require 'gtk2',*)) and obviously, without this module you also can not run the gtk-main loop.

:Pure Ruby Cairo Library - rcairo API

    (12.3.0.2.1){{br}}
    You can check the API for the Pure Ruby Cairo Library at ((<rcairo|URL:http://cairo.rubyforge.org/doc/en/>)).

{{br}}

The examples in ((<the official Cairo Tutorial|URL:http://www.cairographics.org/tutorial/>)) run outside any particular GUI system, hence, we should not, and we do not, have any trouble running them in Ruby directly without the Gtk. But, since this is the Gtk Tutorial, which, as anybody who read most of it before any Cairo features even came up, knows that Cairo should be part of the Gtk. Unfortunately, for those of us who have been struggling to learn or master Gtk for a while, encountering Cairo outside our GUI, it may seem odd, why would anyone consider running 2D graphic programs from a text based terminal. Why would I even try to look for solutions outside my GUI environment which as I could see includes Cairo Library. It never crossed my mind, that by requiring 'cairo' module, I'd gain power of pure Cairo beyond that available in my GUI!

The fact is, that's exactly what you should do, to translate and run the official Cairo Tutorial examples in Ruby. Hence, you should forget, about Ruby's Gdk::Drawable#create_cairo_context, and resort to plain old Cairo ways of creating it via:

surface = Cairo::XyzSurface.new(width, height)
cr = Cairo::Context.new(surface)
# -- Your cairo code starts here -------------- -s-
#      o o o
# -- Your cairo code ends here ---------------- -e-
cr.target.write_to_png("output-file.png")

by replacing the word "Xyz" above with one of the following { Image, PDF, PS, SVG, ... }, including your cairo code and at the end write the contents of the cairo surface you created to the file.

In this tutorial we will favour the integrated Gtk/Cairo, rather than "stand-alone" Cairo, drawing approach, though sometimes it would be more convenient to use Cairo in a non-graphical text based environment. In paragraph 12.3.1.2.3 "((<Show Text - Glyphs|tut-gtk2-dancr-rbcatut-intro#Show Text - Glyphs>))" you can see both of these two approaches.




# ------- AS PROMMISED Hiki Gtk With Cairo Module Download File -----------
{{br}}
{{br}}
# (12.3.0.3) // uset to be marked: (12.3.XXXX)
== Hiki Gtk With Cairo Module Download File
(12.3.0.3){{br}}


To run Gtk/Cairo examples in this tutorial in the Gtk programming environment, as opposed to the console or text based terminal environment, you need to require a new module file called ((*'hiki2-gtk-w-cairo.rb'*)). But to do that you first have to download this file into either your working directory where your example programs reside, or into some common place where you keep module files, just as we did with other module files we used in the examples throughout this tutorial. If you forgot about this, you can refresh your memory in the section:  12.3.1.1 called [((<Time To Start Using Object-Oriented Programming Paradigm|tut-gtk2-dnd-intro#Time To Start Using Object-Oriented Programming Paradigm>))].

#### However, as you will see after this example program, we are going to expand and upgrade our((*HikiGtk*))module to include background manipulation functionality.

Following is the module file you need to download and save as just explained:

# Minimal Gtk/Cairo module; introducing Cairo in Tutorial I'm writing on Hiki, where
# Hiki="http://ruby-gnome2.sourceforge.jp/hiki.cgi?c=view;p=tut-gtk2-dancr-rbcatut"
#
# FILE: hiki2-gtk-w-cairo.rb
# RELEASE: 1.2

module HikiGtk
   require 'gtk2'
   require 'cairo'

   class BasicWindow < Gtk::Window
     def initialize(title = nil)
       super(Gtk::Window::TOPLEVEL)
       set_title(title) if title
       set_size_request(250, 200)
       border_width = 10
       signal_connect("key_press_event") do |widget, event|
         if event.state.control_mask? and event.keyval == Gdk::Keyval::GDK_q
           destroy
           true
         else
           false
         end
       end
       signal_connect("delete_event") { |widget, event| quit }
       signal_connect("destroy") { Gtk.main_quit }
       puts "DEBUG:initialize completed in class BasicWindow"
     end
     def quit
       puts "DEBUG: quiting Hiki example"
       destroy
       false
     end
   end

   class CairoWindow < BasicWindow
     PI = Math::PI
     RAD2DEG = 180/PI
     DEG2RAD = PI/180

     BLACK=[0,0,0];     RED=[1,0,0];  GREEN=[0,1,0];  BLUE=[0,0,1];
     TURQUOISE=[0,1,1]; PINK=[1,0,1]; YELLOW=[1,1,0]; WHITE=[1,1,1]
     GRAY=[0.8,0.8,0.8]

     def initialize(title=nil)
       super
       unless Gdk.cairo_available?
         raise "This program requires GTK+ 2.8.0 or later and cairo support"
       end
       drawing_area = Gtk::DrawingArea.new
       add(drawing_area)
       drawing_area.signal_connect("expose_event") do |w, e|
         cr = w.window.create_cairo_context
         cr.rectangle(e.area.x, e.area.y, e.area.width, e.area.height)
         cr.clip()
         draw(cr, w)
       end
       puts "DEBUG:initialize completed in class CairoWindow"
     end
     def draw(cr, drawing_area)
       width, height = drawing_area.window.size
       # Fill the background with gray
       cr.set_source_rgb(GRAY)
       cr.rectangle(0, 0, width, height)
       cr.fill()
     end
   end
end



# ------- REQUIRED FILES: -----------
{{br}}
{{br}}

:To run the examples in this article download the following files into your program directory:

    (12.3.0.4){{br}}
    * hiki2-gtk-w-cairo.rb' .......... module file; see section 12.3.0.3 ((<Hiki Gtk With Cairo Module Download File|tut-gtk2-dancr-rbcatut-intro#Hiki Gtk With Cairo Module Download File>)).