From 2c96f4dbdd858bf5ebf400ca5f2ec2a3efd8b64c Mon Sep 17 00:00:00 2001
Message-Id: <2c96f4dbdd858bf5ebf400ca5f2ec2a3efd8b64c.1336393804.git.humitos@gmail.com>
From: Manuel Kaufmann <humitos@gmail.com>
Date: Mon, 7 May 2012 09:29:51 -0300
Subject: [PATCH Clock] 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.
Original Patch: Manuel Quiñones <manuq@laptop.org>
I've just made some modification so it applies in the latest git version of Clock Activity.
Signed-off-by: Manuel Kaufmann <humitos@gmail.com>
---
clock.py | 183 ++++++++++++++++++++++++++++++++++----------------------------
1 file changed, 100 insertions(+), 83 deletions(-)
diff --git a/clock.py b/clock.py
index 1219aee..9715f65 100755
a
|
b
|
More about clocks and time in the World |
66 | 66 | import gobject |
67 | 67 | gobject.threads_init() |
68 | 68 | |
69 | | import pygtk |
70 | 69 | import gtk |
71 | 70 | from gtk import gdk |
72 | 71 | import pango |
| 72 | import cairo |
| 73 | import pangocairo |
| 74 | import rsvg |
73 | 75 | import gst |
74 | 76 | |
75 | 77 | OLD_TOOLBAR = False |
… |
… |
except ImportError: |
83 | 85 | import math |
84 | 86 | from datetime import datetime |
85 | 87 | import threading |
86 | | import gc |
87 | 88 | import re |
88 | 89 | |
89 | 90 | from pgettext import pgettext as _ |
… |
… |
from pgettext import pgettext as _ |
91 | 92 | from sugar.activity import activity |
92 | 93 | from sugar.graphics.toggletoolbutton import ToggleToolButton |
93 | 94 | from sugar.graphics.radiotoolbutton import RadioToolButton |
| 95 | from sugar.graphics import style |
94 | 96 | |
95 | 97 | from speaker import Speaker |
96 | 98 | from timewriter import TimeWriter |
… |
… |
class ClockFace(gtk.DrawingArea): |
491 | 493 | # The display mode of the clock |
492 | 494 | self._mode = _MODE_SIMPLE_CLOCK |
493 | 495 | |
494 | | # SVG Background cache |
495 | | self._cache_pixbuf = None |
496 | | self._radius = -1 |
| 496 | # SVG Background handle |
| 497 | self._svg_handle = None |
497 | 498 | |
498 | | # The graphic context used for drawings |
499 | | self._gc = None |
| 499 | self._radius = -1 |
500 | 500 | self._line_width = 2 |
501 | 501 | |
502 | 502 | # Color codes (approved colors for XO screen: |
503 | 503 | # http://wiki.laptop.org/go/XO_colors) |
504 | | colormap = self.get_colormap() |
505 | 504 | |
506 | 505 | # XO Medium Blue |
507 | | self._COLOR_HOURS = colormap.alloc_color("#005FE4") |
| 506 | self._COLOR_HOURS = "#005FE4" |
508 | 507 | |
509 | 508 | # XO Medium Green |
510 | | self._COLOR_MINUTES = colormap.alloc_color("#00B20D") |
| 509 | self._COLOR_MINUTES = "#00B20D" |
511 | 510 | |
512 | 511 | # XO Medium Red |
513 | | self._COLOR_SECONDS = colormap.alloc_color("#E6000A") |
| 512 | self._COLOR_SECONDS = "#E6000A" |
514 | 513 | |
515 | 514 | # White |
516 | | self._COLOR_WHITE = colormap.alloc_color("#FFFFFF") |
| 515 | self._COLOR_WHITE = "#FFFFFF" |
517 | 516 | |
518 | 517 | # Black |
519 | | self._COLOR_BLACK = colormap.alloc_color("#000000") |
| 518 | self._COLOR_BLACK = "#000000" |
520 | 519 | |
521 | 520 | # gtk.Widget signals |
522 | 521 | self.connect("expose-event", self._expose_cb) |
… |
… |
class ClockFace(gtk.DrawingArea): |
552 | 551 | self._height = allocation.height |
553 | 552 | self._line_width = int(self._radius / 150) |
554 | 553 | |
555 | | # Reload the cached pixbuf |
556 | | self._cache_pixbuf = gdk.pixbuf_new_from_file_at_size("clock.svg", |
557 | | 2 * self._radius, 2 * self._radius) |
558 | | gc.collect() # Reclaim memory from old pixbuf |
| 554 | # Reload the svg handle |
| 555 | self._svg_handle = rsvg.Handle(file="clock.svg") |
559 | 556 | |
560 | 557 | self.initialized = True |
561 | 558 | |
… |
… |
class ClockFace(gtk.DrawingArea): |
572 | 569 | self.queue_resize() |
573 | 570 | |
574 | 571 | if self._active: |
575 | | self._gc = self.window.new_gc() |
576 | | |
577 | 572 | if self._mode == _MODE_NICE_CLOCK: |
578 | 573 | self._draw_nice_clock() |
579 | 574 | elif self._mode == _MODE_SIMPLE_CLOCK: |
… |
… |
class ClockFace(gtk.DrawingArea): |
622 | 617 | seconds_length = 2 * self._radius / 60 * self._time.second |
623 | 618 | |
624 | 619 | # Fill background |
625 | | self._gc.set_line_attributes(self._line_width, gdk.LINE_SOLID, \ |
626 | | gdk.CAP_BUTT, gdk.JOIN_BEVEL) |
627 | | self._gc.set_foreground(self._COLOR_WHITE) |
628 | | self.window.draw_rectangle(self._gc, True, \ |
629 | | int(self._center_x - 1.1 * self._radius), \ |
630 | | int(self._center_y - 0.8 * self._radius), \ |
631 | | int(2.2 * self._radius), \ |
632 | | int(0.55 * self._radius)) |
| 620 | cr = self.window.cairo_create() |
| 621 | cr.set_source_rgba(*style.Color(self._COLOR_WHITE).get_rgba()) |
| 622 | cr.rectangle(int(self._center_x - 1.1 * self._radius), |
| 623 | int(self._center_y - 0.8 * self._radius), |
| 624 | int(2.2 * self._radius), |
| 625 | int(0.55 * self._radius)) |
| 626 | cr.fill() |
633 | 627 | |
634 | 628 | h = int(0.15 * self._radius) |
635 | 629 | x = int(self._center_x - self._radius) |
636 | 630 | |
637 | 631 | # Hours scale |
638 | | self._gc.set_foreground(self._COLOR_HOURS) |
| 632 | cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba()) |
639 | 633 | y = int(self._center_y - 0.75 * self._radius) |
640 | | self.window.draw_rectangle(self._gc, True, x, y, hours_length, h) |
| 634 | cr.rectangle(x, y, hours_length, h) |
| 635 | cr.fill() |
641 | 636 | |
642 | 637 | # Minutes scale |
643 | | self._gc.set_foreground(self._COLOR_MINUTES) |
| 638 | cr.set_source_rgba(*style.Color(self._COLOR_MINUTES).get_rgba()) |
644 | 639 | y = int(self._center_y - 0.60 * self._radius) |
645 | | self.window.draw_rectangle(self._gc, True, x, y, minutes_length, h) |
| 640 | cr.rectangle(x, y, minutes_length, h) |
| 641 | cr.fill() |
646 | 642 | |
647 | 643 | # Seconds scale |
648 | | self._gc.set_foreground(self._COLOR_SECONDS) |
| 644 | cr.set_source_rgba(*style.Color(self._COLOR_SECONDS).get_rgba()) |
649 | 645 | y = int(self._center_y - 0.45 * self._radius) |
650 | | self.window.draw_rectangle(self._gc, True, x, y, seconds_length, h) |
| 646 | cr.rectangle(x, y, seconds_length, h) |
| 647 | cr.fill() |
651 | 648 | |
652 | 649 | def _draw_time(self): |
653 | 650 | """Draw the time in colors (digital display). |
… |
… |
class ClockFace(gtk.DrawingArea): |
669 | 666 | markup_time = self._time.strftime(markup) |
670 | 667 | #markup_time = time.strftime(markup) |
671 | 668 | |
672 | | self._gc.set_foreground(self._COLOR_BLACK) |
| 669 | cr = self.window.cairo_create() |
| 670 | cr = pangocairo.CairoContext(cr) |
| 671 | cr.set_source_rgba(*style.Color(self._COLOR_BLACK).get_rgba()) |
| 672 | pango_layout = cr.create_layout() |
673 | 673 | d = int(self._center_y + 0.3 * self._radius) |
674 | | self._draw_markup(self._center_x, d, markup_time) |
| 674 | pango_layout.set_markup(markup_time) |
| 675 | dx, dy = pango_layout.get_pixel_size() |
| 676 | pango_layout.set_alignment(pango.ALIGN_CENTER) |
| 677 | cr.translate(self._center_x - dx / 2.0, d - dy / 2.0) |
| 678 | cr.show_layout(pango_layout) |
675 | 679 | |
676 | 680 | def _draw_simple_clock(self): |
677 | 681 | """Draw the simple clock variants. |
… |
… |
class ClockFace(gtk.DrawingArea): |
685 | 689 | The simple clock background is a white disk, with hours and minutes |
686 | 690 | ticks, and the hour numbers. |
687 | 691 | """ |
| 692 | cr = self.window.cairo_create() |
| 693 | cr.set_line_width(4 * self._line_width) |
| 694 | |
688 | 695 | # Simple clock background |
689 | | self._gc.set_foreground(self._COLOR_WHITE) |
690 | | x_delta = self._center_x - self._radius |
691 | | y_delta = self._center_y - self._radius |
692 | | |
693 | | self.window.draw_arc(self._gc, True, x_delta, y_delta, |
694 | | 2 * self._radius, 2 * self._radius, 0, 360 * 64) |
695 | | self._gc.set_foreground(self.get_style().fg[gtk.STATE_NORMAL]) |
696 | | self._gc.set_line_attributes(4 * self._line_width, |
697 | | gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND) |
698 | | self.window.draw_arc(self._gc, False, x_delta, y_delta, |
699 | | 2 * self._radius, 2 * self._radius, 0, 360 * 64) |
| 696 | cr.set_source_rgba(*style.Color(self._COLOR_WHITE).get_rgba()) |
| 697 | cr.arc(self._width / 2, self._height / 2, self._radius, 0, 2 * math.pi) |
| 698 | cr.fill_preserve() |
| 699 | cr.set_source_rgba(*style.Color(self._COLOR_BLACK).get_rgba()) |
| 700 | cr.stroke() |
700 | 701 | |
701 | 702 | # Clock ticks |
702 | | self._gc.set_line_attributes(4 * self._line_width, |
703 | | gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND) |
704 | 703 | for i in xrange(60): |
705 | 704 | if i % 15 == 0: |
706 | 705 | inset = 0.175 * self._radius |
… |
… |
class ClockFace(gtk.DrawingArea): |
711 | 710 | |
712 | 711 | cos = math.cos(i * math.pi / 30.0) |
713 | 712 | sin = math.sin(i * math.pi / 30.0) |
714 | | self.window.draw_line(self._gc, |
715 | | int(self._center_x + (self._radius - inset) * cos), |
716 | | int(self._center_y + (self._radius - inset) * sin), |
717 | | int(self._center_x + self._radius * cos), |
718 | | int(self._center_y + self._radius * sin)) |
| 713 | cr.move_to(int(self._center_x + (self._radius - inset) * cos), |
| 714 | int(self._center_y + (self._radius - inset) * sin)) |
| 715 | cr.line_to(int(self._center_x + self._radius * cos), |
| 716 | int(self._center_y + self._radius * sin)) |
| 717 | cr.stroke() |
719 | 718 | |
720 | 719 | def _draw_nice_background(self): |
721 | 720 | """Draw the nice clock background. |
722 | 721 | |
723 | 722 | The background has been loaded from the clock.svg file to a |
724 | | pixbuf, and we just draw this pixbuf onto the pixmap where we |
725 | | will be drawing the hands. |
726 | | """ |
727 | | # We draw the background from the SVG pixbuf |
728 | | self.window.draw_pixbuf(None, self._cache_pixbuf, |
729 | | 0, 0, self._center_x - self._radius, self._center_y - self._radius) |
| 723 | rsvg handle, and we just transform this handle and render it |
| 724 | with cairo. |
| 725 | """ |
| 726 | # We transform the background SVG |
| 727 | cr = self.window.cairo_create() |
| 728 | scale_x = self._radius * 2.0 / self._svg_handle.props.width |
| 729 | scale_y = self._radius * 2.0 / self._svg_handle.props.height |
| 730 | matrix = cairo.Matrix(xx=scale_x, yy=scale_y, |
| 731 | x0=self._center_x - self._radius, |
| 732 | y0=self._center_y - self._radius) |
| 733 | cr.transform(matrix) |
| 734 | self._svg_handle.render_cairo(cr) |
730 | 735 | |
731 | 736 | def _draw_nice_clock(self): |
732 | 737 | """Draw the nice clock. |
… |
… |
class ClockFace(gtk.DrawingArea): |
741 | 746 | minutes = self._time.minute |
742 | 747 | seconds = self._time.second |
743 | 748 | |
| 749 | cr = self.window.cairo_create() |
| 750 | cr.set_line_cap(cairo.LINE_CAP_ROUND) |
| 751 | |
744 | 752 | # Hour hand: |
745 | 753 | # The hour hand is rotated 30 degrees (pi/6 r) per hour + |
746 | 754 | # 1/2 a degree (pi/360) per minute |
747 | | self._gc.set_foreground(self._COLOR_HOURS) |
748 | | self._gc.set_line_attributes(8 * self._line_width, |
749 | | gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND) |
750 | | self.window.draw_line(self._gc, self._center_x, self._center_y, |
751 | | int(self._center_x + self._radius * 0.5 * |
752 | | math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)), |
753 | | int(self._center_y + self._radius * 0.5 * |
754 | | - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes))) |
| 755 | cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba()) |
| 756 | cr.set_line_width(8 * self._line_width) |
| 757 | cr.move_to(self._center_x, self._center_y) |
| 758 | cr.line_to(int(self._center_x + self._radius * 0.5 * |
| 759 | math.sin(math.pi / 6 * hours + math.pi / 360 * minutes)), |
| 760 | int(self._center_y + self._radius * 0.5 * |
| 761 | - math.cos(math.pi / 6 * hours + math.pi / 360 * minutes))) |
| 762 | cr.stroke() |
755 | 763 | |
756 | 764 | # Minute hand: |
757 | 765 | # The minute hand is rotated 6 degrees (pi/30 r) per minute |
758 | | self._gc.set_foreground(self._COLOR_MINUTES) |
759 | | self._gc.set_line_attributes(6 * self._line_width, |
760 | | gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND) |
761 | | self.window.draw_line(self._gc, self._center_x, self._center_y, |
762 | | int(self._center_x + self._radius * 0.8 * |
| 766 | cr.set_source_rgba(*style.Color(self._COLOR_MINUTES).get_rgba()) |
| 767 | cr.set_line_width(6 * self._line_width) |
| 768 | cr.move_to(self._center_x, self._center_y) |
| 769 | cr.line_to(int(self._center_x + self._radius * 0.8 * |
763 | 770 | math.sin(math.pi / 30 * minutes)), |
764 | | int(self._center_y + self._radius * 0.8 * |
| 771 | int(self._center_y + self._radius * 0.8 * |
765 | 772 | - math.cos(math.pi / 30 * minutes))) |
| 773 | cr.stroke() |
766 | 774 | |
767 | 775 | # Seconds hand: |
768 | 776 | # Operates identically to the minute hand |
769 | | self._gc.set_foreground(self._COLOR_SECONDS) |
770 | | self._gc.set_line_attributes(2 * self._line_width, |
771 | | gdk.LINE_SOLID, gdk.CAP_ROUND, gdk.JOIN_ROUND) |
772 | | self.window.draw_line(self._gc, self._center_x, self._center_y, |
773 | | int(self._center_x + self._radius * 0.7 * |
| 777 | cr.set_source_rgba(*style.Color(self._COLOR_SECONDS).get_rgba()) |
| 778 | cr.set_line_width(2 * self._line_width) |
| 779 | cr.move_to(self._center_x, self._center_y) |
| 780 | cr.line_to(int(self._center_x + self._radius * 0.7 * |
774 | 781 | math.sin(math.pi / 30 * seconds)), |
775 | 782 | int(self._center_y + self._radius * 0.7 * |
776 | 783 | - math.cos(math.pi / 30 * seconds))) |
| 784 | cr.stroke() |
777 | 785 | |
778 | 786 | def _draw_numbers(self): |
779 | 787 | """Draw the numbers of the hours. |
780 | 788 | """ |
781 | | self._gc.set_foreground(self._COLOR_HOURS) |
| 789 | cr = self.window.cairo_create() |
| 790 | cr = pangocairo.CairoContext(cr) |
| 791 | cr.set_source_rgba(*style.Color(self._COLOR_HOURS).get_rgba()) |
| 792 | pango_layout = cr.create_layout() |
782 | 793 | |
783 | 794 | for i in xrange(12): |
784 | 795 | # TRANS: The format of the font used to print hour |
… |
… |
class ClockFace(gtk.DrawingArea): |
786 | 797 | hour_number = _("Hour Number", |
787 | 798 | '<markup><span lang="en" ' + |
788 | 799 | 'font_desc="Sans Bold 20">%d</span></markup>') % (i + 1) |
789 | | self._draw_markup(self._center_x + 0.75 * \ |
790 | | self._radius * math.cos((i - 2) * math.pi / 6.0), \ |
791 | | self._center_y + 0.75 * self._radius * \ |
792 | | math.sin((i - 2) * math.pi / 6.0), hour_number) |
| 800 | cr.save() |
| 801 | dx, dy = pango_layout.get_pixel_size() |
| 802 | cr.translate(- dx / 2.0 + self._center_x + 0.75 * |
| 803 | self._radius * math.cos((i - 2) * math.pi / 6.0), |
| 804 | - dy / 2.0 + self._center_y + 0.75 * self._radius * |
| 805 | math.sin((i - 2) * math.pi / 6.0)) |
| 806 | pango_layout.set_markup(hour_number) |
| 807 | cr.update_layout(pango_layout) |
| 808 | cr.show_layout(pango_layout) |
| 809 | cr.restore() |
793 | 810 | |
794 | 811 | def _redraw_canvas(self): |
795 | 812 | """Force a redraw of the clock on the screen. |