diff options
author | Thomas 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) |
commit | 8a6a80557f5e6b8bb69181ac39e1b93ba145e499 (patch) | |
tree | 34c8c471f805960e803c299e073a2bb865c83e4f | |
parent | f5ba3b40da10c79508b2e43e9a99b3db78ba4d5b (diff) |
removed *.mo files, adding html view
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | MANIFEST | 4 | ||||
-rw-r--r-- | ep_directionconstraint_none.py | 76 | ||||
-rw-r--r-- | ep_layer_image.py | 128 | ||||
-rw-r--r-- | ep_layer_referencepattern.py | 123 | ||||
-rw-r--r-- | ep_merger_alphaimage.py | 116 | ||||
-rw-r--r-- | ep_merger_border.py | 132 | ||||
-rw-r--r-- | ep_merger_flip.py | 108 | ||||
-rw-r--r-- | ep_merger_layermask.py | 134 | ||||
-rw-r--r-- | ep_merger_rectangulartile.py | 112 | ||||
-rw-r--r-- | ep_merger_straight.py | 81 | ||||
-rw-r--r-- | htmltextview.py | 474 | ||||
-rw-r--r-- | ka_canvas.py | 42 | ||||
-rw-r--r-- | ka_debug.py | 10 | ||||
-rw-r--r-- | ka_extensionpoint.py | 6 | ||||
-rw-r--r-- | ka_random.py | 233 | ||||
-rw-r--r-- | ka_widget.py | 135 | ||||
-rw-r--r-- | kandid.glade | 11 | ||||
-rw-r--r-- | locale/de/LC_MESSAGES/net.sourceforge.kandid.mo | bin | 885 -> 0 bytes | |||
-rw-r--r-- | locale/de/intro.html | 30 | ||||
-rw-r--r-- | locale/en/LC_MESSAGES/net.sourceforge.kandid.mo | bin | 404 -> 0 bytes | |||
-rw-r--r-- | locale/en/intro.html | 30 | ||||
-rw-r--r-- | model_population.py | 9 | ||||
-rw-r--r-- | test_suite.py | 11 |
24 files changed, 679 insertions, 1329 deletions
@@ -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 @@ -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 Binary files differdeleted file mode 100644 index 717ff56..0000000 --- a/locale/de/LC_MESSAGES/net.sourceforge.kandid.mo +++ /dev/null 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 Binary files differdeleted file mode 100644 index 5bfb918..0000000 --- a/locale/en/LC_MESSAGES/net.sourceforge.kandid.mo +++ /dev/null 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() |