Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Jourdan <b.vehikel@googlemail.com>2009-12-12 17:47:09 (GMT)
committer Thomas Jourdan <b.vehikel@googlemail.com>2009-12-12 18:34:15 (GMT)
commit8a6a80557f5e6b8bb69181ac39e1b93ba145e499 (patch)
tree34c8c471f805960e803c299e073a2bb865c83e4f
parentf5ba3b40da10c79508b2e43e9a99b3db78ba4d5b (diff)
removed *.mo files, adding html view
-rw-r--r--.gitignore3
-rw-r--r--MANIFEST4
-rw-r--r--ep_directionconstraint_none.py76
-rw-r--r--ep_layer_image.py128
-rw-r--r--ep_layer_referencepattern.py123
-rw-r--r--ep_merger_alphaimage.py116
-rw-r--r--ep_merger_border.py132
-rw-r--r--ep_merger_flip.py108
-rw-r--r--ep_merger_layermask.py134
-rw-r--r--ep_merger_rectangulartile.py112
-rw-r--r--ep_merger_straight.py81
-rw-r--r--htmltextview.py474
-rw-r--r--ka_canvas.py42
-rw-r--r--ka_debug.py10
-rw-r--r--ka_extensionpoint.py6
-rw-r--r--ka_random.py233
-rw-r--r--ka_widget.py135
-rw-r--r--kandid.glade11
-rw-r--r--locale/de/LC_MESSAGES/net.sourceforge.kandid.mobin885 -> 0 bytes
-rw-r--r--locale/de/intro.html30
-rw-r--r--locale/en/LC_MESSAGES/net.sourceforge.kandid.mobin404 -> 0 bytes
-rw-r--r--locale/en/intro.html30
-rw-r--r--model_population.py9
-rw-r--r--test_suite.py11
24 files changed, 679 insertions, 1329 deletions
diff --git a/.gitignore b/.gitignore
index 9fe2a17..43e0583 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*~
# no binaries
*.pyc
+*.mo
# no archives
*.xo
*.tar.bz2
@@ -26,5 +27,5 @@ coverage.lst
*.gpg
*.txt
# allow logo
-#!activity/activity-kandid.svg
+!activity/activity-kandid.svg
diff --git a/MANIFEST b/MANIFEST
index 1e9638f..098d89a 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,12 +1,15 @@
activity/activity.info
activity/activity-kandid.svg
locale/en/activity.linfo
+locale/en/intro.html
locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
locale/de/activity.linfo
+locale/de/intro.html
locale/de/LC_MESSAGES/net.sourceforge.kandid.mo
po/Kandid.pot
po/de.po
po/en.po
+intro/intro1.png
activity.py
ep_buzzwordconstraint_sugar.py
ep_colorconstraint_bw.py
@@ -34,6 +37,7 @@ exon_buzzword.py
exon_color.py
exon_direction.py
exon_position.py
+htmltextview.py
ka_controller.py
ka_debug.py
ka_extensionpoint.py
diff --git a/ep_directionconstraint_none.py b/ep_directionconstraint_none.py
deleted file mode 100644
index 35fb6c1..0000000
--- a/ep_directionconstraint_none.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import math
-import ka_random
-import model_constraintpool
-import model_locus
-
-DISTANCE_CONSTRAINT = 'distance'
-RADIAN_CONSTRAINT = 'radian'
-
-class NoneDirectionConstraint(model_locus.Locus):
- """NoneDirectionConstraint
- """
-
- cdef = [{'bind' : DISTANCE_CONSTRAINT,
- 'name' : 'Distance per hop',
- 'domain': model_constraintpool.FLOAT_RANGE,
- 'min' : 0.0, 'max': 0.5,
- },
- {'bind' : RADIAN_CONSTRAINT,
- 'name' : 'Rotation in radians',
- 'domain': model_constraintpool.FLOAT_RANGE,
- 'min' : -1.0*math.pi, 'max': math.pi,
- },
- ]
-
- def __init__(self, trunk):
- """Direction constraint constructor
- """
- super(NoneDirectionConstraint, self).__init__(trunk)
-
- def filter(self, radian, distance):
- """No constraints for radian and distance.
- post: (-1.0*math.pi) <= __return__[0] <= math.pi
- post: __return__[1] >= 0.0
- """
- return ka_random.radian_limitate(radian), math.fabs(distance)
-
- def randomize(self):
- """Set radian and distance to random values.
- post: (-1.0*math.pi) <= __return__[0] <= math.pi
- post: __return__[1] >= 0.0
- """
- cpool = model_constraintpool.ConstraintPool.get_pool()
- radian_constraint = cpool.get(self, RADIAN_CONSTRAINT)
- distance_constraint = cpool.get(self, DISTANCE_CONSTRAINT)
- radian = ka_random.uniform_constrained(radian_constraint)
- distance = ka_random.uniform_constrained(distance_constraint)
- return ka_random.radian_limitate(radian), math.fabs(distance)
-
- def mutate(self, radian, distance):
- """Make small random changes in radian and distance.
- post: (-1.0*math.pi) <= __return__[0] <= math.pi
- post: __return__[1] >= 0.0
- """
- cpool = model_constraintpool.ConstraintPool.get_pool()
- radian_constraint = cpool.get(self, RADIAN_CONSTRAINT)
- distance_constraint = cpool.get(self, DISTANCE_CONSTRAINT)
- radian += ka_random.jitter_constrained(radian_constraint)
- distance += ka_random.jitter_constrained(distance_constraint)
- return ka_random.radian_limitate(radian), math.fabs(distance)
diff --git a/ep_layer_image.py b/ep_layer_image.py
deleted file mode 100644
index 37f91a8..0000000
--- a/ep_layer_image.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import os
-import cairo
-import model_layer
-import ka_random
-import ka_importer
-
-class ImageLayer(model_layer.Layer):
- """ImageLayer
- """
-
- def __init__(self, trunk):
- """Constructor for bitmap image layer."""
- super(ImageLayer, self).__init__(trunk)
- self.selected_image = ''
- self.alpha_blending = 1.0
-
- def __eq__(self, other):
- """Equality based on the cells color components."""
- equal = isinstance(other, ImageLayer) \
- and super(ImageLayer, self).__eq__(other) \
- and self.selected_image == other.selected_image \
- and self.alpha_blending == other.alpha_blending
- return equal
-
- def randomize(self):
- """Randomize the layers components."""
- super(ImageLayer, self).randomize()
- image_list = ka_importer.get_rgb_image_list()
- if len(image_list) > 0:
- self.selected_image = \
- image_list[random.randint(0, len(image_list)-1)]
- self.alpha_blending = random.random()
-
- def mutate(self):
- """Make small random changes to the layers components."""
- super(ImageLayer, self).mutate()
- if ka_random.is_mutating():
- image_list = ka_importer.get_rgb_image_list()
- if len(image_list) > 0:
- self.selected_image = \
- image_list[random.randint(0, len(image_list)-1)]
- if ka_random.is_mutating():
- self.alpha_blending += ka_random.jitter(0.2)
- self.alpha_blending = ka_random.limitate(self.alpha_blending)
-
- def shuffle(self):
- """Shuffle similar componets."""
- super(ImageLayer, self).shuffle()
-
- def crossingover(self, other):
- """
- pre: isinstance(other, ImageLayer)
- pre: isinstance(self, ImageLayer)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = ImageLayer(self.get_trunk())
- cross_sequence = self.crossingover_base(new_one, other, 2)
- new_one.selected_image = self.selected_image if cross_sequence[0] \
- else other.selected_image
- new_one.alpha_blending = self.alpha_blending if cross_sequence[0] \
- else other.alpha_blending
- return new_one
-
- def render(self, ctx, width, height):
- """
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- self.begin_render(ctx, width, height)
-
- if os.path.isfile(self.selected_image):
- surface = cairo.ImageSurface.create_from_png(self.selected_image)
- ctx.translate(-0.5, -0.5)
- ctx.scale(1.0/surface.get_width(), 1.0/surface.get_height())
- ctx.set_source_surface(surface)
- ctx.paint_with_alpha(self.alpha_blending)
-
- def explain(self, formater):
- """
- pre: formater is not None
- """
- formater.alfa_item(self.alpha_blending, 'alfa blendig: %d%%' \
- % (100*self.alpha_blending))
- if os.path.isfile(self.selected_image):
- png_surface = cairo.ImageSurface.create_from_png(self.selected_image)
- width = height = 48
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
- ctx = cairo.Context(surface)
- ctx.scale(width, height)
- ctx.scale(1.0/png_surface.get_width(), 1.0/png_surface.get_height())
- ctx.set_source_surface(png_surface)
- ctx.paint()
- formater.surface_item(surface,
- 'image file:' + self.selected_image,
- self.selected_image)
-
- def copy(self):
- """The Voronoi diagram layers copy constructor.
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = ImageLayer(self.get_trunk())
- self.copy_base(new_one)
- new_one.selected_image = self.selected_image
- new_one.alpha_blending = self.alpha_blending
- return new_one
diff --git a/ep_layer_referencepattern.py b/ep_layer_referencepattern.py
deleted file mode 100644
index f04c483..0000000
--- a/ep_layer_referencepattern.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import cairo
-import model_layer
-
-image_list = []
-
-class ReferencePattern(model_layer.Layer):
- """ReferencePattern
- """
-
- def __init__(self, trunk):
- """Constructor for bitmap image layer."""
- super(ReferencePattern, self).__init__(trunk)
-
- def __eq__(self, other):
- """Equality based on the cells color components."""
- equal = isinstance(other, ReferencePattern) \
- and super(ReferencePattern, self).__eq__(other)
- return equal
-
- def randomize(self):
- """Randomize the layers components."""
- super(ReferencePattern, self).randomize()
-
- def mutate(self):
- """Make small random changes to the layers components."""
- super(ReferencePattern, self).mutate()
-
- def shuffle(self):
- """Shuffle similar componets."""
- super(ReferencePattern, self).shuffle()
-
- def crossingover(self, other):
- """
- pre: isinstance(other, ReferencePattern)
- pre: isinstance(self, ReferencePattern)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = ReferencePattern(self.get_trunk())
- return new_one
-
- def render(self, ctx, width, height):
- """
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- self.begin_render(ctx, width, height)
-
- ctx.set_source_rgba(1.0, 1.0, 1.0, 0.5)
- ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
- ctx.fill()
-
- linear = cairo.LinearGradient(-0.5, -0.5, 1.0, 1.0)
- linear.add_color_stop_rgba(0.00, 1.0, 0.0, 0.0, 1.0)
- linear.add_color_stop_rgba(1.00, 1.0, 1.0, 0.0, 0.0)
-
- ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
- ctx.set_source(linear)
- ctx.fill()
-
- ctx.set_line_width(0.05)
- ctx.set_source_rgba(0.0, 0.0, 0.0, 0.5)
- ctx.rectangle(-0.5, -0.5, 1.0, 1.0)
- ctx.stroke()
-
- ctx.set_source_rgba(1.0, 1.0, 1.0, 0.25)
- ctx.select_font_face("Sans",
- cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
- ctx.set_font_size(0.2)
- text = 'L'
- ctx.move_to(0.0, 0.0)
- ctx.show_text(text)
- ctx.set_font_size(0.03)
- for ix in range(11):
- for iy in range(11):
- ctx.move_to(ix/10.0-0.5, iy/10.0-0.5)
- ctx.show_text(str(ix)+'&'+str(iy))
-
-
- ctx.set_line_width(0.01)
- ctx.set_source_rgba(0.75, 0.75, 0.75, 0.75)
- diagonal = 0.05
- ctx.move_to(-diagonal, -diagonal)
- ctx.line_to( diagonal, diagonal)
- ctx.move_to( diagonal, -diagonal)
- ctx.line_to(-diagonal, diagonal)
- ctx.stroke()
-
- def explain(self, formater):
- """
- pre: formater is not None
- """
-# super(ReferencePattern, self).explain(formater)
- pass
-
- def copy(self):
- """The Voronoi diagram layers copy constructor.
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = ReferencePattern(self.get_trunk())
- self.copy_base(new_one)
- return new_one
diff --git a/ep_merger_alphaimage.py b/ep_merger_alphaimage.py
deleted file mode 100644
index 93ac88a..0000000
--- a/ep_merger_alphaimage.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import cairo
-import model_allele
-import ka_random
-import ka_importer
-
-class AlphaImageMerger(model_allele.Allele):
- """AlphaImageMerger
- """
- constraint_wieght = (0.0, 0.5)
-
- def __init__(self, trunk):
- """Constructor for a simple merger."""
- super(AlphaImageMerger, self).__init__(trunk)
- self.selected_image = ''
-
- def __eq__(self, other):
- """Equality based on same instance."""
- equal = isinstance(other, AlphaImageMerger) \
- and self.selected_image == other.selected_image
- return equal
-
- def randomize(self):
- """No member variables. Nothing to do."""
- image_list = ka_importer.get_alpha_image_list()
- self.selected_image = image_list[random.randint(0, len(image_list)-1)]
-
- def mutate(self):
- """No member variables. Nothing to do."""
- if ka_random.is_mutating():
- image_list = ka_importer.get_alpha_image_list()
- self.selected_image = image_list[random.randint(0, len(image_list)-1)]
-
- def shuffle(self):
- """No member variables. Nothing to do."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, AlphaImageMerger)
- pre: isinstance(self, AlphaImageMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = AlphaImageMerger(self.get_trunk())
- cross_sequence = ka_random.crossing_sequence(1)
- new_one.selected_image = self.selected_image if cross_sequence[0] \
- else other.selected_image
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # paint one layer
- ctx.save()
-
- msk_surface = cairo.ImageSurface.create_from_png(self.selected_image)
- ctx.save()
- ctx.translate(-0.5, -0.5)
- ctx.scale(1.0/msk_surface.get_width(), 1.0/msk_surface.get_height())
- ctx.mask_surface(msk_surface, 0.0, 0.0)
- ctx.restore()
-
- single_layer.render(ctx, width, height)
- ctx.restore()
-
- def explain(self):
- """
- post: len(__return__) == 3
- """
- png_surface = cairo.ImageSurface.create_from_png(self.selected_image)
- width = height = 48
- surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
- ctx = cairo.Context(surface)
- ctx.scale(width, height)
- ctx.scale(1.0/png_surface.get_width(), 1.0/png_surface.get_height())
- ctx.set_source_surface(png_surface)
- ctx.paint()
-
- return u'Transparent image merger, image ' \
- + unicode(self.selected_image), \
- surface, \
- self.selected_image
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, AlphaImageMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = AlphaImageMerger(self.get_trunk())
- new_one.selected_image = self.selected_image
- return new_one
diff --git a/ep_merger_border.py b/ep_merger_border.py
deleted file mode 100644
index 385caea..0000000
--- a/ep_merger_border.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import cairo
-import model_allele
-import ka_random
-
-class BorderMerger(model_allele.Allele):
- """BorderMerger
- inv: 0.0 <= self.border_weight <= 0.5
- inv: self.border_alfa == 0.0 or self.border_alfa == 1.0
- """
- constraint_wieght = (0.0, 0.5)
-
- def __init__(self, trunk):
- """Constructor for a simple merger."""
- super(BorderMerger, self).__init__(trunk)
- self.border_weight = 0.25
- self.border_alfa = 1.0
- self.constraint_weight = (0.0, 0.5)
-
- def __eq__(self, other):
- """Equality based on same instance."""
- equal = isinstance(other, BorderMerger) \
- and self.border_weight == other.border_weight \
- and self.border_alfa == other.border_alfa
- return equal
-
- def randomize(self):
- """No member variables. Nothing to do."""
- self.border_weight = random.uniform(self.constraint_weight[0], self.constraint_weight[1])
- self.border_alfa = 1.0 if random.randint(0, 1) > 0 else 0.0
-
- def mutate(self):
- """No member variables. Nothing to do."""
- if ka_random.is_mutating():
- self.border_weight += ka_random.jitter_constrained(self.constraint_weight)
- self.border_weight = ka_random.limitate_range(self.border_weight, self.constraint_weight[0], self.constraint_weight[1])
- if ka_random.is_mutating():
- self.border_alfa = 1.0 if random.randint(0, 1) > 0 else 0.0
-
- def shuffle(self):
- """No member variables. Nothing to do."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, BorderMerger)
- pre: isinstance(self, BorderMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = BorderMerger(self.get_trunk())
- cross_sequence = ka_random.crossing_sequence(2)
- new_one.border_weight = self.border_weight if cross_sequence[0] else other.border_weight
- new_one.border_alfa = self.border_alfa if cross_sequence[1] else other.border_alfa
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # paint one layer
- ctx.save()
- msk_surface = ctx.get_target().create_similar(cairo.CONTENT_ALPHA,
- width, height)
- msk_ctx = cairo.Context(msk_surface)
- msk_width = msk_surface.get_width()
- msk_height = msk_surface.get_height()
-
- # fill the whole background with an alpha value.
- msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
- msk_ctx.set_source_rgba(1.0, 1.0, 1.0, self.border_alfa)
- msk_ctx.paint()
-
- # fill the interior with an alpha value.
- msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
- msk_ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0-self.border_alfa)
- msk_ctx.rectangle(self.border_weight*msk_width,
- self.border_weight*msk_height,
- (1.0-2.0*self.border_weight)*msk_height,
- (1.0-2.0*self.border_weight)*msk_width)
- msk_ctx.fill()
-
- ctx.save()
- ctx.translate(-0.5, -0.5)
- ctx.scale(1.0/msk_width, 1.0/msk_height)
- ctx.mask_surface(msk_surface, 0.0, 0.0)
- ctx.restore()
-
- single_layer.render(ctx, width, height)
- ctx.restore()
-
- def explain(self):
- """
- post: len(__return__) == 3
- """
- return u'Border merger, border weight=' + unicode(self.border_weight) \
- + u' alfa=' + unicode(self.border_alfa), \
- None, \
- None
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, BorderMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = BorderMerger(self.get_trunk())
- new_one.border_weight = self.border_weight
- new_one.border_alfa = self.border_alfa
- return new_one
diff --git a/ep_merger_flip.py b/ep_merger_flip.py
deleted file mode 100644
index 078b50c..0000000
--- a/ep_merger_flip.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import ka_random
-import model_allele
-
-class FlipMerger(model_allele.Allele):
- """FlipMerger: Reverse the layer horizontally or vertically.
- inv: self.xFlip == -1.0 or self.xFlip == 1.0
- inv: self.yFlip == -1.0 or self.yFlip == 1.0
- """
-
- def __init__(self, trunk):
- """Constructor for a flip merger."""
- super(FlipMerger, self).__init__(trunk)
- self.xFlip = 1.0
- self.yFlip = 1.0
-
- def __eq__(self, other):
- """Equality based on fliping horizontally or vertically."""
- equal = isinstance(other, FlipMerger) \
- and self.xFlip == other.xFlip \
- and self.yFlip == other.yFlip
- return equal
-
- def randomize(self):
- """Randomize the layers components."""
- self.xFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
- self.yFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
-
- def mutate(self):
- """Make small random changes to the layers components."""
- if ka_random.is_mutating():
- self.xFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
- if ka_random.is_mutating():
- self.yFlip = 1.0 if random.randint(0, 1) > 0 else -1.0
-
- def shuffle(self):
- """Shuffle similar componets."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, FlipMerger)
- pre: isinstance(self, FlipMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = FlipMerger(self.get_trunk())
- cross_sequence = ka_random.crossing_sequence(2)
- new_one.xFlip = self.xFlip if cross_sequence[0] else other.xFlip
- new_one.yFlip = self.yFlip if cross_sequence[1] else other.yFlip
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # paint one layer
- ctx.scale(self.xFlip, self.yFlip)
- ctx.save()
- single_layer.render(ctx, width, height)
- ctx.restore()
-
- def explain(self):
- """
- post: len(__return__) == 3
- """
- if self.xFlip and self.yFlip:
- text = u'Flip merger: flip horizontally and vertically.'
- elif self.xFlip:
- text = u'Flip merger: flip horizontally.'
- elif self.yFlip:
- text = u'Flip merger: flip vertically.'
- else:
- text = u'Flip merger: did not flip.'
- return text, None, None
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, FlipMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = FlipMerger(self.get_trunk())
- new_one.xFlip = self.xFlip
- new_one.yFlip = self.yFlip
- return new_one
diff --git a/ep_merger_layermask.py b/ep_merger_layermask.py
deleted file mode 100644
index 90d1422..0000000
--- a/ep_merger_layermask.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import cairo
-import model_allele
-import ka_random
-
-class LayermaskMerger(model_allele.Allele):
- """LayermaskMerger
- inv: self.scale_x >= 1.0
- inv: self.scale_y >= 1.0
- """
-
- def __init__(self, trunk):
- """Constructor for a simple merger."""
- super(LayermaskMerger, self).__init__(trunk)
- self.scale_x = 1.0
- self.scale_y = 1.0
- self.constraint_scale = (1.0, 2.5)
-
- def __eq__(self, other):
- """Equality based on same instance."""
- equal = isinstance(other, LayermaskMerger) \
- and self.scale_x == other.scale_x \
- and self.scale_y == other.scale_y
- return equal
-
- def randomize(self):
- """No member variables. Nothing to do."""
- self.scale_x = random.uniform(self.constraint_scale[0], self.constraint_scale[1])
- self.scale_y = random.uniform(self.constraint_scale[0], self.constraint_scale[1])
-
- def mutate(self):
- """No member variables. Nothing to do."""
- if ka_random.is_mutating():
- self.scale_x += ka_random.jitter_constrained(self.constraint_scale)
- self.scale_x = ka_random.limitate_range(self.scale_x, self.constraint_scale[0], self.constraint_scale[1])
- if ka_random.is_mutating():
- self.scale_y += ka_random.jitter_constrained(self.constraint_scale)
- self.scale_y = ka_random.limitate_range(self.scale_y, self.constraint_scale[0], self.constraint_scale[1])
-
- def shuffle(self):
- """No member variables. Nothing to do."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, LayermaskMerger)
- pre: isinstance(self, LayermaskMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = LayermaskMerger(self.get_trunk())
- cross_sequence = ka_random.crossing_sequence(2)
- new_one.scale_x = self.scale_x if cross_sequence[0] else other.scale_x
- new_one.scale_y = self.scale_y if cross_sequence[1] else other.scale_y
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # paint one layer
- ctx.save()
- msk_surface = ctx.get_target().create_similar(cairo.CONTENT_ALPHA,
- width, height)
- # draw mask.
- msk_ctx = cairo.Context(msk_surface)
- sx, sy = self.scale_x**2, self.scale_y**2
-# sx, sy = 2.5**2, 2.5**2
- msk_ctx.scale(sx*width, sy*height)
-# msk_ctx.translate(0.5-0.5/sx, 0.5-0.5/sy)
- msk_ctx.translate(0.5/sx, 0.5/sy)
-
- # draw mask background
- msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
- msk_ctx.set_source_rgba(0.0, 0.0, 0.0, 0.5)
- msk_ctx.paint()
- # draw mask layer
- msk_ctx.set_operator(cairo.OPERATOR_SOURCE)
- mask_layer.render(msk_ctx, width, height)
-# dummy = ep_layer_referencepattern.ReferencePattern(self.path+'/TODO')
-# dummy.operator = cairo.OPERATOR_SOURCE
-# dummy.render(msk_ctx, width, height)
-# msk_surface.write_to_png('/dev/shm/TODO.png')
-
- ctx.save()
- ctx.translate(-0.5, -0.5)
- ctx.scale(1.0/msk_surface.get_width(), 1.0/msk_surface.get_height())
- ctx.mask_surface(msk_surface, 0, 0)
- ctx.restore()
-
- single_layer.render(ctx, width, height)
- ctx.restore()
-
- def explain(self):
- """
- post: len(__return__) == 3
- """
- return 'Layermask merger, scale_x**2=' + unicode(self.scale_x**2) \
- + u' scale_y**2=' + unicode(self.scale_y**2), \
- None, \
- None
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, LayermaskMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = LayermaskMerger(self.get_trunk())
- new_one.scale_x = self.scale_x
- new_one.scale_y = self.scale_y
- return new_one
diff --git a/ep_merger_rectangulartile.py b/ep_merger_rectangulartile.py
deleted file mode 100644
index 21a09cf..0000000
--- a/ep_merger_rectangulartile.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import ka_random
-import model_allele
-
-class RectangularTileMerger(model_allele.Allele):
- """RectangularTileMerger: Reverse the layer horizontally or vertically.
- inv: self.xTiles > 0
- inv: self.yTiles > 0
- """
-
- def __init__(self, trunk):
- """Constructor for a flip merger."""
- super(RectangularTileMerger, self).__init__(trunk)
- self.xTiles = 1
- self.yTiles = 1
-
- def __eq__(self, other):
- """Equality based on fliping horizontally or vertically."""
- equal = isinstance(other, RectangularTileMerger) \
- and self.xTiles == other.xTiles \
- and self.yTiles == other.yTiles
- return equal
-
- def randomize(self):
- """Randomize the layers components."""
- self.xTiles = random.randint(2, 4)
- self.yTiles = random.randint(2, 4)
-
- def mutate(self):
- """Make small random changes to the layers components."""
- if ka_random.is_mutating():
- self.xTiles += random.randint(-1, 1)
- self.xTiles = 1 if self.xTiles < 1 else self.xTiles
- if ka_random.is_mutating():
- self.yTiles += random.randint(-1, 1)
- self.yTiles = 1 if self.yTiles < 1 else self.yTiles
-
- def shuffle(self):
- """Shuffle similar componets."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, RectangularTileMerger)
- pre: isinstance(self, RectangularTileMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = RectangularTileMerger(self.get_trunk())
- cross_sequence = ka_random.crossing_sequence(2)
- new_one.xTiles = self.xTiles if cross_sequence[0] else other.xTiles
- new_one.yTiles = self.yTiles if cross_sequence[1] else other.yTiles
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # repeat painting layer
- dx, dy = 1.0 / self.xTiles, 1.0 / self.yTiles
- for tx in range(self.xTiles):
- for ty in range(self.yTiles):
- ctx.save()
- ctx.translate((tx-0.5*self.xTiles)*dx+0.5*dx, \
- (ty-0.5*self.yTiles)*dy+0.5*dy)
- ctx.scale(dx, dy)
- ctx.save()
- single_layer.render(ctx, width, height)
- ctx.restore()
- ctx.restore()
-
- def explain(self):
- """
- post: len(__return__) == 3
- """
- return u'Rectangular tile merger: %d*x, %d*y' \
- % (self.xTiles, self.yTiles), \
- None, \
- None
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, RectangularTileMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = RectangularTileMerger(self.get_trunk())
- new_one.xTiles = self.xTiles
- new_one.yTiles = self.yTiles
- return new_one
diff --git a/ep_merger_straight.py b/ep_merger_straight.py
deleted file mode 100644
index 0570ab2..0000000
--- a/ep_merger_straight.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import model_allele
-
-class StraightMerger(model_allele.Allele):
- """StraightMerger
- """
-
- def __init__(self, trunk):
- """Constructor for a simple merger."""
- super(StraightMerger, self).__init__(trunk)
-
- def __eq__(self, other):
- """Equality based on same instance."""
- equal = isinstance(other, StraightMerger)
- return equal
-
- def randomize(self):
- """No member variables. Nothing to do."""
- pass
-
- def mutate(self):
- """No member variables. Nothing to do."""
- pass
-
- def shuffle(self):
- """No member variables. Nothing to do."""
- pass
-
- def crossingover(self, other):
- """
- pre: isinstance(other, StraightMerger)
- pre: isinstance(self, StraightMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- post: __return__ is not other
- """
- new_one = StraightMerger(self.get_trunk())
- return new_one
-
- def render_single_layer(self, single_layer, mask_layer, ctx, width, height):
- """
- pre: single_layer is not None
- pre: ctx is not None
- pre: width > 0
- pre: height > 0
- pre: width == height
- """
- # paint one layer
- ctx.save()
- single_layer.render(ctx, width, height)
- ctx.restore()
-
- def explain(self):
- return 'Straight merger', \
- None, \
- None
-
- def copy(self):
- """A copy constructor.
- post: isinstance(__return__, StraightMerger)
- # check for distinct references, needs to copy content, not references
- post: __return__ is not self
- """
- new_one = StraightMerger(self.get_trunk())
- return new_one
diff --git a/htmltextview.py b/htmltextview.py
new file mode 100644
index 0000000..9fa8a31
--- /dev/null
+++ b/htmltextview.py
@@ -0,0 +1,474 @@
+### Copyright (C) 2005-2007 Gustavo J. A. M. Carneiro
+###
+### This library is free software; you can redistribute it and/or
+### modify it under the terms of the GNU Lesser General Public
+### License as published by the Free Software Foundation; either
+### version 2 of the License, or (at your option) any later version.
+###
+### This library is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+### Lesser General Public License for more details.
+###
+### You should have received a copy of the GNU Lesser General Public
+### License along with this library; if not, write to the
+### Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+### Boston, MA 02111-1307, USA.
+
+# Attention:
+# In 2009 I used only a subset of the original code published
+# by Gustavo J. A. M. Carneiro.
+# You can find the original source code here:
+# http://www.gnome.org/~gjc/htmltextview.py
+# Thomas Jourdan
+
+'''
+A gtk.TextView-based renderer for XHTML-IM, as described in:
+ http://www.jabber.org/jeps/jep-0071.html
+'''
+import gobject
+import pango
+import gtk
+import xml.sax, xml.sax.handler
+import re
+import warnings
+from cStringIO import StringIO
+import urllib2
+import operator
+
+__all__ = ['HtmlTextView']
+
+whitespace_rx = re.compile("\\s+")
+allwhitespace_rx = re.compile("^\\s*$")
+
+## pixels = points * display_resolution
+display_resolution = 0.3514598*(gtk.gdk.screen_height() /
+ float(gtk.gdk.screen_height_mm()))
+
+
+def _parse_css_color(color):
+ '''_parse_css_color(css_color) -> gtk.gdk.Color'''
+ if color.startswith("rgb(") and color.endswith(')'):
+ r, g, b = [int(c)*257 for c in color[4:-1].split(',')]
+ return gtk.gdk.Color(r, g, b)
+ else:
+ return gtk.gdk.color_parse(color)
+
+
+class HtmlHandler(xml.sax.handler.ContentHandler):
+
+ def __init__(self, textview, startiter):
+ xml.sax.handler.ContentHandler.__init__(self)
+ self.textbuf = textview.get_buffer()
+ self.textview = textview
+ self.iter = startiter
+ self.text = ''
+ self.styles = [] # a gtk.TextTag or None, for each span level
+ self.list_counters = [] # stack (top at head) of list
+ # counters, or None for unordered list
+
+ def _parse_style_color(self, tag, value):
+ color = _parse_css_color(value)
+ tag.set_property("foreground-gdk", color)
+
+ def _parse_style_background_color(self, tag, value):
+ color = _parse_css_color(value)
+ tag.set_property("background-gdk", color)
+ if gtk.gtk_version >= (2, 8):
+ tag.set_property("paragraph-background-gdk", color)
+
+ def _get_current_attributes(self):
+ attrs = self.textview.get_default_attributes()
+ self.iter.backward_char()
+ self.iter.get_attributes(attrs)
+ self.iter.forward_char()
+ return attrs
+
+ def __parse_length_frac_size_allocate(self, textview, allocation,
+ frac, callback, args):
+ callback(allocation.width*frac, *args)
+
+ def _parse_length(self, value, font_relative, callback, *args):
+ '''Parse/calc length, converting to pixels, calls callback(length, *args)
+ when the length is first computed or changes'''
+ if value.endswith('%'):
+ frac = float(value[:-1])/100
+ if font_relative:
+ attrs = self._get_current_attributes()
+ font_size = attrs.font.get_size() / pango.SCALE
+ callback(frac*display_resolution*font_size, *args)
+ else:
+ ## CSS says "Percentage values: refer to width of the closest
+ ## block-level ancestor"
+ ## This is difficult/impossible to implement, so we use
+ ## textview width instead; a reasonable approximation..
+ alloc = self.textview.get_allocation()
+ self.__parse_length_frac_size_allocate(self.textview, alloc,
+ frac, callback, args)
+ self.textview.connect("size-allocate",
+ self.__parse_length_frac_size_allocate,
+ frac, callback, args)
+
+ elif value.endswith('pt'): # points
+ callback(float(value[:-2])*display_resolution, *args)
+
+ elif value.endswith('em'): # ems, the height of the element's font
+ attrs = self._get_current_attributes()
+ font_size = attrs.font.get_size() / pango.SCALE
+ callback(float(value[:-2])*display_resolution*font_size, *args)
+
+ elif value.endswith('ex'): # x-height, ~ the height of the letter 'x'
+ ## FIXME: figure out how to calculate this correctly
+ ## for now 'em' size is used as approximation
+ attrs = self._get_current_attributes()
+ font_size = attrs.font.get_size() / pango.SCALE
+ callback(float(value[:-2])*display_resolution*font_size, *args)
+
+ elif value.endswith('px'): # pixels
+ callback(int(value[:-2]), *args)
+
+ else:
+ warnings.warn("Unable to parse length value '%s'" % value)
+
+ def __parse_font_size_cb(length, tag):
+ tag.set_property("size-points", length/display_resolution)
+ __parse_font_size_cb = staticmethod(__parse_font_size_cb)
+
+ def _parse_style_font_size(self, tag, value):
+ try:
+ scale = {
+ "xx-small": pango.SCALE_XX_SMALL,
+ "x-small": pango.SCALE_X_SMALL,
+ "small": pango.SCALE_SMALL,
+ "medium": pango.SCALE_MEDIUM,
+ "large": pango.SCALE_LARGE,
+ "x-large": pango.SCALE_X_LARGE,
+ "xx-large": pango.SCALE_XX_LARGE,
+ } [value]
+ except KeyError:
+ pass
+ else:
+ attrs = self._get_current_attributes()
+ tag.set_property("scale", scale / attrs.font_scale)
+ return
+ if value == 'smaller':
+ tag.set_property("scale", pango.SCALE_SMALL)
+ return
+ if value == 'larger':
+ tag.set_property("scale", pango.SCALE_LARGE)
+ return
+ self._parse_length(value, True, self.__parse_font_size_cb, tag)
+
+ def _parse_style_font_style(self, tag, value):
+ try:
+ style = {
+ "normal": pango.STYLE_NORMAL,
+ "italic": pango.STYLE_ITALIC,
+ "oblique": pango.STYLE_OBLIQUE,
+ } [value]
+ except KeyError:
+ warnings.warn("unknown font-style %s" % value)
+ else:
+ tag.set_property("style", style)
+
+ def __frac_length_tag_cb(length, tag, propname):
+ tag.set_property(propname, length)
+ __frac_length_tag_cb = staticmethod(__frac_length_tag_cb)
+
+ def _parse_style_margin_left(self, tag, value):
+ self._parse_length(value, False, self.__frac_length_tag_cb,
+ tag, "left-margin")
+
+ def _parse_style_margin_right(self, tag, value):
+ self._parse_length(value, False, self.__frac_length_tag_cb,
+ tag, "right-margin")
+
+ def _parse_style_font_weight(self, tag, value):
+ ## TODO: missing 'bolder' and 'lighter'
+ try:
+ weight = {
+ '100': pango.WEIGHT_ULTRALIGHT,
+ '200': pango.WEIGHT_ULTRALIGHT,
+ '300': pango.WEIGHT_LIGHT,
+ '400': pango.WEIGHT_NORMAL,
+ '500': pango.WEIGHT_NORMAL,
+ '600': pango.WEIGHT_BOLD,
+ '700': pango.WEIGHT_BOLD,
+ '800': pango.WEIGHT_ULTRABOLD,
+ '900': pango.WEIGHT_HEAVY,
+ 'normal': pango.WEIGHT_NORMAL,
+ 'bold': pango.WEIGHT_BOLD,
+ } [value]
+ except KeyError:
+ warnings.warn("unknown font-style %s" % value)
+ else:
+ tag.set_property("weight", weight)
+
+ def _parse_style_font_family(self, tag, value):
+ tag.set_property("family", value)
+
+ def _parse_style_text_align(self, tag, value):
+ try:
+ align = {
+ 'left': gtk.JUSTIFY_LEFT,
+ 'right': gtk.JUSTIFY_RIGHT,
+ 'center': gtk.JUSTIFY_CENTER,
+ 'justify': gtk.JUSTIFY_FILL,
+ } [value]
+ except KeyError:
+ warnings.warn("Invalid text-align:%s requested" % value)
+ else:
+ tag.set_property("justification", align)
+
+ def _parse_style_text_decoration(self, tag, value):
+ if value == "none":
+ tag.set_property("underline", pango.UNDERLINE_NONE)
+ tag.set_property("strikethrough", False)
+ elif value == "underline":
+ tag.set_property("underline", pango.UNDERLINE_SINGLE)
+ tag.set_property("strikethrough", False)
+ elif value == "overline":
+ warnings.warn("text-decoration:overline not implemented")
+ tag.set_property("underline", pango.UNDERLINE_NONE)
+ tag.set_property("strikethrough", False)
+ elif value == "line-through":
+ tag.set_property("underline", pango.UNDERLINE_NONE)
+ tag.set_property("strikethrough", True)
+ elif value == "blink":
+ warnings.warn("text-decoration:blink not implemented")
+ else:
+ warnings.warn("text-decoration:%s not implemented" % value)
+
+
+ ## build a dictionary mapping styles to methods, for greater speed
+ __style_methods = dict()
+ for style in ["background-color", "color", "font-family", "font-size",
+ "font-style", "font-weight", "margin-left", "margin-right",
+ "text-align", "text-decoration"]:
+ try:
+ method = locals()["_parse_style_%s" % style.replace('-', '_')]
+ except KeyError:
+ warnings.warn("Style attribute '%s' not yet implemented" % style)
+ else:
+ __style_methods[style] = method
+
+ def _get_style_tags(self):
+ return [tag for tag in self.styles if tag is not None]
+
+ def _begin_span(self, style, tag=None):
+ if style is None:
+ self.styles.append(tag)
+ return None
+ if tag is None:
+ tag = self.textbuf.create_tag()
+ for attr, val in [item.split(':', 1) for item in style.split(';')]:
+ attr = attr.strip().lower()
+ val = val.strip()
+ try:
+ method = self.__style_methods[attr]
+ except KeyError:
+ warnings.warn("Style attribute '%s' requested "
+ "but not yet implemented" % attr)
+ else:
+ method(self, tag, val)
+ self.styles.append(tag)
+
+ def _end_span(self):
+ self.styles.pop(-1)
+
+ def _insert_text(self, text):
+ tags = self._get_style_tags()
+ if tags:
+ self.textbuf.insert_with_tags(self.iter, text, *tags)
+ else:
+ self.textbuf.insert(self.iter, text)
+
+ def _flush_text(self):
+ if not self.text:
+ return
+ self._insert_text(self.text.replace('\n', ''))
+ self.text = ''
+
+ def _anchor_event(self, tag, textview, event, iter, href, type_):
+ if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
+ self.textview.emit("url-clicked", href, type_)
+ return True
+ return False
+
+ def characters(self, content):
+ if allwhitespace_rx.match(content) is not None:
+ return
+ #if self.text:
+ # self.text += ' '
+ self.text += whitespace_rx.sub(' ', content)
+
+ def startElement(self, name, attrs):
+ self._flush_text()
+ try:
+ style = attrs['style']
+ except KeyError:
+ style = None
+
+ tag = None
+ if name == 'a':
+ tag = self.textbuf.create_tag()
+ tag.set_property('foreground', '#0000ff')
+ tag.set_property('underline', pango.UNDERLINE_SINGLE)
+ try:
+ type_ = attrs['type']
+ except KeyError:
+ type_ = None
+ tag.connect('event', self._anchor_event, attrs['href'], type_)
+ tag.is_anchor = True
+
+ self._begin_span(style, tag)
+
+ if name == 'br':
+ pass # handled in endElement
+ elif name == 'p':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ elif name == 'div':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ elif name == 'span':
+ pass
+ elif name == 'ul':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ self.list_counters.insert(0, None)
+ elif name == 'ol':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ self.list_counters.insert(0, 0)
+ elif name == 'li':
+ if self.list_counters[0] is None:
+ li_head = unichr(0x2022)
+ else:
+ self.list_counters[0] += 1
+ li_head = "%i." % self.list_counters[0]
+ self.text = ' '*len(self.list_counters)*4 + li_head + ' '
+ elif name == 'img':
+ pixbuf = None
+ if attrs['src'].startswith('file:'):
+ # read image files only from local file system.
+ try:
+ ## Max image size = 500 KB (to try to prevent DoS)
+ mem = urllib2.urlopen(attrs['src']).read(500*1024)
+ ## Caveat: GdkPixbuf is known not to be safe to load
+ ## images from network... this program is now potentially
+ ## hackable ;)
+ loader = gtk.gdk.PixbufLoader()
+ loader.write(mem); loader.close()
+ pixbuf = loader.get_pixbuf()
+ except Exception, ex:
+ try:
+ alt = attrs['alt']
+ except KeyError:
+ alt = "Broken image"
+ if pixbuf is not None:
+ tags = self._get_style_tags()
+ if tags:
+ tmpmark = self.textbuf.create_mark(None, self.iter, True)
+
+ self.textbuf.insert_pixbuf(self.iter, pixbuf)
+
+ if tags:
+ start = self.textbuf.get_iter_at_mark(tmpmark)
+ for tag in tags:
+ self.textbuf.apply_tag(tag, start, self.iter)
+ self.textbuf.delete_mark(tmpmark)
+ else:
+ self._insert_text("[IMG: %s]" % alt)
+ elif name == 'body':
+ pass
+ elif name == 'a':
+ pass
+ else:
+ warnings.warn("Unhandled element '%s'" % name)
+
+ def endElement(self, name):
+ self._flush_text()
+ if name == 'p':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ elif name == 'div':
+ if not self.iter.starts_line():
+ self._insert_text("\n")
+ elif name == 'span':
+ pass
+ elif name == 'br':
+ self._insert_text("\n")
+ elif name == 'ul':
+ self.list_counters.pop()
+ elif name == 'ol':
+ self.list_counters.pop()
+ elif name == 'li':
+ self._insert_text("\n")
+ elif name == 'img':
+ pass
+ elif name == 'body':
+ pass
+ elif name == 'a':
+ pass
+ else:
+ warnings.warn("Unhandled element '%s'" % name)
+ self._end_span()
+
+
+class HtmlTextView(gtk.TextView):
+ __gtype_name__ = 'HtmlTextView'
+ __gsignals__ = {
+ 'url-clicked': (gobject.SIGNAL_RUN_LAST, None, (str, str)), # href, type
+ }
+
+ def __init__(self):
+ gtk.TextView.__init__(self)
+ self.set_wrap_mode(gtk.WRAP_CHAR)
+ self.set_editable(False)
+ self._changed_cursor = False
+ self.connect("motion-notify-event", self.__motion_notify_event)
+ self.connect("leave-notify-event", self.__leave_event)
+ self.connect("enter-notify-event", self.__motion_notify_event)
+ self.set_pixels_above_lines(3)
+ self.set_pixels_below_lines(3)
+
+ def __leave_event(self, widget, event):
+ if self._changed_cursor:
+ window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
+ window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
+ self._changed_cursor = False
+
+ def __motion_notify_event(self, widget, event):
+ x, y, _ = widget.window.get_pointer()
+ x, y = widget.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, x, y)
+ tags = widget.get_iter_at_location(x, y).get_tags()
+ for tag in tags:
+ if getattr(tag, 'is_anchor', False):
+ is_over_anchor = True
+ break
+ else:
+ is_over_anchor = False
+ if not self._changed_cursor and is_over_anchor:
+ window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
+ window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND2))
+ self._changed_cursor = True
+ elif self._changed_cursor and not is_over_anchor:
+ window = widget.get_window(gtk.TEXT_WINDOW_TEXT)
+ window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
+ self._changed_cursor = False
+ return False
+
+ def display_html(self, html):
+ buffer = self.get_buffer()
+ eob = buffer.get_end_iter()
+ ## this works too if libxml2 is not available
+ #parser = xml.sax.make_parser(['drv_libxml2'])
+ parser = xml.sax.make_parser()
+ # parser.setFeature(xml.sax.handler.feature_validation, True)
+ parser.setContentHandler(HtmlHandler(self, eob))
+ #parser.setEntityResolver(HtmlEntityResolver())
+ parser.parse(StringIO(html))
+
+ if not eob.starts_line():
+ buffer.insert(eob, "\n")
diff --git a/ka_canvas.py b/ka_canvas.py
deleted file mode 100644
index ab31380..0000000
--- a/ka_canvas.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import gtk
-import gtk.glade
-
-class KandidCanvas(object):
- """
- inv: self.widgetTree is not None
- inv: self.widget is not None
- """
-
- def __init__(self):
- # Load Glade XML
- self.widgetTree = gtk.glade.XML("kandid.glade")
-
- # Get Window
- self._window = self.widgetTree.get_widget('window1')
- # Get Windows child
- self._window_child = self._window.get_child()
-
- # Remove the widget's parent
- if self._window_child.parent:
- self._window_child.parent.remove(self._window_child)
-
- # self.widget will be attached to the Activity
- # This can be any GTK widget except a window
- self.widget = self._window_child
diff --git a/ka_debug.py b/ka_debug.py
index 4f2f71b..0be89b8 100644
--- a/ka_debug.py
+++ b/ka_debug.py
@@ -41,8 +41,8 @@ def info(msg):
"""Log an info message."""
global _last_clock
clock_now = time.clock()
- print 'debug', int((time.time()-_start_time)*1000), \
- int((clock_now-_last_clock)*1000), ':', msg
+# print 'debug', int((time.time()-_start_time)*1000), \
+# int((clock_now-_last_clock)*1000), ':', msg
_last_clock = clock_now
_avtivate_logger()
_logger.debug(msg)
@@ -52,8 +52,8 @@ def err(msg):
"""Log an error message."""
global _last_clock
clock_now = time.clock()
- print 'error', int((time.time()-_start_time)*1000), \
- int((clock_now-_last_clock)*1000), ':', msg
+# print 'error', int((time.time()-_start_time)*1000), \
+# int((clock_now-_last_clock)*1000), ':', msg
_last_clock = clock_now
_avtivate_logger()
_logger.error(msg)
@@ -97,7 +97,7 @@ def contains_by_id(this_list, this_element):
return True
return False
-try_once = True
+try_once = False
if try_once:
try:
try_once = False
diff --git a/ka_extensionpoint.py b/ka_extensionpoint.py
index 1f6067f..8e5740e 100644
--- a/ka_extensionpoint.py
+++ b/ka_extensionpoint.py
@@ -63,11 +63,15 @@ def _add(extension_type, extension_class):
if e_key not in _extensions:
_extensions.append(e_key)
-def scann():
+def get_bundle_path():
bundle_path = ka_debug.DEBUG_PATH
if 'SUGAR_BUNDLE_PATH' in os.environ:
from sugar.activity import activity
bundle_path = activity.get_bundle_path()
+ return bundle_path
+
+def scann():
+ bundle_path = get_bundle_path()
ka_debug.info('searching for extensions in ' + bundle_path)
for element in os.listdir(bundle_path):
if element.startswith(_PREFIX) and element.endswith(_PYEXT) \
diff --git a/ka_random.py b/ka_random.py
deleted file mode 100644
index f9a9752..0000000
--- a/ka_random.py
+++ /dev/null
@@ -1,233 +0,0 @@
-# coding: UTF8
-# Copyright 2009 Thomas Jourdan
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import random
-import math
-
-crossing_probability = 0.75
-shuffling_probability = 0.10
-mutating_probability = 0.25
-
-_flurry = 0.5
-
-def set_flurry(flurry_rate):
- """
- pre: 0 <= flurry_rate <= 9
- post: 0.0 <= _flurry <= 1.0
- """
- global _flurry
- _flurry = flurry_rate / 10.0
-
-def get_flurry():
- """
- post: 0 <= __return__ <= 9
- """
- return _flurry * 10.0
-
-def binomial(n, probability):
- """ Generates a binomial distributed random value.
- Returns a random number between 0 (inclusive) and n (inclusive).
- Code is from http://geovistastudio.sourceforge.net/
- pre: n >= 0
- pre: 0.0 <= probability <= 1.0
- post: 0 <= __return__ <= n
- """
- r = 0
- for _ in range(n):
- if random.random() < probability:
- r += 1
- return r
-
-def jitter(sigma):
- """
- """
- return 0.03 * _flurry * random.gauss(0.0, sigma)
-
-def jitter_constrained(constraint):
- """
- pre: len(constraint) == 2
- pre: constraint[0] <= constraint[1]
- """
- sigma = 0.03 * _flurry * (constraint[1] - constraint[0])
- return random.gauss(0.0, sigma)
-
-def jitter_discret_constrained(value, constraint):
- """
- pre: len(constraint) == 2
- pre: constraint[0] <= constraint[1]
- post: constraint[0] <= __return__ <= constraint[1]
- """
- interval = constraint[1] - constraint[0]
- delta = binomial(interval, _flurry * 0.25) * random.choice([-1, 1])
- return limitate_range(value+delta, constraint[0], constraint[1])
-
-def randint_constrained(constraint):
- """
- pre: len(constraint) == 2
- pre: constraint[0] <= constraint[1]
- post: constraint[0] <= __return__ <= constraint[1]
- """
- return random.randint(constraint[0], constraint[1])
-
-def uniform_constrained(constraint):
- """
- pre: len(constraint) == 2
- pre: constraint[0] <= constraint[1]
- post: constraint[0] <= __return__ <= constraint[1]
- """
- return random.uniform(constraint[0], constraint[1])
-
-def is_shuffling():
- return random.random() < shuffling_probability * _flurry
-
-def is_mutating():
- return random.random() < mutating_probability * _flurry
-
-def is_crossing():
- return random.random() < crossing_probability * _flurry
-
-def crossing_sequence(size):
- """Produces a sequence filled with True or False elements.
- With a low crossing_probability there will be lesser change overs from
- True series to False series.
- pre: size >= 1
- post: len(__return__) == size
- post: forall(__return__, lambda x: x is True or x is False)
- """
- sample = [random.choice([False, True])]
- for i in range(size-1):
- if is_crossing():
- sample.append(not sample[-1])
- else:
- sample.append(sample[-1])
- return sample
-
-def crossingover(first, second):
- """
- pre: len(first) >= 1
- pre: len(second) >= 1
- post: min([len(first), len(second)]) <= len(__return__) <= max([len(first), len(second)])
- post: forall(__return__, lambda x: x in first or x in second)
- """
- seq = []
- len_first, len_second = len(first), len(second)
- len_max = max([len_first, len_second])
- randseq = crossing_sequence(len_max)
- for index in range(len_max):
- if randseq[index]:
- if index < len_first:
- seq.append(first[index])
- else:
- if index < len_second:
- seq.append(second[index])
- return seq
-
-def mutate_list(this_list, number_of_constraint, new_element):
- # maybe remove one element
- len_this = len(this_list)
- if len_this > number_of_constraint[0] and is_mutating():
- del this_list[random.randint(0, len_this-1)]
- len_this -= 1
- # maybe duplicate one of the elements
- if len_this < number_of_constraint[1] and is_mutating():
- random_element = this_list[random.randint(0, len_this-1)]
- dupli_element = random_element.copy()
- this_list.insert(random.randint(0, len_this-1), dupli_element)
- len_this += 1
- # maybe insert a new element
- if len_this < number_of_constraint[1] and is_mutating():
-# single_element = factory.create_random(type_constraint, path)
- new_element.randomize()
- this_list.insert(random.randint(0, len_this-1), new_element)
- len_this += 1
-
- # delegate mutation to the elements child components
- for single_element in this_list:
- single_element.mutate()
-
-def limitate(value):
- """
- post: 0.0 <= __return__ <= 1.0
- """
- if value < 0.0:
- value = 0.0
- elif value > 1.0:
- value = 1.0
- return value
-
-def cyclic_limitate(value):
- """
- post: 0.0 <= __return__ <= 1.0
- """
- return _cyclic_limitate(value, 0.0, 1.0)
-
-def radian_limitate(value):
- """
- post: (-1.0*math.pi) <= __return__ <= math.pi
- """
- return _cyclic_limitate(value, -1.0*math.pi, math.pi)
-
-def _cyclic_limitate(value, lower_bound, upper_bound):
- """
- post: lower_bound <= __return__ <= upper_bound
- """
- while value < lower_bound:
- value += upper_bound-lower_bound
- while value > upper_bound:
- value -= upper_bound-lower_bound
- return value
-
-def limitate_range(value, min_value, max_value):
- """
- pre: min_value <= max_value
- post: min_value <= __return__ <= max_value
- """
- if value < min_value:
- value = min_value
- elif value > max_value:
- value = max_value
- return value
-
-def copy_list(other_list):
- """Make a deep copy of a list and its elements.
- pre: other_list is not None
- post: __return__ is not other_list
- post: len(__return__) == len(other_list)
- post: forall([other_list[i] is not __return__[i] for i in range(len(other_list))])
- post: forall([other_list[i] == __return__[i] for i in range(len(other_list))])
- """
- new_list = []
- for element in other_list:
- new_list.append(element.copy())
- return new_list
-
-def copy_tuple_list(other_list):
- """Make a deep copy of a list and its tuple elements.
- Assumes every tuple has two cells
- pre: other_list is not None
- pre: len(other_list) == 0 or forall([len(e) == 2 for e in other_list])
- post: __return__ is not other_list
- post: len(__return__) == len(other_list)
- post: forall([other_list[i][0] is not __return__[i][0] for i in range(len(other_list))])
- post: forall([other_list[i][0] == __return__[i][0] for i in range(len(other_list))])
- post: forall([other_list[i][1] is not __return__[i][1] for i in range(len(other_list))])
- post: forall([other_list[i][1] == __return__[i][1] for i in range(len(other_list))])
- """
- new_list = []
- for element in other_list:
- new_list.append( (element[0].copy(), element[1].copy()) )
- return new_list
diff --git a/ka_widget.py b/ka_widget.py
index e1db2aa..e0f486e 100644
--- a/ka_widget.py
+++ b/ka_widget.py
@@ -16,9 +16,13 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gtk
import gtk.glade
+import os
+import sys
from gettext import gettext as _
+import ka_extensionpoint
import ka_controller
import ka_debug
+import htmltextview
class KandidWidget(object):
"""
@@ -43,47 +47,124 @@ class KandidWidget(object):
# This can be any GTK widget except a window
main_view.pack_start(kandidWindow_child)
- self._localizeGUI()
+ self._localizePopulation()
+ self._localizeIntro()
# Display everything
kandidWindow_child.show()
main_view.show()
-
def getWidget_tree(self):
return self._widget_tree
-
- def _localizeGUI(self):
- local = {'breedGenerationButton' : _('Breed'),
- 'randomGenerationButton' : _('Random'),
- 'flurryLabel' : _('Flurry rate:'),
- 'introLabel' : _('Introduction'),
- 'populationLabel' : _('Population'),
- }
- for key, label in local.iteritems():
- self._widget_tree.get_widget(key).set_label(label)
- ka_debug.info('localize "%s" to "%s"' % (key, label))
- for cell_index in range(ka_controller.POPULATION_CAPACITY):
- local = {'favorite_menuitem_#' : _('My favorite'),
- 'awfull_menuitem_#' : _('Awful bore'),
- 'publishprotozoon_menuitem_#' : _('Publish to my friends'),
- }
- self._set_localized_child_text(local, cell_index)
-
- for cell_index in range(ka_controller.INCOMMING_CAPACITY):
- local = {'accept_menuitem_#' : _('Accept protozoon'),
- 'decline_menuitem_#' : _('Decline protozoon'),
+ widget_tree = property(getWidget_tree)
+
+ def _localizePopulation(self):
+ try:
+ local = {'breedGenerationButton' : _('Breed'),
+ 'randomGenerationButton' : _('Random'),
+ 'flurryLabel' : _('Flurry rate:'),
+ 'introLabel' : _('Introduction'),
+ 'populationLabel' : _('Population'),
}
- self._set_localized_child_text(local, cell_index)
+ for key, label in local.iteritems():
+ self._widget_tree.get_widget(key).set_label(label)
+ ka_debug.info('localize "%s" to "%s"' % (key, label))
+
+ for cell_index in range(ka_controller.POPULATION_CAPACITY):
+ local = {'favorite_menuitem_#' : _('My favorite'),
+ 'awfull_menuitem_#' : _('Awful bore'),
+ 'publishprotozoon_menuitem_#' : _('Publish to my friends'),
+ }
+ self._set_localized_child_text(local, cell_index)
+
+ for cell_index in range(ka_controller.INCOMMING_CAPACITY):
+ local = {'accept_menuitem_#' : _('Accept protozoon'),
+ 'decline_menuitem_#' : _('Decline protozoon'),
+ }
+ self._set_localized_child_text(local, cell_index)
+ except:
+ ka_debug.err('localize population failed [%s] [%s]' % \
+ (sys.exc_info()[0], sys.exc_info()[1]))
def _set_localized_child_text(self, local, cell_index):
strix = str(cell_index)
for key, label in local.iteritems():
ikey = key if cell_index < 0 else key.replace('#', strix)
self._widget_tree.get_widget(ikey).get_child().set_text(label)
- ka_debug.info('localize "%s" to "%s"' % (ikey, label))
+# ka_debug.info('localize "%s" to "%s"' % (ikey, label))
-
+ def _localizeIntro(self):
+ try:
+# IMAGE_ROOT = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'intro' + os.sep
+ htmlview = htmltextview.HtmlTextView()
+ def url_cb(view, url, type_):
+ #print "url-clicked", url, type_
+ pass
+ htmlview.connect("url-clicked", url_cb)
+ locale, territory = KandidWidget.get_localization()
+ htmlview.display_html(KandidWidget.get_localized_content(locale,
+ territory))
+ htmlview.show()
+ intro_scrolled = self._widget_tree.get_widget('intro_scrolledwindow')
+ intro_scrolled.add(htmlview)
+ intro_scrolled.show()
+ except:
+ ka_debug.err('localize introduction failed [%s] [%s]' % \
+ (sys.exc_info()[0], sys.exc_info()[1]))
- widget_tree = property(getWidget_tree)
+ @staticmethod
+ def get_localized_content(locale, territory):
+ bundle_path = ka_extensionpoint.get_bundle_path()
+ content = ''
+ in_file = None
+ file_path = os.path.join(bundle_path,
+ 'locale',
+ locale + '_' + territory,
+ 'intro.html')
+ if not os.path.isfile(file_path):
+ file_path = os.path.join(bundle_path,
+ 'locale',
+ locale,
+ 'intro.html')
+ if not os.path.isfile(file_path):
+ file_path = os.path.join(bundle_path,
+ 'locale',
+ 'en',
+ 'intro.html')
+ try:
+ ka_debug.info('intro file [%s]' % file_path)
+ in_file = open(file_path, 'r')
+ content = in_file.read()
+ except:
+ ka_debug.err('failed reading [%s] [%s] [%s]' % \
+ (file_path, sys.exc_info()[0], sys.exc_info()[1]))
+ finally:
+ if in_file:
+ in_file.close()
+ return content
+ @staticmethod
+ def get_localization():
+ """ returns localization and territory
+ post: len(locale) >= 2
+ post: len(territory) >= 2
+ """
+ languages = []
+ territory, locale= '', ''
+ for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
+ val = os.environ.get(envar)
+ if val:
+ languages = val.split(':')
+ for lang in languages:
+ pos = lang.find('.')
+ if pos >= 0:
+ lang = lang[:pos]
+ pos = lang.find('_')
+ if pos >= 0:
+ locale = lang[:pos]
+ territory = lang[pos+1:]
+ else:
+ locale = lang
+ break
+ ka_debug.info('languages: %s "%s" "%s"' % (languages, locale, territory))
+ return locale, territory
diff --git a/kandid.glade b/kandid.glade
index 9156e7a..5e5cb5e 100644
--- a/kandid.glade
+++ b/kandid.glade
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Sun Dec 6 14:58:18 2009 -->
+<!--Generated with glade3 3.4.5 on Tue Dec 8 20:05:40 2009 -->
<glade-interface>
<widget class="GtkWindow" id="kandidWindow">
<child>
@@ -1096,19 +1096,14 @@
<property name="visible">True</property>
<property name="border_width">4</property>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow">
+ <widget class="GtkScrolledWindow" id="intro_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
- <widget class="GtkTextView" id="textview_intro">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="editable">False</property>
- <property name="cursor_visible">False</property>
- </widget>
+ <placeholder/>
</child>
</widget>
</child>
diff --git a/locale/de/LC_MESSAGES/net.sourceforge.kandid.mo b/locale/de/LC_MESSAGES/net.sourceforge.kandid.mo
deleted file mode 100644
index 717ff56..0000000
--- a/locale/de/LC_MESSAGES/net.sourceforge.kandid.mo
+++ /dev/null
Binary files differ
diff --git a/locale/de/intro.html b/locale/de/intro.html
new file mode 100644
index 0000000..e3d6d7c
--- /dev/null
+++ b/locale/de/intro.html
@@ -0,0 +1,30 @@
+<body xmlns='http://www.w3.org/1999/xhtml'>
+<p style='font-size:x-large'>Kandid is a system to evolve graphical forms.</p>
+<br/>
+<p style='font-size:large'>Introduction</p>
+<p>
+In Kandid a population consist of images looking like abstract art or patterns.<br/>
+These graphical forms are not drawn by hand. Instead new forms can be found<br/>
+using an interactive genetic algorithm.
+</p>
+<br/>
+<p>
+Kandid is a combination of image rendering algorithms, genetic programming and<br/>
+interactive esthetic selection. The program is missing a fitness function,<br/>
+a component you normally expect in an artificial genetic system. The lack of<br/>
+the built in fitness function is done by concept. No mathematical algorithm can<br/>
+decide if a graphic is meaningful for the user. The fitness function in Kandid<br/>
+is replaced by human esthetic selection. In other words: The user tolds the<br/>
+program which images are 'cool' or never seen before.
+</p>
+<br/>
+<p style='font-size:large'>Usage</p>
+<p>
+An image population starts with randomly generated images.
+<br/><img src="file:intro/intro1.png"/><br/>
+</p>
+<p>
+The image population can be evolved in subsequent generations.
+<br/><img src="file:intro/intro1.png"/><br/>
+</p>
+</body>
diff --git a/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo b/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
deleted file mode 100644
index 5bfb918..0000000
--- a/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo
+++ /dev/null
Binary files differ
diff --git a/locale/en/intro.html b/locale/en/intro.html
new file mode 100644
index 0000000..e3d6d7c
--- /dev/null
+++ b/locale/en/intro.html
@@ -0,0 +1,30 @@
+<body xmlns='http://www.w3.org/1999/xhtml'>
+<p style='font-size:x-large'>Kandid is a system to evolve graphical forms.</p>
+<br/>
+<p style='font-size:large'>Introduction</p>
+<p>
+In Kandid a population consist of images looking like abstract art or patterns.<br/>
+These graphical forms are not drawn by hand. Instead new forms can be found<br/>
+using an interactive genetic algorithm.
+</p>
+<br/>
+<p>
+Kandid is a combination of image rendering algorithms, genetic programming and<br/>
+interactive esthetic selection. The program is missing a fitness function,<br/>
+a component you normally expect in an artificial genetic system. The lack of<br/>
+the built in fitness function is done by concept. No mathematical algorithm can<br/>
+decide if a graphic is meaningful for the user. The fitness function in Kandid<br/>
+is replaced by human esthetic selection. In other words: The user tolds the<br/>
+program which images are 'cool' or never seen before.
+</p>
+<br/>
+<p style='font-size:large'>Usage</p>
+<p>
+An image population starts with randomly generated images.
+<br/><img src="file:intro/intro1.png"/><br/>
+</p>
+<p>
+The image population can be evolved in subsequent generations.
+<br/><img src="file:intro/intro1.png"/><br/>
+</p>
+</body>
diff --git a/model_population.py b/model_population.py
index e1d8b9e..e3733e2 100644
--- a/model_population.py
+++ b/model_population.py
@@ -30,6 +30,8 @@ STATE_INIT = 'I'
STATE_RANDOMIZED = 'R'
STATE_EVOLVED = 'E'
+MAGIC_NUMBER = 'mk01'
+
class KandidModel(object):
"""
inv: self.size >= 2
@@ -290,7 +292,10 @@ def from_buffer(buffer):
# ka_debug.info('read from_buffer')
obj = None
try:
- obj = pickle.loads(zlib.decompress(buffer))
+ if buffer.startswith(MAGIC_NUMBER):
+ obj = pickle.loads(zlib.decompress(buffer[4:]))
+ else:
+ ka_debug.err('missing magic number')
except:
ka_debug.err('failed reading buffer [%s] [%s]' % \
(sys.exc_info()[0], sys.exc_info()[1]))
@@ -300,7 +305,7 @@ def from_buffer(buffer):
def to_buffer(obj):
# ka_debug.info('write %s to_buffer' % type(obj))
try:
- return zlib.compress(pickle.dumps(obj, protocol=2))
+ return MAGIC_NUMBER + zlib.compress(pickle.dumps(obj, protocol=2))
except:
ka_debug.err('failed writing buffer [%s] [%s]' % \
(sys.exc_info()[0], sys.exc_info()[1]))
diff --git a/test_suite.py b/test_suite.py
index 5314f89..444eb58 100644
--- a/test_suite.py
+++ b/test_suite.py
@@ -22,12 +22,14 @@ import unittest
import cairo
import gtk
import time
+import string
import ka_debug
import ka_factory
import ka_task
import model_random
import ka_extensionpoint
import ka_incoming
+import ka_widget
import model_protozoon
import model_constraintpool
import model_population
@@ -561,6 +563,15 @@ class TestKandidModel(unittest.TestCase):
layer.render(ctx, width, height)
surface.write_to_png(outname + '.png')
+ def test_localization(self):
+ locale, territory = ka_widget.KandidWidget.get_localization()
+ for lc in locale:
+ self.assertTrue(lc in string.ascii_lowercase)
+ for tc in territory:
+ self.assertTrue(tc in string.ascii_uppercase)
+ cont = ka_widget.KandidWidget.get_localized_content(locale, territory)
+ self.assertEqual(0, cont.find('<body'))
+
def test_render2(self):
return #TODO
test_enumerator.explain()