Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/ka_controller.py
diff options
context:
space:
mode:
Diffstat (limited to 'ka_controller.py')
-rw-r--r--ka_controller.py357
1 files changed, 357 insertions, 0 deletions
diff --git a/ka_controller.py b/ka_controller.py
new file mode 100644
index 0000000..05ef161
--- /dev/null
+++ b/ka_controller.py
@@ -0,0 +1,357 @@
+# 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 hashlib
+import gtk
+import gtk.glade
+import cairo
+import ka_debug
+import model_population
+import ka_incoming
+import kandidtube
+import ka_random
+import ka_task
+
+class KandidController(object):
+ """
+ inv: self.model is not None
+ inv: self._canvas is not None
+ """
+
+ def __init__(self, init_canvas):
+ """
+ pre: init_canvas is not None
+ """
+ self._canvas = init_canvas
+ self._tube = None
+
+ # create to data model
+ self.model = model_population.KandidModel(12)
+ self.model.randomize()
+ self.incoming = ka_incoming.KandidIncoming(3)
+
+ self.surface_cache = {}
+
+ #Create a dictionary to connect events
+ events = {
+ 'on_breed_generation' : self.on_breed_generation,
+ 'on_random_generation' : self.on_random_generation,
+ 'on_flurry_value_changed' : self.on_flurry_value_changed,
+ 'on_decline_incoming' : self.on_decline_incoming,
+ 'on_accept_incoming' : self.on_accept_incoming,
+ 'delete_event' : gtk.main_quit }
+ for cell_index in range(self.model.size):
+ strix = str(cell_index)
+ key = 'on_drawingarea_#_expose'.replace('#', strix)
+ events[key] = self.on_drawingarea_expose
+ key = 'on_drawingarea_#_size_allocate'.replace('#', strix)
+ events[key] = self.on_drawingarea_size_allocate
+ key = 'on_fitness_#_value_changed'.replace('#', strix)
+ events[key] = self.on_fitness_value_changed
+ key = 'on_protozoon_popup_#'.replace('#', strix)
+ events[key] = self.on_protozoon_popup
+ key = 'on_publishprotozoon_activate_#'.replace('#', strix)
+ events[key] = self.on_publishprotozoon_activate
+ events['on_favorite_activate_' + strix] = self.on_favorite_activate
+ events['on_awfull_activate_' + strix] = self.on_awfull_activate
+ for cell_index in range(3):
+ strix = str(cell_index)
+ key = 'on_incomingarea_#_expose'.replace('#', strix)
+ events[key] = self.on_incomingarea_expose
+ key = 'on_incoming_#_popup'.replace('#', strix)
+ events[key] = self.on_incoming_popup
+ self._canvas.widgetTree.signal_autoconnect(events)
+ self._update_gui()
+
+ def _update_model(self, in_model):
+ if in_model:
+ self.model = in_model
+ self.model._state = model_population.STATE_EVOLVED
+ self._update_gui()
+
+ def _update_gui(self):
+ for cell_index in range(self.model.size):
+ strix = str(cell_index)
+ key = 'fitness_#'.replace('#', strix)
+ self._canvas.widgetTree.get_widget(key). \
+ set_value(self.model.fitness[cell_index])
+ self._canvas.widgetTree.get_widget('flurrySpinButton'). \
+ set_value(self.model.flurry_rate)
+
+ def _draw_from_cache(self, widget, cell_index):
+ if self.surface_cache.has_key(cell_index):
+ ka_debug.info('_draw_from_cache: ' + widget.name + ' ' + str(cell_index))
+ ctx = self._create_context(widget)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.set_source_surface(self.surface_cache[cell_index])
+ ctx.paint()
+
+ def on_drawingarea_expose(self, widget, event):
+ """ Repaint image of a single protozoon inside population area.
+ pre: widget is not None
+ """
+ # draw precalculated protozoon stored in the surface cache.
+ ka_debug.info('on_drawingarea_expose: ' + widget.name + ' '
+ + str(widget.allocation.width)
+ + 'x' + str(widget.allocation.height))
+ self._draw_from_cache(widget, _name_to_index(widget.name))
+
+ def on_drawingarea_size_allocate(self, widget, event):
+ """ New size for drawing area available.
+ pre: widget is not None
+ """
+ ka_debug.info('on_drawingarea_size_allocate: ' + widget.name + ' '
+ + str(widget.allocation.width)
+ + 'x' + str(widget.allocation.height))
+ self._start_calculation([_name_to_index(widget.name)])
+
+ def _create_context(self, widget):
+ """ Create cairo context.
+ pre: widget is not None
+ """
+ ctx = widget.window.cairo_create()
+ ctx.rectangle(0, 0, \
+ widget.allocation.width, widget.allocation.height)
+ ctx.clip()
+ return ctx
+
+ def on_fitness_value_changed(self, *args):
+ """
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_fitness_value_changed %f [%s]' %
+ (args[0].get_value(), args[0].get_name()))
+ self.model.fitness[_name_to_index(args[0].get_name())] \
+ = args[0].get_value()
+
+ def on_breed_generation(self, *args):
+ if ka_task.is_completed():
+ ka_debug.info('on_breed_generation entry')
+ ka_task.GeneratorTask(self.task_breed_generation,
+ self.on_model_completed).start()
+ ka_debug.info('on_breed_generation exit')
+ else:
+ ka_debug.info('on_breed_generation ignored')
+
+ def on_random_generation(self, *args):
+ if ka_task.is_completed():
+ ka_debug.info('on_random_generation entry')
+ ka_task.GeneratorTask(self.task_random_generation,
+ self.on_model_completed).start()
+ ka_debug.info('on_random_generation exit')
+ else:
+ ka_debug.info('on_random_generation ignored')
+
+ def on_model_completed(self, *args):
+ """
+ pre: len(args) == 1
+ """
+ ka_debug.info('on_model_completed entry')
+ for cell_index in args[0]:
+ self._canvas.widgetTree.get_widget('vbox_' + str(cell_index)). \
+ set_sensitive(False)
+ self._start_calculation(args[0])
+ ka_debug.info('on_model_completed exit')
+
+ def _start_calculation(self, concerned):
+ """
+ pre: len(concerned) > 0
+ pre: forall(concerned, lambda x: 0 <= x <= self.model.size)
+ """
+ for cell_index in concerned:
+ widget = self._canvas.widgetTree.get_widget('drawingarea_'
+ + str(cell_index))
+ task = ka_task.GeneratorTask(self.task_draw,
+ self.on_image_completed)
+ task.start(self.model.protozoans[cell_index], cell_index,
+ widget.allocation.width, widget.allocation.height)
+
+ def task_breed_generation(self, *args, **kwargs):
+ """
+ pre: len(args) == 0
+ """
+ ka_debug.info('task_breed_generation entry')
+ new_indices = self.model.breed()
+ ka_debug.info('task_breed_generation exit')
+ return new_indices
+
+ def task_random_generation(self, *args, **kwargs):
+ """
+ pre: len(args) == 0
+ """
+ ka_debug.info('task_random_generation entry')
+ new_indices = self.model.random()
+ ka_debug.info('task_random_generation exit')
+ return new_indices
+
+ def task_draw(self, *args, **kwargs):
+ """
+ pre: len(args) == 4
+ """
+ protozoon, cell_index, width, height = args[0], args[1], args[2], args[3]
+ ka_debug.info('task_draw entry: ' + str(cell_index))
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
+ ctx = cairo.Context(surface)
+ protozoon.draw(ctx, width, height)
+ self.surface_cache[cell_index] = surface
+ ka_debug.info('task_draw exit: ' + str(cell_index))
+ return cell_index
+
+ def on_image_completed(self, *args):
+ ka_debug.info('on_image_completed: ' + str(args[0]))
+ cell_index = args[0]
+ widget = self._canvas.widgetTree.get_widget('drawingarea_'
+ + str(cell_index))
+ self._draw_from_cache(widget, cell_index)
+ self._canvas.widgetTree.get_widget('vbox_' + str(cell_index)). \
+ set_sensitive(True)
+ self._canvas.widgetTree.get_widget('fitness_' + str(cell_index)). \
+ set_value(self.model.fitness[cell_index])
+
+ def on_flurry_value_changed(self, *args):
+ """
+ pre: len(args) >= 1
+ pre: 0 <= args[0].get_value() <= 9
+ """
+ ka_debug.info('on_flurry_value_changed [%s]' % args[0].get_value())
+ ka_random.set_flurry(args[0].get_value())
+
+ def on_protozoon_popup(self, widget, event):
+ ka_debug.info('on_protozoon_popup: ' + widget.name)
+ self._show_popup(widget, event, \
+ 'protozoon_menu_%u' % _name_to_index(widget.name))
+
+ def on_publishprotozoon_activate(self, *args):
+ """Publish single protozoon to all other buddies.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_publishprotozoon_activate [%s]' % args[0].get_name())
+ if self._tube:
+ proto = self.model.protozoans[_name_to_index(args[0].get_name())]
+ self._tube.publish_protozoon(model_population.to_buffer(proto))
+
+ def on_favorite_activate(self, *args):
+ """Set best ranking for this protozoon.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_favorite_activate [%s]' % args[0].get_name())
+ self.model.raise_fitness(_name_to_index(args[0].get_name()))
+ self._update_gui()
+
+ def on_awfull_activate(self, *args):
+ """Set last ranking for this protozoon.
+ pre: len(args) >= 1
+ """
+ ka_debug.info('on_awfull_activate [%s]' % args[0].get_name())
+ self.model.reduce_fitness(_name_to_index(args[0].get_name()))
+ self._update_gui()
+
+ def on_received(self, code_type, code_element, code_md5):
+ """Update population or protozoon preview when received from others."""
+ ka_debug.info('on_received: Received %u bytes, type: [%s] md5: [%s]' % \
+ (len(code_element), code_type, code_md5))
+ if hashlib.md5(code_element).hexdigest() == code_md5:
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ if code_type == kandidtube.SEND_POPULATION:
+ if self.is_overwrite_allowed:
+ self._update_model(model_population.from_buffer(code_element))
+ self.model._state = model_population.STATE_EVOLVED
+ inbox_widget.queue_draw()
+ else:
+ ka_debug.info("I've already an evolved population, doing nothing")
+ elif code_type == kandidtube.SEND_PROTOZOON:
+ self.incoming.append_protozoon(model_population.from_buffer(code_element))
+ inbox_widget.queue_draw()
+ else:
+ ka_debug.info('Somebody called me using an illegal type [%s]' \
+ % code_type)
+ else:
+ ka_debug.info('Somebody called me with an corrupt data model')
+
+ def on_new_tube(self, tube, is_initiator, get_buddy):
+ """Creates communication object and sends population
+ pre: tube > 0
+ pre: get_buddy is not None
+ """
+ self._tube = kandidtube.KandidTube(self,
+ tube,
+ is_initiator,
+ get_buddy)
+
+# Event handling for incoming protozoans
+ def on_incoming_popup(self, widget, event):
+ ka_debug.info('on_incoming_popup: ' + widget.name)
+ index = _name_to_index(widget.name)
+ self._show_popup(widget, event, 'incoming_menu_'+ str(index))
+
+ def _show_popup(self, widget, event, menu):
+ ka_debug.info('%s [%s]' % (menu, widget.name))
+ popup_menu = self._canvas.widgetTree.get_widget(menu)
+ popup_menu.popup(None, None, None, event.button, event.time)
+
+ def on_accept_incoming(self, menu_item):
+ ka_debug.info('on_accept_incoming [%s]' % menu_item.parent.name)
+ new_at = self.incoming.accept_protozoon(self.model, \
+ _name_to_index(menu_item.parent.name))
+ self._start_calculation([new_at])
+# self._update_gui()
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ inbox_widget.queue_draw()
+
+ def on_decline_incoming(self, menu_item):
+ ka_debug.info('on_decline_incoming [%s]' % menu_item.parent.name)
+ self.incoming.decline_protozoon(_name_to_index(menu_item.parent.name))
+ inbox_widget = self._canvas.widgetTree.get_widget('incomingBox')
+ inbox_widget.queue_draw()
+
+ def on_incomingarea_expose(self, widget, event):
+ """ Repaint image of a single protozoon inside incoming area.
+ pre: widget is not None
+ """
+ ka_debug.info('on_incomingarea_expose: ' + widget.name)
+ self.incoming.draw(_name_to_index(widget.name), \
+ self._create_context(widget), \
+ widget.allocation.width, widget.allocation.height)
+
+# data model and persistence
+ def is_overwrite_allowed(self):
+ """Preserve an already evolved population from over writing."""
+ return self.model.is_overwrite_allowed()
+
+ def serialize_model(self):
+ """Serialize population to a string buffer."""
+ return model_population.to_buffer(self.model)
+
+ def read_file(self, file_path):
+ """Delegate reading from journal to data model
+ pre: (file_path is not None) and (len(file_path) >= 1)
+ """
+ self._update_model(model_population.read_file(file_path))
+
+ def write_file(self, file_path):
+ """Delegate writing data model to journal
+ pre: (file_path is not None) and (len(file_path) >= 1)
+ """
+ model_population.write_file(file_path, self.model)
+
+def _name_to_index(name):
+ """Extract index from last part of a name.
+ Parts are separated by _.
+ pre: name[-1].isdigit()
+ post: __return__ >= 0
+ """
+ return int(name.rsplit('_')[-1])