Back to index

gcompris  8.2.2
electric.py
Go to the documentation of this file.
00001 #  gcompris - electric
00002 #
00003 # Copyright (C) 2005 Bruno Coudoin
00004 #
00005 #   This program is free software; you can redistribute it and/or modify
00006 #   it under the terms of the GNU General Public License as published by
00007 #   the Free Software Foundation; either version 2 of the License, or
00008 #   (at your option) any later version.
00009 #
00010 #   This program is distributed in the hope that it will be useful,
00011 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 #   GNU General Public License for more details.
00014 #
00015 #   You should have received a copy of the GNU General Public License
00016 #   along with this program; if not, write to the Free Software
00017 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 #
00019 
00020 import gnomecanvas
00021 import gcompris
00022 import gcompris.utils
00023 import gcompris.skin
00024 import gcompris.admin
00025 import gcompris.bonus
00026 import gtk
00027 import gtk.gdk
00028 import gobject
00029 
00030 import os
00031 import tempfile
00032 
00033 # Set to True to debug
00034 debug = False
00035 
00036 from gcompris import gcompris_gettext as _
00037 
00038 class Gcompris_electric:
00039   """Tux hide a number, you must guess it"""
00040 
00041   def __init__(self, gcomprisBoard):
00042 
00043     self.gcomprisBoard = gcomprisBoard
00044 
00045     self.gcomprisBoard.disable_im_context = True
00046 
00047     # Part of UI : tools buttons
00048     # TOOL SELECTION
00049     self.tools = [
00050       ["DEL",    "draw/tool-del.png",     "draw/tool-del_on.png",     gcompris.CURSOR_DEL],
00051       ["SELECT", "draw/tool-select.png",  "draw/tool-select_on.png",  gcompris.CURSOR_SELECT]
00052       ]
00053 
00054     # These are used to let us restart only after the bonus is displayed.
00055     # When the bonus is displayed, it call us first with pause(1) and then
00056     # with pause(0)
00057     self.board_paused  = 0
00058     self.gamewon       = 0
00059 
00060     # The list of placed components
00061     self.components = []
00062     self.gnucap_timer = 0
00063     self.gnucap_timer_interval = 500
00064 
00065     self.gnucap_binary = None
00066 
00067   def start(self):
00068 
00069     self.gcomprisBoard.level=1
00070     self.gcomprisBoard.maxlevel=3
00071     self.gcomprisBoard.sublevel=1
00072     self.gcomprisBoard.number_of_sublevel=1
00073 
00074     gcompris.bar_set(gcompris.BAR_LEVEL)
00075 
00076     gcompris.bar_set_level(self.gcomprisBoard)
00077 
00078     gcompris.set_background(self.gcomprisBoard.canvas.root(),
00079                             gcompris.skin.image_to_skin("gcompris-bg.jpg"))
00080 
00081     self.display_game()
00082 
00083     #
00084     # Check gnucap is installed and save it's path in self.gnucap_binary
00085     #
00086     for binary in ("/usr/bin/gnucap",
00087                    "/usr/local/bin/gnucap",
00088                    "gnucap.exe"):
00089       if(os.path.exists(binary)):
00090         self.gnucap_binary = binary
00091         break
00092 
00093     if not self.gnucap_binary:
00094       gcompris.utils.dialog(_("Cannot find the 'gnucap' electric simulator.\nYou can download and install it from:\n<http://geda.seul.org/tools/gnucap/>\nTo be detected, it must be installed in\n/usr/bin/gnucap or /usr/local/bin/gnucap.\nYou can still use this activity to draw schematics without computer simulation."),
00095                             None)
00096 
00097 
00098   def end(self):
00099 
00100     gcompris.reset_locale()
00101 
00102     gcompris.set_cursor(gcompris.CURSOR_DEFAULT);
00103 
00104     # Remove the root item removes all the others inside it
00105     self.cleanup_game()
00106 
00107   def ok(self):
00108     pass
00109 
00110   def repeat(self):
00111     if debug: print("Gcompris_electric repeat.")
00112 
00113 
00114   def key_press(self, keyval, commit_str, preedit_str):
00115     # Return  True  if you did process a key
00116     # Return  False if you did not processed a key
00117     #         (gtk need to send it to next widget)
00118     return False
00119 
00120   def pause(self, pause):
00121     self.board_paused = pause
00122 
00123     # When the bonus is displayed, it call us first with pause(1) and then
00124     # with pause(0)
00125     # the game is won
00126     if(pause == 0 and self.gamewon):
00127       self.increment_level()
00128       self.gamewon = 0
00129 
00130       self.cleanup_game()
00131       self.display_game()
00132 
00133     return
00134 
00135 
00136   def set_level(self, level):
00137     self.gcomprisBoard.level=level;
00138     self.gcomprisBoard.sublevel=1;
00139 
00140     # Set the level in the control bar
00141     gcompris.bar_set_level(self.gcomprisBoard);
00142 
00143     self.cleanup_game()
00144     self.display_game()
00145 
00146   #
00147   # End of Initialisation
00148   # ---------------------
00149   #
00150 
00151   # ------------------------------------------------------------
00152   # ------------------------------------------------------------
00153   # ------------------------------------------------------------
00154   # ------------------------------------------------------------
00155 
00156   # Code that increments the sublevel and level
00157   # And bail out if no more levels are available
00158   # return 1 if continue, 0 if bail out
00159   def increment_level(self):
00160     self.gcomprisBoard.sublevel += 1
00161 
00162     if(self.gcomprisBoard.sublevel>self.gcomprisBoard.number_of_sublevel):
00163       # Try the next level
00164       self.gcomprisBoard.sublevel=1
00165       self.gcomprisBoard.level += 1
00166       gcompris.bar_set_level(self.gcomprisBoard)
00167 
00168       if(self.gcomprisBoard.level>self.gcomprisBoard.maxlevel):
00169         # the current board is finished : bail out
00170         gcompris.bonus.board_finished(gcompris.bonus.FINISHED_RANDOM)
00171         return 0
00172 
00173     return 1
00174 
00175   # Cleanup the board game
00176   def cleanup_game(self):
00177     self.gamewon = False
00178 
00179     if self.gnucap_timer :
00180       gtk.timeout_remove(self.gnucap_timer)
00181       self.gnucap_timer = 0
00182 
00183     # remove the appended items from our tools
00184     for i in range(0,len(self.tools)):
00185       self.tools[i].pop()
00186 
00187     # No more component in the simulation set
00188     self.components = []
00189 
00190     # Remove the root item removes all the others inside it
00191     self.rootitem.destroy()
00192 
00193   # Display the board game
00194   def display_game(self):
00195 
00196       # Create our rootitem. We put each canvas item in it so at the end we
00197       # only have to kill it. The canvas deletes all the items it contains
00198       # automaticaly.
00199       self.rootitem = self.gcomprisBoard.canvas.root().add(
00200           gnomecanvas.CanvasGroup,
00201           x=0.0,
00202           y=0.0
00203           )
00204 
00205       self.create_components(self.gcomprisBoard.level)
00206 
00207       # Display the tools
00208       x = 12
00209       y = 10
00210       for i in range(0,len(self.tools)):
00211 
00212         item = self.rootitem.add(
00213           gnomecanvas.CanvasPixbuf,
00214           pixbuf = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin(self.tools[i][1])),
00215           x=x,
00216           y=y
00217           )
00218         x += 45
00219         item.connect("event", self.tool_item_event, i)
00220 
00221         if(self.tools[i][0]=="SELECT"):
00222           self.select_tool = item
00223           self.select_tool_number = i
00224           # Always select the SELECT item by default
00225           self.current_tool = i
00226           self.old_tool_item = item
00227           self.old_tool_item.set(pixbuf = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin(self.tools[i][2])))
00228           gcompris.set_cursor(self.tools[i][3]);
00229 
00230         # Add the item in self.tools for later use
00231         self.tools[i].append(item)
00232 
00233 
00234   # Return the textual form of the current selected tool
00235   # Return on of self.tools[i][0]
00236   def get_current_tools(self):
00237       return(self.tools[self.current_tool][0])
00238 
00239 
00240   # Event when a tool is selected
00241   # Perform instant action or swich the tool selection
00242   def tool_item_event(self, item, event, tool):
00243 
00244     if event.type == gtk.gdk.BUTTON_PRESS:
00245       if event.button == 1:
00246         self.assign_tool(tool)
00247         return True
00248 
00249     return False
00250 
00251 
00252   def assign_tool(self, newtool):
00253     # Deactivate old button
00254     item = self.tools[self.current_tool][4]
00255     item.set(pixbuf = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin(self.tools[self.current_tool][1])))
00256 
00257     # Activate new button
00258     self.current_tool = newtool
00259     item = self.tools[newtool][4]
00260     item.set(pixbuf = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin(self.tools[self.current_tool][2])))
00261     gcompris.set_cursor(self.tools[self.current_tool][3]);
00262 
00263 
00264   #
00265   # Depending on the given level, initialize the component selector
00266   #
00267   def create_components(self, level):
00268 
00269     if(level == 1):
00270       # A list of couple (component class, it's value)
00271       component_set = ((Battery, 10),
00272                        (Bulb, 0.11),
00273                        (Switch, None),
00274                        )
00275     elif(level == 2):
00276       # A list of couple (component class, it's value)
00277       component_set = ((Battery, 10),
00278                        (Bulb, 0.11),
00279                        (Rheostat, 1000),
00280                        (Switch, None),
00281                        (Switch2, None),
00282                        (Connection, None),
00283                        )
00284     elif(level == 3):
00285       # A list of couple (component class, it's value)
00286       component_set = ((Battery, 10),
00287                        (Bulb, 0.11),
00288                        (Rheostat, 1000),
00289                        (Resistor, 1000),
00290                        (Switch, None),
00291                        (Connection, None),
00292                        (Diode, None),
00293                        )
00294 
00295     Selector(self, component_set)
00296 
00297 
00298 
00299 
00300 # ----------------------------------------------------------------------
00301 # ----------------------------------------------------------------------
00302 # ----------------------------------------------------------------------
00303 # ----------------------------------------------------------------------
00304 
00305   def run_simulation(self):
00306     if debug: print "run_simulation %s" %(self.gnucap_binary,)
00307     if not self.gnucap_binary:
00308       return
00309 
00310     if debug: print "self.gnucap_timer = %d" %(self.gnucap_timer,)
00311     if not self.gnucap_timer:
00312       if debug: print "run_simulation timeout_add"
00313       self.gnucap_timer = gobject.timeout_add(self.gnucap_timer_interval, self.call_gnucap)
00314 
00315   def call_gnucap(self):
00316     if not self.components:
00317       if debug: print "call_gnucap: No component"
00318       self.gnucap_timer = 0
00319       return
00320 
00321     connected = False
00322     for component in self.components:
00323       if component.is_connected():
00324         connected = True
00325 
00326     if debug: print "call_gnucap create the tempfile"
00327 
00328     fd, filename = tempfile.mkstemp(".gnucap", "gcompris_electric", None, True)
00329     f = os.fdopen(fd, "w+t")
00330 
00331     gnucap = "Title GCompris\n"
00332 
00333     # Ugly hack: connect a 0 ohm (1 fempto) resistor between net 0
00334     # and first net found
00335     gnucap += "R999999999 0 "
00336     for component in self.components:
00337       if component.is_connected():
00338         gnucap += str(component.get_nodes()[0].get_wires()[0].get_wire_id())
00339         break
00340     gnucap += " 1f\n"
00341 
00342     gnucap_print = ""
00343     for component in self.components:
00344       if component.is_connected():
00345         thisgnucap = component.to_gnucap("")
00346         gnucap += thisgnucap[0]
00347         gnucap_print += thisgnucap[1]
00348 
00349     gnucap += gnucap_print
00350     gnucap += ".dc\n"
00351     gnucap += ".end\n"
00352     if debug: print gnucap
00353     f.writelines(gnucap)
00354     f.close()
00355 
00356     #
00357     # Run gnucap with the temporary datafile we created.
00358     #
00359     if debug: print "calling gnucap: %s -b %s" % (self.gnucap_binary, filename)
00360     output = os.popen("%s -b %s" % (self.gnucap_binary, filename))
00361 
00362     #
00363     # Read and analyse gnucap result
00364     #
00365     if debug: print "---------------- GNUCAP OUTPUT PARSING ---------------------"
00366     line = ""
00367     for line in output.readlines():
00368       if debug: print "==="
00369       if debug: print line
00370       if(line.split() == " 0."):
00371         break
00372     if debug: print "------------------------------------------------------------"
00373 
00374     if debug: print "===>"
00375     if debug: print line
00376     if debug: print "===>"
00377 
00378     # Close it to check errors
00379     results = output.close()
00380     if results:
00381       print('Failed to run gnugap with error ', results)
00382       self.gnucap_timer = 0
00383       return
00384 
00385     values = []
00386     if(line.split()[0] == "0."):
00387       if debug: print "FOUND 0."
00388       values = line.split()
00389       del values[0]
00390 
00391     if debug: print values
00392     i = 0
00393 
00394     # Set all component values
00395     for component in self.components:
00396       if not component.is_connected():
00397         done = component.set_voltage_intensity(False, 0.0, 0.0)
00398       else:
00399         # Each Component class may have several gnucap component to retrieve
00400         # data from
00401         done = False
00402         while not done:
00403           if debug: print "Processing component %d" %(i,)
00404           try:
00405             if debug: print values[i]
00406             if debug: print values[i+1]
00407             dumy = values[i]
00408             dumy = values[i+1]
00409           except:
00410             if debug: print "Warning: gnucap parsing mismatch"
00411             done = True
00412             continue
00413 
00414           try:
00415             volt = self.convert_gnucap_value(values[i])
00416             amp = self.convert_gnucap_value(values[i+1])
00417             done = component.set_voltage_intensity(True, volt, amp)
00418             if debug: print "Converted U=%sV I=%sA" %(volt, amp)
00419           except:
00420             if debug: print "Failed to convert V=%sV I=%sA" %(values[i], values[i+1])
00421             done = component.set_voltage_intensity(False, 0.0, 0.0)
00422 
00423           i += 2
00424 
00425 
00426     if not debug: os.remove(filename)
00427     self.gnucap_timer = 0
00428 
00429   # Convert a gnucap value back in a regular number
00430   # Return a float value
00431   # Or a ValueError exception
00432   def convert_gnucap_value(self, value):
00433     unit = 1
00434 
00435     if value.endswith("T"):
00436       unit = e12
00437       value = value.replace("T", "")
00438 
00439     if value.endswith("G"):
00440       unit = 1e9
00441       value = value.replace("G", "")
00442 
00443     if value.endswith("Meg"):
00444       unit = 1e6
00445       value = value.replace("Meg", "")
00446 
00447     if value.endswith("K"):
00448       unit = 1e3
00449       value = value.replace("K", "")
00450 
00451     if value.endswith("u"):
00452       unit = 1e-6
00453       value = value.replace("u", "")
00454 
00455     if value.endswith("n"):
00456       unit = 1e-9
00457       value = value.replace("n", "")
00458 
00459     if value.endswith("p"):
00460       unit = 1e-12
00461       value = value.replace("p", "")
00462 
00463     if value.endswith("f"):
00464       unit = 1e-15
00465       value = value.replace("f", "")
00466 
00467     # return absolue value
00468     sign = 1
00469     if float(value) < 0:
00470       sign = -1
00471 
00472     return (float(sign)*float(value)*unit)
00473 
00474 
00475 
00476 
00477 # ----------------------------------------------------------------------
00478 # ----------------------------------------------------------------------
00479 # ----------------------------------------------------------------------
00480 # ----------------------------------------------------------------------
00481 # ----------------------------------------------------------------------
00482 
00483 
00484 # A wire between 2 Nodes
00485 class Wire:
00486     # BUG: Wire ID 0 is a dummy wire, we start at 1
00487     # 2006/02/19 chgans@gna.org: From gnucap manual chapter 3.1:
00488     # Node 0 is used as a reference for all calculations and is assumed
00489     #to have a voltage of zero. (This is the ground, earth or common
00490     #node.) Nodes must be nonnegative integers, but need not be
00491     #numbered sequentially.
00492     counter = 1
00493     connection = {}
00494     colors = [ 0xdfc766FFL,
00495                0xdf9766FFL,
00496                0xdf667dFFL,
00497                0xdf66bcFFL,
00498                0xc466dfFFL,
00499                0x9c66dfFFL,
00500                0xA4FFB3FFL,
00501                0x6666dfFFL,
00502                0x669fdfFFL,
00503                0x66df6cFFL,
00504                0x66dfd8FFL ]
00505 
00506     def __init__(self, electric, source_node, x1, y1, x2, y2):
00507       self.electric = electric
00508       self.rootitem = electric.rootitem
00509       self.source_node = source_node
00510       self.target_node = None
00511       self.x1 = x1
00512       self.y1 = y1
00513       self.x2 = x2
00514       self.y2 = y2
00515       self.wire_item = self.rootitem.add(
00516         gnomecanvas.CanvasLine,
00517         points=( self.x1, self.y1, self.x2, self.y2),
00518         fill_color_rgba = 0xFF0000FFL,
00519         width_units=5.0
00520         )
00521       self.wire_item.connect("event", self.delete_wire, self)
00522       self.wire_id = -1
00523       self._add_connection(source_node)
00524 
00525     def _add_connection(self, node):
00526       # Is this node already connected
00527       if(node.get_wires()):
00528         wire_id = node.get_wires()[0].get_wire_id()
00529         if debug: print "Node already connected to %d (self.wire_id=%d)" %(wire_id, self.wire_id)
00530         if self.wire_id >= 0:
00531           # This node was already connected elsewhere, reset it to use our
00532           # wire_id
00533           for wire in node.get_wires():
00534             if debug: print "   Was connected to %d" %(wire.get_wire_id(),)
00535             if wire.get_wire_id() != self.wire_id:
00536               wire.set_wire_id(self.wire_id)
00537         else:
00538           if wire_id == -1:
00539             self.wire_id = Wire.counter
00540             Wire.counter += 1
00541           else:
00542             self.wire_id = wire_id
00543 
00544       else:
00545         if self.wire_id == -1:
00546           self.wire_id = Wire.counter
00547           Wire.counter += 1
00548 
00549         Wire.connection[node] = self
00550 
00551       if debug: print "WIRE_ID = %d" %self.wire_id
00552 
00553     def set_wire_id(self, id):
00554       self.wire_id = id
00555       if(self.target_node):
00556         self.target_node.renumber_wire(self, id)
00557       if(self.source_node):
00558         self.source_node.renumber_wire(self, id)
00559 
00560       # Colorize the wire
00561       self.wire_item.set(fill_color_rgba = Wire.colors[id % len(Wire.colors)])
00562 
00563     def get_wire_id(self):
00564       return self.wire_id
00565 
00566     def destroy(self):
00567       self.wire_item.destroy()
00568       self.wire_id = -1
00569       self.source_node.remove_wire(self, None)
00570       if self.target_node:
00571         self.target_node.remove_wire(self, Wire.counter)
00572       Wire.counter += 1
00573       self.source_node = None
00574       self.target_node = None
00575       self.electric.run_simulation()
00576 
00577 
00578     def set_target_node(self, node):
00579       self.target_node = node
00580       self._add_connection(node)
00581 
00582       # Colorize the wire
00583       self.wire_item.set(fill_color_rgba = Wire.colors[self.wire_id % len(Wire.colors)])
00584 
00585     # Move wire. In fact, the attached component are moved and THEY
00586     # move the wire
00587     def move_all_wire(self, x, y):
00588       if(self.source_node and self.target_node):
00589         source_component = self.source_node.get_component()
00590         target_component = self.target_node.get_component()
00591         # TBD Need to move the components but not loosing their distance.
00592         #     Don't know yet how
00593 
00594     # Move one node of the wire at a time, depending on the given node
00595     def move(self, node, x, y):
00596       if(node == self.source_node):
00597         self.move_source_node(x, y)
00598       elif(node== self.target_node):
00599         self.move_target_node(x, y)
00600 
00601     def move_source_node(self, x1, y1):
00602       self.x1 = x1
00603       self.y1 = y1
00604       self.wire_item.set( points = (self.x1, self.y1,
00605                                     self.x2, self.y2) )
00606 
00607     def move_target_node(self, x2, y2):
00608       self.x2 = x2
00609       self.y2 = y2
00610       self.wire_item.set( points = (self.x1, self.y1,
00611                                     self.x2, self.y2) )
00612 
00613 
00614     # Callback event to delete a wire
00615     def delete_wire(self, widget, event, wire):
00616 
00617       if event.type == gtk.gdk.BUTTON_PRESS:
00618         if event.button == 1:
00619           wire.destroy()
00620         elif event.button == 3:
00621           if debug: print "WIRE_ID = %d" %self.wire_id
00622 
00623       return False
00624 
00625 
00626 #------------------------------------------------------------
00627 # Node
00628 #
00629 # x, y are node coordinate relative to the component image
00630 class Node:
00631     def __init__(self, image, name, x, y):
00632       self.image = image
00633       self.name  = name
00634       self.x     = x
00635       self.y     = y
00636       self.wires = []
00637       self.item  = None
00638 
00639     # Return the newly create Node item
00640     def create(self, component, x, y):
00641       self.component = component
00642       self.rootitem = component.get_rootitem()
00643       pixmap = gcompris.utils.load_pixmap(self.image)
00644       self.center_x =  pixmap.get_width()/2
00645       self.center_y =  pixmap.get_height()/2
00646 
00647       self.item = self.rootitem.add(
00648         gnomecanvas.CanvasPixbuf,
00649         pixbuf = pixmap,
00650         x = x + self.center_x,
00651         y = y + self.center_y,
00652         )
00653       self.item.set_data('node', self)
00654       return self.item
00655 
00656 
00657     def get_component(self):
00658       return self.component
00659 
00660     # Add a wire to this node
00661     def add_wire(self, wire):
00662       self.wires.append(wire)
00663 
00664     # Remove a wire from this node and reasign all wires
00665     # already connected to this node a new given wire number
00666     def remove_wire(self, wire, wire_id):
00667       try:
00668         self.wires.remove(wire)
00669         if(wire_id):
00670           self.renumber_wire(wire, wire_id)
00671       except:
00672         pass
00673 
00674 
00675     # Renumber the wires
00676     def renumber_wire(self, wire, wire_id):
00677       for wire in self.wires:
00678         if(wire.get_wire_id() != wire_id):
00679           wire.set_wire_id(wire_id)
00680 
00681     # Return the list of all wires on this node
00682     def get_wires(self):
00683       return(self.wires)
00684 
00685     # Has wire, return True if the given wire is already connected
00686     # to this node
00687     def has_wire(self, wire):
00688       try:
00689         if(self.wires.index(wire)):
00690           return True
00691         else:
00692           return False
00693       except:
00694         pass
00695 
00696 
00697     def move(self, x, y):
00698       if(self.item):
00699         self.item.set(x = x + self.x,
00700                       y = y + self.y)
00701 
00702       for wire in self.wires:
00703         wire.move(self,
00704                   x + self.x + self.center_x,
00705                   y + self.y + self.center_y)
00706 
00707 
00708 #------------------------------------------------------------
00709 # Component
00710 #
00711 # nodes is a list of class Node object
00712 class Component(object):
00713     counter = 0
00714     def __init__(self, electric,
00715                  gnucap_name, gnucap_value,
00716                  image, nodes):
00717       self.electric = electric
00718       self.canvas = electric.gcomprisBoard.canvas
00719       self.rootitem = electric.rootitem
00720 
00721       if gnucap_name:
00722         self.gnucap_name = gnucap_name + str(Component.counter)
00723         self.electric.components.append(self)
00724       else:
00725         self.gnucap_name = ""
00726 
00727       Component.counter += 1
00728       self.gnucap_value = gnucap_value
00729       self.image = image
00730       self.nodes = nodes
00731       # Create a group for this component
00732       self.comp_rootitem = self.rootitem.add(
00733         gnomecanvas.CanvasGroup,
00734         x=0.0,
00735         y=0.0
00736         )
00737       self.comp_rootitem.hide()
00738 
00739       # Add the component image
00740       pixmap = gcompris.utils.load_pixmap(self.image)
00741       self.x = 0
00742       self.y = 0
00743       self.center_x =  pixmap.get_width()/2
00744       self.center_y =  pixmap.get_height()/2
00745 
00746       self.component_item_offset_x = 0
00747       self.component_item_offset_y = 0
00748       self.component_item = self.comp_rootitem.add(
00749         gnomecanvas.CanvasPixbuf,
00750         pixbuf = pixmap,
00751         x = self.x + self.component_item_offset_x,
00752         y = self.y + self.component_item_offset_y,
00753         )
00754       self.component_item.connect("event", self.component_move, self)
00755 
00756       # Add each connector
00757       for node in self.nodes:
00758         item = node.create(self, node.x, node.y)
00759         item.connect("event", self.create_wire, node)
00760 
00761       # A text item to display textual values (volt and amp)
00762       self.item_values_x = 0
00763       self.item_values_y = 0
00764       self.item_values = self.comp_rootitem.add(
00765         gnomecanvas.CanvasText,
00766         x = self.item_values_x,
00767         y = self.item_values_y,
00768         font = "Sans 8",
00769         text = "",
00770         fill_color = "white",
00771         justification=gtk.JUSTIFY_CENTER
00772         )
00773       self.item_values.connect("event", self.component_move, self)
00774 
00775     # Return False if we need more value to complete our component
00776     # This is usefull in case where one Component is made of several gnucap component
00777     def set_voltage_intensity(self, valid_value, voltage, intensity):
00778       self.voltage = voltage
00779       self.intensity = intensity
00780       if(valid_value):
00781         self.item_values.set(text="V=%.2fV\nI=%.3fA"%(voltage,intensity))
00782         self.item_values.show()
00783       else:
00784         self.item_values.hide()
00785       return True
00786 
00787 
00788     def get_rootitem(self):
00789       return self.comp_rootitem
00790 
00791     def move(self, x, y):
00792       self.x =  x - self.center_x
00793       self.y =  y - self.center_y
00794 
00795       self.item_values.set(x =  self.item_values_x + self.x,
00796                            y =  self.item_values_y + self.y)
00797 
00798       self.component_item.set(x =  self.x + self.component_item_offset_x,
00799                               y =  self.y + self.component_item_offset_y )
00800 
00801       for node in self.nodes:
00802         node.move( self.x,  self.y)
00803 
00804     def show(self):
00805       self.comp_rootitem.show()
00806 
00807     def hide(self):
00808       self.comp_rootitem.hide()
00809 
00810     def destroy(self):
00811       try:
00812         # Remove ourself from the list of gnucap aware components
00813         self.electric.components.remove(self)
00814       except:
00815         pass
00816 
00817       for node in self.nodes:
00818         while node.get_wires():
00819           wire = node.get_wires()[0]
00820           node.remove_wire(wire, None)
00821           wire.destroy()
00822 
00823       self.comp_rootitem.destroy()
00824 
00825     # Return the nodes
00826     def get_nodes(self):
00827       return self.nodes
00828 
00829     # Return True if this component is connected and can provides a gnucap
00830     # description
00831     #
00832     # It assume that if a single node is not connected, the whole
00833     # component is not connected
00834     def is_connected(self):
00835 
00836       for node in self.nodes:
00837         if not node.get_wires():
00838           return False
00839 
00840       return True
00841 
00842     # Return the gnucap definition for this component
00843     # model is optional
00844     #
00845     def to_gnucap(self, model):
00846       gnucap = self.gnucap_name
00847 
00848       # No definition, it happens for connection spot
00849       # But in this case, it should not be called at all. it's not in the top level
00850       # components list.
00851       if gnucap == "":
00852         return "* Component ignored\n"
00853 
00854       # ignore component if there are some unconnected node.
00855       for node in self.nodes:
00856         if not node.get_wires():
00857           if debug: print "Component ignored"
00858           return "* Component ignored\n"
00859 
00860       for node in self.nodes:
00861         gnucap += " "
00862         if(node.get_wires()):
00863           gnucap += str(node.get_wires()[0].get_wire_id())
00864 
00865       gnucap += " "
00866       gnucap += str(self.gnucap_value)
00867       gnucap += "\n"
00868       gnucap += model
00869 
00870       return [gnucap, ".print dc + v(%s) i(%s)\n" %(self.gnucap_name, self.gnucap_name)]
00871 
00872     # Callback event to move the component
00873     def component_move(self, widget, event, component):
00874 
00875       if event.state & gtk.gdk.BUTTON1_MASK:
00876         if event.type == gtk.gdk.MOTION_NOTIFY:
00877           if(self.electric.get_current_tools()=="SELECT"):
00878             component.move(event.x, event.y)
00879 
00880         else:
00881           if(self.electric.get_current_tools()=="DEL"):
00882             self.destroy()
00883 
00884       return True
00885 
00886     # Callback event to create a wire
00887     def create_wire(self, widget, event, node):
00888 
00889       if(self.electric.get_current_tools()=="DEL"):
00890         return True
00891 
00892       if event.type == gtk.gdk.BUTTON_PRESS:
00893         if event.button == 1:
00894           bounds = widget.get_bounds()
00895           self.pos_x = (bounds[0]+bounds[2])/2
00896           self.pos_y = (bounds[1]+bounds[3])/2
00897           self.wire = Wire(self.electric, node,
00898                            self.pos_x, self.pos_y, event.x, event.y)
00899           node.add_wire(self.wire)
00900           return True
00901 
00902       if event.type == gtk.gdk.MOTION_NOTIFY:
00903         if event.state & gtk.gdk.BUTTON1_MASK:
00904           self.wire.move_target_node(event.x, event.y)
00905 
00906       if event.type == gtk.gdk.BUTTON_RELEASE:
00907         if event.button == 1:
00908           node_target = None
00909           # Our wire line confuses the get_item_at. Look arround.
00910           for x in range(-10, 11, 5):
00911             target_item = self.canvas.get_item_at(event.x + x, event.y)
00912             if target_item:
00913               node_target = target_item.get_data('node')
00914               if(node_target):
00915                 break
00916 
00917           # Take care not to wire the same component or 2 times the same node
00918           if(not node_target
00919              or node.get_component() == node_target.get_component()
00920              or node_target.has_wire(self.wire)):
00921             node.remove_wire(self.wire, None)
00922             self.wire.destroy()
00923           else:
00924             self.wire.set_target_node(node_target)
00925             node_target.add_wire(self.wire)
00926             self.electric.run_simulation()
00927 
00928           return True
00929 
00930       return False
00931 
00932 # -----------------------------------------------------------------------
00933 # -----------------------------------------------------------------------
00934 # -----------------------------------------------------------------------
00935 #
00936 # Pre defined components
00937 # ----------------------
00938 #
00939 
00940 # ----------------------------------------
00941 # RESISTOR
00942 #
00943 #
00944 class Resistor(Component):
00945   image = "electric/resistor.png"
00946   icon  = "electric/resistor_icon.png"
00947   def __init__(self, electric,
00948                x, y, value):
00949     super(Resistor, self).__init__(electric,
00950                                "R",
00951                                value,
00952                                self.image,
00953                                [Node("electric/connect.png", "A", -30, -5),
00954                                 Node("electric/connect.png", "B", 92, -5)])
00955 
00956     # Overide some values
00957     self.item_values_x = 50
00958     self.item_values_y = 12
00959 
00960     self.move(x, y)
00961     self.show()
00962 
00963 # ----------------------------------------
00964 # DIODE
00965 #
00966 #
00967 class Diode(Component):
00968   image = "electric/diode.png"
00969   icon  = "electric/diode_icon.png"
00970   def __init__(self, electric,
00971                x, y, dummy):
00972     super(Diode, self).__init__(electric,
00973                                 "D",
00974                                 "ddd 1.",
00975                                self.image,
00976                                [Node("electric/connect.png", "A", -30, -9),
00977                                 Node("electric/connect.png", "B", 95, -9)])
00978 
00979     # Overide some values
00980     self.item_values_x = 50
00981     self.item_values_y = 12
00982 
00983     self.move(x, y)
00984     self.show()
00985 
00986 
00987   # Return the gnucap definition for this component
00988   # Here we just add the diode model
00989   def to_gnucap(self, model):
00990     # Our 'ddd' Diode model
00991     # Idealized diode: ~0V treshold voltage. Characteristic graph
00992     # passes through the two points (10 mV, 10 mA) and (20 mV, 2000
00993     # mA) => N  = 0.072 IS = 5x10-5 A
00994     model = ".model  ddd  d  ( is= 50.u  rs= 0.  n= 0.072  tt= 0.  cjo= 1.p  vj= 1.  m= 0.5\n"
00995     model += "+ eg= 1.11  xti= 3.  kf= 0.  af= 1.  fc= 0.5  bv= 0.  ibv= 0.001 )\n"
00996 
00997     gnucap = []
00998     gnucap = super(Diode, self).to_gnucap(model)
00999     return gnucap
01000 
01001 # ----------------------------------------
01002 # SWITCH
01003 #
01004 #
01005 class Switch(Component):
01006   image = "electric/switch_off.png"
01007   icon  = "electric/switch_icon.png"
01008   def __init__(self, electric,
01009                x, y, dummy):
01010     self.click_ofset_x = 32
01011     self.click_ofset_y = -28
01012     self.value_on  = "0"
01013     self.value_off = "10000k"
01014 
01015     super(Switch, self).__init__(electric,
01016                                  "R",
01017                                  self.value_off,
01018                                  self.image,
01019                                  [Node("electric/connect.png", "A", -30, -10),
01020                                   Node("electric/connect.png", "B", 100, -10)])
01021 
01022     # Overide some values
01023     self.item_values.hide()
01024 
01025     self.move(x, y)
01026 
01027     pixmap = gcompris.utils.load_pixmap("electric/switch_click.png")
01028     self.click_item = self.comp_rootitem.add(
01029       gnomecanvas.CanvasPixbuf,
01030       pixbuf = pixmap,
01031       x = self.x + self.click_ofset_x,
01032       y = self.y + self.click_ofset_y,
01033       )
01034     self.click_item.connect("event", self.component_click)
01035     self.show()
01036 
01037   # Callback event on the switch
01038   def component_click(self, widget, event):
01039     if event.state & gtk.gdk.BUTTON1_MASK:
01040       if(self.gnucap_value == self.value_off):
01041         self.gnucap_value = self.value_on
01042         pixmap = gcompris.utils.load_pixmap("electric/switch_on.png")
01043       else:
01044         self.gnucap_value = self.value_off
01045         pixmap = gcompris.utils.load_pixmap("electric/switch_off.png")
01046 
01047       self.component_item.set(pixbuf = pixmap)
01048       self.electric.run_simulation()
01049 
01050     return False
01051 
01052   # Callback event to move the component
01053   def component_move(self, widget, event, component):
01054      super(Switch, self).component_move(widget, event, component)
01055 
01056      if(self.electric.get_current_tools()=="DEL"):
01057        return True
01058 
01059      self.click_item.set(
01060        x = self.x + self.click_ofset_x,
01061        y = self.y + self.click_ofset_y)
01062 
01063      return True
01064 
01065   # Return False if we need more value to complete our component
01066   # This is usefull in case where one Component is made of several gnucap component
01067   def set_voltage_intensity(self, valid_value, voltage, intensity):
01068     self.voltage = voltage
01069     self.intensity = intensity
01070     # Never show values
01071     self.item_values.hide()
01072     return True
01073 
01074 # ----------------------------------------
01075 # SWITCH2
01076 #
01077 #
01078 class Switch2(Component):
01079   image = "electric/switch2_on.png"
01080   icon  = "electric/switch2_icon.png"
01081   def __init__(self, electric,
01082                x, y, dummy):
01083     self.click_ofset_x = 22
01084     self.click_ofset_y = -28
01085     self.gnucap_current_resistor = 1
01086     self.gnucap_nb_resistor = 0
01087     self.value_on  = "0"
01088     self.value_off = "10000k"
01089     self.value_top = self.value_on
01090     self.value_bottom = self.value_off
01091 
01092     super(Switch2, self).__init__(electric,
01093                                    "R",
01094                                    self.value_off,
01095                                    self.image,
01096                                    [Node("electric/connect.png", "C", -15, 0),
01097                                     Node("electric/connect.png", "A", 80, -25),
01098                                     Node("electric/connect.png", "B", 80, 25)])
01099 
01100     # Overide some values
01101     self.item_values.hide()
01102 
01103     self.move(x, y)
01104 
01105     pixmap = gcompris.utils.load_pixmap("electric/switch_click.png")
01106     self.click_item = self.comp_rootitem.add(
01107       gnomecanvas.CanvasPixbuf,
01108       pixbuf = pixmap,
01109       x = self.x + self.click_ofset_x,
01110       y = self.y + self.click_ofset_y,
01111       )
01112     self.click_item.connect("event", self.component_click)
01113     self.show()
01114 
01115 
01116   # Callback event on the switch
01117   def component_click(self, widget, event):
01118     if event.state & gtk.gdk.BUTTON1_MASK:
01119       if(self.value_top == self.value_off):
01120         self.value_top = self.value_on
01121         self.value_bottom = self.value_off
01122         self.component_item_offset_y = - 6
01123         pixmap = gcompris.utils.load_pixmap("electric/switch2_on.png")
01124       else:
01125         self.value_top = self.value_off
01126         self.value_bottom = self.value_on
01127         self.component_item_offset_y = 10
01128         pixmap = gcompris.utils.load_pixmap("electric/switch2_off.png")
01129 
01130       self.component_item.set(y = self.y + self.component_item_offset_y)
01131       self.component_item.set(pixbuf = pixmap)
01132       self.electric.run_simulation()
01133 
01134     return False
01135 
01136   # Callback event to move the component
01137   def component_move(self, widget, event, component):
01138     super(Switch2, self).component_move(widget, event, component)
01139 
01140     if(self.electric.get_current_tools()=="DEL"):
01141       return True
01142 
01143     self.click_item.set(
01144       x = self.x + self.click_ofset_x,
01145       y = self.y + self.click_ofset_y)
01146 
01147     return True
01148 
01149   # Return True if this component is connected and can provides a gnucap
01150   # description
01151   #
01152   # The switch2 needs at least 2 connected nodes
01153   #
01154   def is_connected(self):
01155     count = 0
01156     for node in self.nodes:
01157       if node.get_wires():
01158         count += 1
01159 
01160     if count >= 2:
01161       return True
01162 
01163     return False
01164 
01165   # Return the gnucap definition for a single resitor of the switch2
01166   # node_id1 and node_id2 are the index in the list of nodes
01167   def to_gnucap_res(self, gnucap_name, node_id1, node_id2, gnucap_value):
01168     gnucap = gnucap_name
01169     gnucap += " "
01170 
01171     for i in (node_id1, node_id2):
01172       node = self.nodes[i]
01173       if node.get_wires():
01174         gnucap += str(node.get_wires()[0].get_wire_id())
01175 
01176       gnucap += " "
01177 
01178     gnucap += " "
01179     gnucap += str(gnucap_value)
01180     gnucap += "\n"
01181 
01182     return [gnucap, ".print dc + v(%s) i(%s)\n" %(gnucap_name, gnucap_name)]
01183 
01184   # Return the gnucap definition for this component
01185   # depending of the connected nodes, it create one or two resistor
01186   def to_gnucap(self, model):
01187 
01188     gnucap = ["", ""]
01189 
01190     # reset set_voltage_intensity counter
01191     self.gnucap_current_resistor = 0
01192     self.gnucap_nb_resistor = 0
01193 
01194     # top resistor
01195     if self.nodes[0].get_wires() and \
01196        self.nodes[1].get_wires():
01197       gnucap_resp = self.to_gnucap_res(self.gnucap_name + "_top", 0, 1,
01198                                         self.value_top)
01199       gnucap[0] += gnucap_resp[0]
01200       gnucap[1] += gnucap_resp[1]
01201       self.gnucap_nb_resistor += 1
01202 
01203     # bottom resistor
01204     if self.nodes[0].get_wires() and \
01205        self.nodes[2].get_wires():
01206       gnucap_resp = self.to_gnucap_res(self.gnucap_name + "_bot", 0, 2,
01207                                        self.value_bottom)
01208       gnucap[0] += gnucap_resp[0]
01209       gnucap[1] += gnucap_resp[1]
01210       self.gnucap_nb_resistor += 1
01211 
01212     return [gnucap[0], gnucap[1]]
01213 
01214   # Return False if we need more value to complete our component
01215   # This is usefull in case where one Component is made of several gnucap component
01216   def set_voltage_intensity(self, valid_value, voltage, intensity):
01217     # Never show values
01218     self.item_values.hide()
01219 
01220     self.gnucap_current_resistor += 1
01221 
01222     if(self.gnucap_nb_resistor == self.gnucap_current_resistor):
01223       return True
01224     else:
01225       return False
01226 
01227 # ----------------------------------------
01228 # RHEOSTAT
01229 #
01230 #
01231 class Rheostat(Component):
01232   image = "electric/resistor_track.png"
01233   icon  = "electric/resistor_track_icon.png"
01234   def __init__(self, electric,
01235                x, y, resitance):
01236     self.gnucap_current_resistor = 1
01237     self.gnucap_nb_resistor = 0
01238     self.wiper_ofset_x = -2
01239     self.wiper_ofset_min_y = 22
01240     self.wiper_ofset_max_y = 103
01241     self.wiper_ofset_y = self.wiper_ofset_min_y
01242     self.resitance = resitance
01243     super(Rheostat, self).__init__(electric,
01244                                    "R",
01245                                    self.resitance,
01246                                    self.image,
01247                                    [Node("electric/connect.png", "A", 0, -25),
01248                                     Node("electric/connect.png", "B", 50, 50),
01249                                     Node("electric/connect.png", "C", 0, 125)])
01250 
01251     # Overide some values
01252     self.item_values_x = 20
01253     self.item_values_y = 70
01254     self.item_values.set(fill_color="blue")
01255 
01256     self.move(x, y)
01257 
01258     # The wiper wire
01259     self.wiper_wire_item = self.comp_rootitem.add(
01260       gnomecanvas.CanvasLine,
01261       points = (0,0,0,0),
01262       fill_color_rgba = 0x5A5A5AFFL,
01263       width_units=5.0
01264       )
01265     self.update_wiper_wire()
01266 
01267     pixmap = gcompris.utils.load_pixmap("electric/resistor_wiper.png")
01268     self.wiper_item = self.comp_rootitem.add(
01269       gnomecanvas.CanvasPixbuf,
01270       pixbuf = pixmap,
01271       x = self.x + self.wiper_ofset_x,
01272       y = self.y + self.wiper_ofset_y,
01273       )
01274     self.wiper_item.connect("event", self.component_click)
01275 
01276     self.show()
01277 
01278   def update_wiper_wire(self):
01279     self.wiper_wire_item.set(
01280       points = (self.x + self.wiper_ofset_x + 35,
01281                 self.y + self.wiper_ofset_y + 10,
01282                 self.x + 55,
01283                 self.y + 65))
01284 
01285   def move_wiper(self, new_y):
01286     if(new_y>self.y+self.wiper_ofset_max_y):
01287       self.wiper_ofset_y = self.wiper_ofset_max_y
01288     elif(new_y<self.y+self.wiper_ofset_min_y):
01289       self.wiper_ofset_y = self.wiper_ofset_min_y
01290     else:
01291       self.wiper_ofset_y = new_y - self.y
01292 
01293     self.wiper_item.set(
01294       y = self.y + self.wiper_ofset_y,
01295       )
01296     self.update_wiper_wire()
01297     self.electric.run_simulation()
01298 
01299   # Fixme: can't connect "scroll-event" to this function
01300   def component_scroll(self, widget, event):
01301     if event.type == gtk.gdk.SCROLL:
01302       if event.direction == gtk.gdk.SCROLL_UP:
01303         self.move_wiper(self.y - 5)
01304       elif event.type == gtk.gdk.SCROLL_DOWN:
01305         self.move_wiper(self.y + 5)
01306     return True
01307 
01308   # Callback event on the wiper
01309   def component_click(self, widget, event):
01310     # drag and drop
01311     if event.type == gtk.gdk.MOTION_NOTIFY:
01312       if event.state & gtk.gdk.BUTTON1_MASK:
01313         self.move_wiper(event.y)
01314 
01315     return True
01316 
01317   # Callback event to move the component
01318   def component_move(self, widget, event, component):
01319      super(Rheostat, self).component_move(widget, event, component)
01320 
01321      if(self.electric.get_current_tools()=="DEL"):
01322        return True
01323 
01324      self.wiper_item.set(
01325        x = self.x + self.wiper_ofset_x,
01326        y = self.y + self.wiper_ofset_y)
01327      self.update_wiper_wire()
01328 
01329      return True
01330 
01331   # Return True if this component is connected and can provides a gnucap
01332   # description
01333   #
01334   # The rheostat needs at least 2 connected nodes
01335   #
01336   def is_connected(self):
01337     count = 0
01338     for node in self.nodes:
01339       if node.get_wires():
01340         count += 1
01341 
01342     if count >= 2:
01343       return True
01344 
01345     return False
01346 
01347   # Return the gnucap definition for a single resitor of the rheostat
01348   # node_id1 and node_id2 are the index in the list of nodes
01349   def to_gnucap_res(self, gnucap_name, node_id1, node_id2, gnucap_value):
01350     # Ignore the component if there is some unconnected nodes.
01351     for i in (node_id1, node_id2):
01352       node = self.nodes[i]
01353       if not node.get_wires():
01354         gnucap = "* %s: component ignored: not connected\n" %(gnucap_name)
01355         return [gnucap, ""]
01356 
01357     gnucap = gnucap_name
01358     gnucap += " "
01359 
01360     for i in (node_id1, node_id2):
01361       node = self.nodes[i]
01362       if node.get_wires():
01363         gnucap += str(node.get_wires()[0].get_wire_id())
01364 
01365       gnucap += " "
01366 
01367     gnucap += " "
01368     gnucap += str(gnucap_value)
01369     gnucap += "\n"
01370 
01371     return [gnucap, ".print dc + v(%s) i(%s)\n" %(gnucap_name, gnucap_name)]
01372 
01373   # Return the gnucap definition for this component
01374   # depending of the connected nodes, it create one or two resistor
01375   def to_gnucap(self, model):
01376 
01377     gnucap = ["", ""]
01378 
01379     # reset set_voltage_intensity counter
01380     self.gnucap_current_resistor = 0
01381 
01382     gnucap_value = self.resitance * \
01383                    (self.wiper_ofset_y - self.wiper_ofset_min_y) / \
01384                    (self.wiper_ofset_max_y - self.wiper_ofset_min_y)
01385 
01386     # Main resistor
01387     if self.nodes[0].get_wires() and \
01388        not self.nodes[1].get_wires() and \
01389        self.nodes[2].get_wires():
01390       self.gnucap_nb_resistor = 1
01391       gnucap_resp = self.to_gnucap_res(self.gnucap_name + "_all", 0, 2,
01392                                        self.resitance)
01393       gnucap[0] += gnucap_resp[0]
01394       gnucap[1] += gnucap_resp[1]
01395       return [gnucap[0], gnucap[1]]
01396 
01397 
01398     self.gnucap_nb_resistor = 0
01399 
01400     # top resistor
01401     if self.nodes[0].get_wires() and \
01402        self.nodes[1].get_wires():
01403       self.gnucap_nb_resistor += 1
01404       gnucap_resp  = self.to_gnucap_res(self.gnucap_name + "_top", 0, 1,
01405                                         gnucap_value)
01406       gnucap[0] += gnucap_resp[0]
01407       gnucap[1] += gnucap_resp[1]
01408 
01409     # bottom resistor
01410     if self.nodes[1].get_wires() and \
01411        self.nodes[2].get_wires():
01412       self.gnucap_nb_resistor += 1
01413       gnucap_resp = self.to_gnucap_res(self.gnucap_name + "_bot", 1, 2,
01414                                        self.resitance - gnucap_value)
01415       gnucap[0] += gnucap_resp[0]
01416       gnucap[1] += gnucap_resp[1]
01417 
01418     return [gnucap[0], gnucap[1]]
01419 
01420   # Return False if we need more value to complete our component
01421   # This is usefull in case one Component is made of several gnucap component
01422   def set_voltage_intensity(self, valid_value, voltage, intensity):
01423     self.gnucap_current_resistor += 1
01424     if self.gnucap_current_resistor == 1:
01425       self.voltage = voltage
01426       self.intensity = intensity
01427       if(valid_value ):
01428         self.item_values.set(text="U=%.1fV\nI=%.2fA"%(voltage,intensity))
01429         self.item_values.show()
01430       else:
01431         self.item_values.hide()
01432 
01433     if self.gnucap_nb_resistor != 1:
01434       self.gnucap_current_resistor += 1
01435       if self.gnucap_current_resistor > self.gnucap_nb_resistor:
01436         self.gnucap_current_resistor = 0
01437         return True
01438     else:
01439       self.gnucap_current_resistor = 0
01440       return True
01441     return False
01442 
01443 # ----------------------------------------
01444 # BULB
01445 #
01446 #
01447 class Bulb(Component):
01448   image = "electric/bulb1.png"
01449   icon  = "electric/bulb_icon.png"
01450   def __init__(self, electric,
01451                x, y, power_max):
01452     self.internal_resistor = 1000
01453     super(Bulb, self).__init__(electric,
01454                                "R",
01455                                self.internal_resistor,
01456                                self.image,
01457                                [Node("electric/connect.png", "A", 7, 170),
01458                                 Node("electric/connect.png", "B", 85, 170)])
01459     # Overide some values
01460     self.item_values_x = 55
01461     self.item_values_y = 80
01462     self.item_values.set(fill_color="red")
01463 
01464     # Specific Bulb values
01465     self.is_blown = False
01466     self.move(x, y)
01467     self.show()
01468     self.power_max = power_max
01469     self.resistor_blown = 100000000
01470 
01471   # Change the pixmap depending on the real power in the Bulb
01472   # Return False if we need more value to complete our component
01473   # This is usefull in case where one Component is made of several gnucap component
01474   def set_voltage_intensity(self, valid_value, voltage, intensity):
01475 
01476     super(Bulb, self).set_voltage_intensity(valid_value, voltage, intensity)
01477 
01478     # If the Bulb is blown, do not update it anymore
01479     if self.is_blown:
01480       return True
01481 
01482     power = abs(voltage * intensity)
01483     image_index = min((power * 10) /  self.power_max + 1, 11)
01484     pixmap = gcompris.utils.load_pixmap("electric/bulb%d.png" %(image_index,))
01485     if debug: print "Power = %f (Max=%f) Image index = %d" %(power, self.power_max, image_index)
01486     self.component_item.set(pixbuf = pixmap)
01487 
01488     # If the Bulb is blown, we have to change it's internal
01489     # Resistor value to infinite and ask for a circuit recalc
01490     if image_index == 11:
01491       self.gnucap_value = self.resistor_blown
01492       self.electric.run_simulation()
01493       self.is_blown = True
01494 
01495     return True
01496 
01497   # Callback event to move the component
01498   # We override it to repair the Bulb
01499   def component_move(self, widget, event, component):
01500     # If the Bulb is blown and we get a click repair it
01501     # If the bulb is not blown, you can blown it by right clicking on it
01502     if (event.state & gtk.gdk.BUTTON1_MASK) and self.electric.get_current_tools()=="SELECT":
01503       if self.is_blown:
01504         self.is_blown = False
01505         self.gnucap_value = self.internal_resistor
01506         self.electric.run_simulation()
01507 
01508     elif (event.state & gtk.gdk.BUTTON3_MASK) and self.electric.get_current_tools()=="SELECT":
01509       if not self.is_blown:
01510         # Blown us with arbitrate high value
01511         self.set_voltage_intensity(True, 100, 10)
01512 
01513     return super(Bulb, self).component_move(widget, event, component)
01514 
01515 
01516 
01517 # ----------------------------------------
01518 # BATTERY
01519 #
01520 #
01521 class Battery(Component):
01522   image = "electric/battery.png"
01523   icon  = "electric/battery_icon.png"
01524   def __init__(self, electric,
01525                x, y, value):
01526     super(Battery, self).__init__(electric,
01527                                "Vsupply",
01528                                value,
01529                                self.image,
01530                                [Node("electric/connect.png", "A", 6, -35),
01531                                 Node("electric/connect.png", "B", 6, 120)])
01532     # Overide some values
01533     self.item_values_x = 23
01534     self.item_values_y = 70
01535 
01536     self.move(x, y)
01537     self.show()
01538 
01539   # Return False if we need more value to complete our component
01540   # This is usefull in case where one Component is made of several gnucap component
01541   def set_voltage_intensity(self, valid_value, voltage, intensity):
01542     super(Battery, self).set_voltage_intensity(valid_value, voltage, intensity)
01543 
01544     if(abs(self.intensity) > 1):
01545       # Short circuit case, set the dead battery icon
01546       self.component_item.set(pixbuf = gcompris.utils.load_pixmap("electric/battery_dead.png"))
01547     else:
01548       self.component_item.set(pixbuf = gcompris.utils.load_pixmap("electric/battery.png"))
01549 
01550     return True
01551 
01552 # ----------------------------------------
01553 # CONNECTION
01554 # (a simple spot to connect wire and make a cute scematic)
01555 #
01556 class Connection(Component):
01557   image = "electric/connect_spot.png"
01558   icon  = "electric/connect_icon.png"
01559   def __init__(self, electric,
01560                x, y, dummy):
01561     super(Connection, self).__init__(electric,
01562                                      "",
01563                                      "",
01564                                      self.image,
01565                                      [Node("electric/connect.png", "A", 18, 10)])
01566     self.move(x, y)
01567     self.show()
01568 
01569 # -----------------------------------------------------------------------
01570 # -----------------------------------------------------------------------
01571 # -----------------------------------------------------------------------
01572 #
01573 # Component selector
01574 # ------------------
01575 #
01576 
01577 class Selector:
01578     def __init__(self, electric, components_class):
01579       self.electric = electric
01580       self.rootitem = electric.rootitem
01581 
01582       self.rootitem.add(
01583         gnomecanvas.CanvasPixbuf,
01584         pixbuf = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("draw/tool-selector.png")),
01585         x=5,
01586         y=5.0,
01587         width=107.0,
01588         height=517.0,
01589         width_set=True,
01590         height_set=True
01591         )
01592 
01593       self.x = 15
01594       self.y = 60
01595 
01596       index_y = 10
01597       gap     = 20
01598       self.init_coord = {}
01599       self.offset_x = self.offset_y = 0
01600 
01601       for component_class in components_class:
01602         pixmap = gcompris.utils.load_pixmap(component_class[0].icon)
01603         item = self.rootitem.add(
01604           gnomecanvas.CanvasPixbuf,
01605           pixbuf = pixmap,
01606           x = self.x,
01607           y = self.y + index_y,
01608           )
01609         # Save the original coord to set it back once dropped
01610         self.init_coord[component_class[0]] = (self.x, self.y + index_y)
01611         index_y += pixmap.get_height() + gap
01612 
01613         item.connect("event", self.component_click, component_class)
01614 
01615 
01616     # Callback event on the component
01617     def component_click(self, widget, event, component_class):
01618 
01619       if (event.state & gtk.gdk.BUTTON1_MASK
01620           and self.electric.get_current_tools()=="DEL"):
01621         # Switch to select mode
01622         self.electric.assign_tool(1)
01623 
01624       if event.type == gtk.gdk.MOTION_NOTIFY:
01625         if event.state & gtk.gdk.BUTTON1_MASK:
01626           # Save the click to image offset
01627           if self.offset_x == 0:
01628             bounds = widget.get_bounds()
01629             self.offset_x = (event.x - bounds[0])
01630             self.offset_y = (event.y - bounds[1])
01631 
01632           widget.set(x = event.x - self.offset_x,
01633                      y = event.y - self.offset_y)
01634 
01635       if event.type == gtk.gdk.BUTTON_RELEASE:
01636         if event.button == 1:
01637           bounds = widget.get_bounds()
01638           component_class[0](self.electric,
01639                              event.x - self.offset_x + (bounds[2]-bounds[0])/2,
01640                              event.y - self.offset_y + (bounds[3]-bounds[1])/2,
01641                              component_class[1])
01642 
01643           widget.set(x = self.init_coord[component_class[0]][0],
01644                      y = self.init_coord[component_class[0]][1])
01645 
01646           self.offset_x = self.offset_y = 0
01647 
01648         return True
01649 
01650 
01651     # ------------------------------------------------------------
01652     # ------------------------------------------------------------
01653     # ------------------------------------------------------------
01654 
01655 def stop_board():
01656     gcompris.bonus.board_finished(gcompris.bonus.FINISHED_RANDOM)
01657