Ticket #3454: 0001-Port-to-Cairo.patch

File 0001-Port-to-Cairo.patch, 13.8 KB (added by manuq, 12 years ago)
  • clock.py

    From 40ae77291c5de5dded5a89d87b5d2b645c3c86aa Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Manuel=20Qui=C3=B1ones?= <manuq@laptop.org>
    Date: Tue, 27 Dec 2011 13:25:31 -0300
    Subject: [PATCH clock 1/2] Port to Cairo
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    This will ease the port to GTK+ 3, and it gains in nicer graphics,
    sharp edges now have antialiasing.
    
    Signed-off-by: Manuel Quiñones <manuq@laptop.org>
    ---
     clock.py |  182 ++++++++++++++++++++++++++++++++++----------------------------
     1 files changed, 100 insertions(+), 82 deletions(-)
    
    diff --git a/clock.py b/clock.py
    index 1893b77..7635b2e 100755
    a b import pygtk 
    7070import gtk
    7171from gtk import gdk
    7272import pango
     73import cairo
     74import pangocairo
     75import rsvg
    7376
    7477OLD_TOOLBAR = False
    7578try:
    except ImportError: 
    8285import math
    8386from datetime import datetime
    8487import threading
    85 import gc
    8688import re
    8789
    8890from pgettext import pgettext as _
    from pgettext import pgettext as _ 
    9092from sugar.activity import activity
    9193from sugar.graphics.toggletoolbutton import ToggleToolButton
    9294from sugar.graphics.radiotoolbutton import RadioToolButton
     95from sugar.graphics import style
    9396
    9497from speaker import Speaker
    9598from timewriter import TimeWriter
    class ClockFace(gtk.DrawingArea): 
    477480        # The display mode of the clock
    478481        self._mode = _MODE_SIMPLE_CLOCK
    479482
    480         # SVG Background cache
    481         self._cache_pixbuf = None
    482         self._radius = -1
     483        # SVG Background handle
     484        self._svg_handle = None
    483485
    484         # The graphic context used for drawings
    485         self._gc = None
     486        self._radius = -1
    486487        self._line_width = 2
    487488
    488489        # Color codes (approved colors for XO screen:
    489490        # http://wiki.laptop.org/go/XO_colors)
    490         colormap = self.get_colormap()
    491491
    492492        # XO Medium Blue
    493         self._COLOR_HOURS = colormap.alloc_color("#005FE4")
     493        self._COLOR_HOURS = "#005FE4"
    494494
    495495        # XO Medium Green
    496         self._COLOR_MINUTES = colormap.alloc_color("#00B20D")
     496        self._COLOR_MINUTES = "#00B20D"
    497497
    498498        # XO Medium Red
    499         self._COLOR_SECONDS = colormap.alloc_color("#E6000A")
     499        self._COLOR_SECONDS = "#E6000A"
    500500
    501501        # White
    502         self._COLOR_WHITE = colormap.alloc_color("#FFFFFF")
     502        self._COLOR_WHITE = "#FFFFFF"
    503503
    504504        # Black
    505         self._COLOR_BLACK = colormap.alloc_color("#000000")
     505        self._COLOR_BLACK = "#000000"
    506506
    507507        # gtk.Widget signals
    508508        self.connect("expose-event", self._expose_cb)
    class ClockFace(gtk.DrawingArea): 
    538538        self._height = allocation.height
    539539        self._line_width = int(self._radius / 150)
    540540
    541         # Reload the cached pixbuf
    542         self._cache_pixbuf = gdk.pixbuf_new_from_file_at_size("clock.svg",
    543           2 * self._radius, 2 * self._radius)
    544         gc.collect()  # Reclaim memory from old pixbuf
     541        # Reload the svg handle
     542        self._svg_handle = rsvg.Handle(file="clock.svg")
    545543
    546544        self.initialized = True
    547545
    class ClockFace(gtk.DrawingArea): 
    558556            self.queue_resize()
    559557
    560558        if self._active:
    561             self._gc = self.window.new_gc()
    562 
    563559            if self._mode == _MODE_NICE_CLOCK:
    564560                self._draw_nice_clock()
    565561            elif self._mode == _MODE_SIMPLE_CLOCK:
    class ClockFace(gtk.DrawingArea): 
    608604        seconds_length = 2 * self._radius / 60 * self._time.second
    609605
    610606        # Fill background
    611         self._gc.set_line_attributes(self._line_width, gdk.LINE_SOLID, \
    612                 gdk.CAP_BUTT, gdk.JOIN_BEVEL)
    613         self._gc.set_foreground(self._COLOR_WHITE)
    614         self.window.draw_rectangle(self._gc, True, \
    615                 int(self._center_x - 1.1 * self._radius), \
    616                 int(self._center_y - 0.8 * self._radius), \
    617                 int(2.2 * self._radius), \
    618                 int(0.55 * self._radius))
     607        cr = self.window.cairo_create()
     608        cr.set_source_rgba(*style.Color(self._COLOR_WHITE).get_rgba())
     609        cr.rectangle(int(self._center_x - 1.1 * self._radius),
     610                     int(self._center_y - 0.8 * self._radius),
     611                     int(2.2 * self._radius),
     612                     int(0.55 * self._radius))
     613        cr.fill()
    619614
    620615        h = int(0.15 * self._radius)
    621616        x = int(self._center_x - self._radius)
    622617
    623618        # Hours scale
    624         self._gc.set_foreground(self._COLOR_HOURS)
     619        cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba())
    625620        y = int(self._center_y - 0.75 * self._radius)
    626         self.window.draw_rectangle(self._gc, True, x, y, hours_length, h)
     621        cr.rectangle(x, y, hours_length, h)
     622        cr.fill()
    627623
    628624        # Minutes scale
    629         self._gc.set_foreground(self._COLOR_MINUTES)
     625        cr.set_source_rgba(*style.Color(self._COLOR_MINUTES).get_rgba())
    630626        y = int(self._center_y - 0.60 * self._radius)
    631         self.window.draw_rectangle(self._gc, True, x, y, minutes_length, h)
     627        cr.rectangle(x, y, minutes_length, h)
     628        cr.fill()
    632629
    633630        # Seconds scale
    634         self._gc.set_foreground(self._COLOR_SECONDS)
     631        cr.set_source_rgba(*style.Color(self._COLOR_SECONDS).get_rgba())
    635632        y = int(self._center_y - 0.45 * self._radius)
    636         self.window.draw_rectangle(self._gc, True, x, y, seconds_length, h)
     633        cr.rectangle(x, y, seconds_length, h)
     634        cr.fill()
    637635
    638636    def _draw_time(self):
    639637        """Draw the time in colors (digital display).
    class ClockFace(gtk.DrawingArea): 
    655653        markup_time = self._time.strftime(markup)
    656654        #markup_time = time.strftime(markup)
    657655
    658         self._gc.set_foreground(self._COLOR_BLACK)
     656        cr = self.window.cairo_create()
     657        cr = pangocairo.CairoContext(cr)
     658        cr.set_source_rgba(*style.Color(self._COLOR_BLACK).get_rgba())
     659        pango_layout = cr.create_layout()
    659660        d = int(self._center_y + 0.3 * self._radius)
    660         self._draw_markup(self._center_x, d, markup_time)
     661        pango_layout.set_markup(markup_time)
     662        dx, dy = pango_layout.get_pixel_size()
     663        pango_layout.set_alignment(pango.ALIGN_CENTER)
     664        cr.translate(self._center_x - dx / 2.0, d - dy / 2.0)
     665        cr.show_layout(pango_layout)
    661666
    662667    def _draw_simple_clock(self):
    663668        """Draw the simple clock variants.
    class ClockFace(gtk.DrawingArea): 
    671676        The simple clock background is a white disk, with hours and minutes
    672677        ticks, and the hour numbers.
    673678        """
     679        cr = self.window.cairo_create()
     680        cr.set_line_width(4 * self._line_width)
     681
    674682        # Simple clock background
    675         self._gc.set_foreground(self._COLOR_WHITE)
    676         x_delta = self._center_x - self._radius
    677         y_delta = self._center_y - self._radius
    678 
    679         self.window.draw_arc(self._gc, True, x_delta, y_delta,
    680           2 * self._radius, 2 * self._radius, 0, 360 * 64)
    681         self._gc.set_foreground(self.get_style().fg[gtk.STATE_NORMAL])
    682         self._gc.set_line_attributes(4 * self._line_width,
    683           gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND)
    684         self.window.draw_arc(self._gc, False, x_delta, y_delta,
    685           2 * self._radius, 2 * self._radius, 0, 360 * 64)
     683        cr.set_source_rgba(*style.Color(self._COLOR_WHITE).get_rgba())
     684        cr.arc(self._width / 2, self._height / 2, self._radius, 0, 2 * math.pi)
     685        cr.fill_preserve()
     686        cr.set_source_rgba(*style.Color(self._COLOR_BLACK).get_rgba())
     687        cr.stroke()
    686688
    687689        # Clock ticks
    688         self._gc.set_line_attributes(4 * self._line_width,
    689           gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND)
    690690        for i in xrange(60):
    691691            if i % 15 == 0:
    692692                inset = 0.175 * self._radius
    class ClockFace(gtk.DrawingArea): 
    697697
    698698            cos = math.cos(i * math.pi / 30.0)
    699699            sin = math.sin(i * math.pi / 30.0)
    700             self.window.draw_line(self._gc,
    701               int(self._center_x + (self._radius - inset) * cos),
    702               int(self._center_y + (self._radius - inset) * sin),
    703               int(self._center_x + self._radius * cos),
    704               int(self._center_y + self._radius * sin))
     700            cr.move_to(int(self._center_x + (self._radius - inset) * cos),
     701                       int(self._center_y + (self._radius - inset) * sin))
     702            cr.line_to(int(self._center_x + self._radius * cos),
     703                       int(self._center_y + self._radius * sin))
     704            cr.stroke()
    705705
    706706    def _draw_nice_background(self):
    707707        """Draw the nice clock background.
    708708
    709709        The background has been loaded from the clock.svg file to a
    710         pixbuf, and we just draw this pixbuf onto the pixmap where we
    711         will be drawing the hands.
    712         """
    713         # We draw the background from the SVG pixbuf
    714         self.window.draw_pixbuf(None, self._cache_pixbuf,
    715           0, 0, self._center_x - self._radius, self._center_y - self._radius)
     710        rsvg handle, and we just transform this handle and render it
     711        with cairo.
     712        """
     713        # We transform the background SVG
     714        cr = self.window.cairo_create()
     715        scale_x = self._radius * 2.0 / self._svg_handle.props.width
     716        scale_y = self._radius * 2.0 / self._svg_handle.props.height
     717        matrix = cairo.Matrix(xx=scale_x, yy=scale_y,
     718                              x0=self._center_x - self._radius,
     719                              y0=self._center_y - self._radius)
     720        cr.transform(matrix)
     721        self._svg_handle.render_cairo(cr)
    716722
    717723    def _draw_nice_clock(self):
    718724        """Draw the nice clock.
    class ClockFace(gtk.DrawingArea): 
    727733        minutes = self._time.minute
    728734        seconds = self._time.second
    729735
     736        cr = self.window.cairo_create()
     737        cr.set_line_cap(cairo.LINE_CAP_ROUND)
     738
    730739        # Hour hand:
    731740        # The hour hand is rotated 30 degrees (pi/6 r) per hour +
    732741        # 1/2 a degree (pi/360) per minute
    733         self._gc.set_foreground(self._COLOR_HOURS)
    734         self._gc.set_line_attributes(8 * self._line_width,
    735           gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND)
    736         self.window.draw_line(self._gc, self._center_x, self._center_y,
    737           int(self._center_x + self._radius * 0.5 *
    738           math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)),
    739           int(self._center_y + self._radius * 0.5 *
    740           - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes)))
     742        cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba())
     743        cr.set_line_width(8 * self._line_width)
     744        cr.move_to(self._center_x, self._center_y)
     745        cr.line_to(int(self._center_x + self._radius * 0.5 *
     746            math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)),
     747            int(self._center_y + self._radius * 0.5 *
     748            - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes)))
     749        cr.stroke()
    741750
    742751        # Minute hand:
    743752        # The minute hand is rotated 6 degrees (pi/30 r) per minute
    744         self._gc.set_foreground(self._COLOR_MINUTES)
    745         self._gc.set_line_attributes(6 * self._line_width,
    746           gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND)
    747         self.window.draw_line(self._gc, self._center_x, self._center_y,
    748                 int(self._center_x + self._radius * 0.8 *
     753        cr.set_source_rgba(*style.Color(self._COLOR_MINUTES).get_rgba())
     754        cr.set_line_width(6 * self._line_width)
     755        cr.move_to(self._center_x, self._center_y)
     756        cr.line_to(int(self._center_x + self._radius * 0.8 *
    749757                math.sin(math.pi / 30 * minutes)),
    750                 int(self._center_y + self._radius * 0.8 *
     758                   int(self._center_y + self._radius * 0.8 *
    751759                - math.cos(math.pi / 30 * minutes)))
     760        cr.stroke()
    752761
    753762        # Seconds hand:
    754763        # Operates identically to the minute hand
    755         self._gc.set_foreground(self._COLOR_SECONDS)
    756         self._gc.set_line_attributes(2 * self._line_width,
    757           gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND)
    758         self.window.draw_line(self._gc, self._center_x, self._center_y,
    759                 int(self._center_x + self._radius * 0.7 *
     764        cr.set_source_rgba(*style.Color(self._COLOR_SECONDS).get_rgba())
     765        cr.set_line_width(2 * self._line_width)
     766        cr.move_to(self._center_x, self._center_y)
     767        cr.line_to(int(self._center_x + self._radius * 0.7 *
    760768                math.sin(math.pi / 30 * seconds)),
    761769                int(self._center_y + self._radius * 0.7 *
    762770                - math.cos(math.pi / 30 * seconds)))
     771        cr.stroke()
    763772
    764773    def _draw_numbers(self):
    765774        """Draw the numbers of the hours.
    766775        """
    767         self._gc.set_foreground(self._COLOR_HOURS)
     776        cr = self.window.cairo_create()
     777        cr = pangocairo.CairoContext(cr)
     778        cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba())
     779        pango_layout = cr.create_layout()
    768780
    769781        for i in xrange(12):
    770782            # TRANS: The format of the font used to print hour
    class ClockFace(gtk.DrawingArea): 
    772784            hour_number = _("Hour Number",
    773785              '<markup><span lang="en" ' +
    774786              'font_desc="Sans Bold 20">%d</span></markup>') % (i + 1)
    775             self._draw_markup(self._center_x + 0.75 * \
    776             self._radius * math.cos((i - 2) * math.pi / 6.0), \
    777             self._center_y + 0.75 * self._radius * \
    778             math.sin((i - 2) * math.pi / 6.0), hour_number)
     787            cr.save()
     788            dx, dy = pango_layout.get_pixel_size()
     789            cr.translate(- dx / 2.0 + self._center_x + 0.75 *
     790                self._radius * math.cos((i - 2) * math.pi / 6.0),
     791                - dy / 2.0 + self._center_y + 0.75 * self._radius *
     792                math.sin((i - 2) * math.pi / 6.0))
     793            pango_layout.set_markup(hour_number)
     794            cr.update_layout(pango_layout)
     795            cr.show_layout(pango_layout)
     796            cr.restore()
    779797
    780798    def _redraw_canvas(self):
    781799        """Force a redraw of the clock on the screen.