mousetrap r21 - in branches/ng/src: mousetrap mousetrap/lib mousetrap/ui mousetrap/ui/scripts ocvfw/dev ocvfw/idm



Author: flaper
Date: Sun Feb 22 18:24:52 2009
New Revision: 21
URL: http://svn.gnome.org/viewvc/mousetrap?rev=21&view=rev

Log:
Added mouse movements

Added:
   branches/ng/src/mousetrap/environment.py   (contents, props changed)
   branches/ng/src/mousetrap/lib/mouse.py   (contents, props changed)
Modified:
   branches/ng/src/mousetrap/mousetrap.py
   branches/ng/src/mousetrap/start
   branches/ng/src/mousetrap/ui/main.py
   branches/ng/src/mousetrap/ui/scripts/screen.py
   branches/ng/src/mousetrap/ui/widgets.py
   branches/ng/src/ocvfw/dev/camera.py
   branches/ng/src/ocvfw/idm/forehead.py

Added: branches/ng/src/mousetrap/environment.py
==============================================================================
--- (empty file)
+++ branches/ng/src/mousetrap/environment.py	Sun Feb 22 18:24:52 2009
@@ -0,0 +1,112 @@
+
+# -*- coding: utf-8 -*-
+
+# mouseTrap
+#
+# Copyright 2008 Flavio Percoco Premoli
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library 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
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+""" Holds mouseTrap internal information. """
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2008 Flavio Percoco Premoli."
+__license__   = "GPLv2"
+
+import sys
+import os
+import gtk
+
+## mouseTrap Version
+version     = "@MOUSETRAP_VERSION@"
+
+## "--prefix" parameter used when configuring the build.
+prefix      = "@prefix@"
+
+## The package name (should be "mouseTrap").
+package     = "@PACKAGE@"
+
+## The name of the data directory (usually "share").
+datadirname = "%s/@DATADIRNAME@" % prefix
+
+## Directly mouseTrap data dir
+mTDataDir = "%s/mouseTrap" % datadirname
+
+## The actuall running desktop manager.
+desktop = os.getenv("DESKTOP_MANAGER")
+
+## The name of the O.S
+osName = os.name
+
+## The application's path
+appPath = os.path.dirname(__file__)
+
+## The user's home directory
+home = os.path.expanduser("~")
+
+## Configurations dir
+configPath = home + "/.mouseTrap/"
+
+## Configurations dir
+configPath = "%s/.mouseTrap/" % home
+
+## Scripts Path
+scriptsPath = "%s/scripts/" % configPath
+
+## Profiles Path
+profilesPath = "%s/profiles/" % scriptsPath
+
+## The config file
+configFile = configPath + "userSettings.cfg"
+
+## The debug file
+debugFile = configPath + "mouseTrap_DEBUG.log"
+
+## The language path
+langPath = "%s/locale/" % datadirname
+
+## The haarcascade folder
+haarcascades = appPath + "/haarcascade"
+
+## The debugging parts
+DEBUG = ['widget']
+
+## mouseTrap Modules
+mTModules = { 'lTr' : '_startListener',
+              'cAm' : '_startCam',
+			  'wTp' : '_startWidgetsTrap'}
+
+
+## Screen Resolution
+screen       = { 'width'  : gtk.gdk.screen_width(),
+                 'height' : gtk.gdk.screen_height()}
+
+## Mose Movement Modes
+mouseModes = { }
+
+###################################################
+#                                                 #
+#          MOUSETRAP'S STATES DEFINITION          #
+#                                                 #
+###################################################
+
+## Mousetrap is active and the mouse pointer can be moved
+ACTIVE = "active"
+
+## Mousetrap is active and the click dialog is not hidden.
+CLKDLG = "clk-dialog"

Added: branches/ng/src/mousetrap/lib/mouse.py
==============================================================================
--- (empty file)
+++ branches/ng/src/mousetrap/lib/mouse.py	Sun Feb 22 18:24:52 2009
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+
+# mouseTrap
+#
+# Copyright 2008 Flavio Percoco Premoli
+#
+# This file is part of mouseTrap.
+#
+# mouseTrap 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 2 of the License, or
+# (at your option) any later version.
+#
+# mouseTrap 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 mouseTrap.  If not, see <http://www.gnu.org/licenses/>.
+
+"""The mouse events handler."""
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2008 Flavio Percoco Premoli"
+__license__   = "GPLv2"
+
+import gtk
+import pyatspi
+import environment as env
+import Xlib.ext.xtest as xtest
+
+from Xlib import X, display
+
+clickVal  = { X.ButtonPress   : 0,
+              X.ButtonRelease : 5 }
+
+clickType = { 'p' : [ X.ButtonPress ],
+              'r' : [ X.ButtonRelease ],
+              'c' : [ X.ButtonPress, X.ButtonRelease],
+              'd' : [ X.ButtonPress, X.ButtonRelease,
+                      X.ButtonPress, X.ButtonRelease ] }
+
+
+## GTK Display for any user
+gtkDisplay = gtk.gdk.Display( "" )
+
+## X Display for non gnome users
+xDisplay   = display.Display()
+
+isGnome = False
+if env.desktop == "gnome":
+    isGnome = True
+    #debug.debug( "mousetrap.mouse", "GNOME desktop has been detected" )
+
+    ## pyatspi registry for gnome users
+    reg = pyatspi.Registry
+
+## Is the D&D click being used ?
+dragging = False
+
+
+def position( *arg ):
+    """
+    Get the absolute position of the mouse pointer
+
+    Returns A list with the X and Y coordinates.
+    """
+    return list(gtkDisplay.get_pointer()[1:3])
+
+def click( x = None, y = None, button = "bc1" ):
+    """
+    Execute Mouse Clicks. If the mouse is dragging an object
+    then the release click will be performed.
+
+    Arguments:
+    - x: The X coordinate in the screen.
+    - y: The Y coordinate in the screen.
+    - button: The button click that has to be performed.
+
+    Return True
+    """
+
+    global isGnome
+    global dragging
+
+    if not x or not y:
+        x, y = position()
+
+    if dragging:
+        button = button[:2] + 'r'
+        dragging = False
+    elif button[2] == 'p':
+        dragging = True
+
+    if isGnome:
+        try:
+            reg.generateMouseEvent( x, y, button )
+        except:
+            isGnome = False
+    else:
+        for click in clickType[button[2]]:
+            xDisplay.xtest_fake_input(click, int(button[1]), clickVal[click])
+        xDisplay.flush()
+
+    return True
+
+def move( x=0, y=0, point=None ):
+    """
+    Changes the mouse position to the specified coords.
+
+    Arguments:
+    - self: The main object pointer.
+    - x: The x position.
+    - y: the y position.
+    """
+    global isGnome
+
+    if point is not None:
+        x, y = point.x, point.y
+
+    if isGnome:
+        try:
+            reg.generateMouseEvent( x, y, 'abs' )
+        except:
+            isGnome = False
+    else:
+        xtest.fake_input( xDisplay, X.MotionNotify, x = x, y = y)
+        #display.sync()
+        xDisplay.flush()
+
+    return True
+
+# Dictionary Dispatcher
+dsp = { "move"      : move,
+        "click"     : click,
+        "position"  : position }
+
+def handler( func ):
+    """
+    Mouse functions decorator.
+
+    Arguments:
+    - func: The function called to access the decorator.
+
+    Return The wrapper
+    """
+
+    def wrapper( *arg, **kw ):
+        """
+        Wrapper function.
+
+        This functions will execute the required function passing the arguments to it.
+
+        Return the function executed
+        """
+        return dsp[arg[0]]( *arg[1:], **kw )
+
+    return wrapper

Modified: branches/ng/src/mousetrap/mousetrap.py
==============================================================================
--- branches/ng/src/mousetrap/mousetrap.py	(original)
+++ branches/ng/src/mousetrap/mousetrap.py	Sun Feb 22 18:24:52 2009
@@ -30,6 +30,7 @@
 
 import gtk
 import gobject
+import lib.mouse as mouse
 #import ocvfw.idm as idm
 from ocvfw import pocv
 from ui.scripts.screen import ScriptClass
@@ -74,10 +75,14 @@
         Arguments:
         - self: The main object pointer.
         """
-        return ScriptClass
+        return ScriptClass()
 
     def update_frame(self):
-        self.itf.update_frame(self.idm.get_image())
+        self.itf.update_frame(self.idm.get_image(), self.idm.get_pointer())
+        return True
+
+    def update_pointers(self):
+        self.itf.script.update_items(self.idm.get_pointer())
         return True
 
 
@@ -86,6 +91,7 @@
 loop = gobject.MainLoop()
 a = Controller()
 a.start()
-gobject.timeout_add(100, a.update_frame)
+gobject.timeout_add(150, a.update_frame)
+#gobject.timeout_add(50, a.update_pointers)
 gobject.threads_init()
 loop.run()

Modified: branches/ng/src/mousetrap/start
==============================================================================
--- branches/ng/src/mousetrap/start	(original)
+++ branches/ng/src/mousetrap/start	Sun Feb 22 18:24:52 2009
@@ -1,3 +1,24 @@
 #!/bin/sh
 
+getDesktop() {
+
+    if [ "x$DESKTOP_SESSION" = "default" -o "x$DESKTOP_SESSION" = "x" ]
+    then
+        user=`whoami`
+        desktop=`ps -eo pid,ruser,ruid,args | egrep gnome-session | grep -v grep | grep $user`
+
+        if [ "x$desktop" = "x" ]
+        then
+            export DESKTOP_MANAGER=other
+        else
+            export DESKTOP_MANAGER=gnome
+        fi
+
+    else
+        export DESKTOP_MANAGER=$DESKTOP_SESSION
+    fi
+
+}
+
+getDesktop
 PYTHONPATH=$PYTHONPATH:../ python mousetrap.py

Modified: branches/ng/src/mousetrap/ui/main.py
==============================================================================
--- branches/ng/src/mousetrap/ui/main.py	(original)
+++ branches/ng/src/mousetrap/ui/main.py	Sun Feb 22 18:24:52 2009
@@ -117,7 +117,7 @@
         self.vBox.pack_start(self.cap_expander)
 
         self.map_expander = gtk.expander_new_with_mnemonic("_Script Mapper")
-        self.map_expander.add(self.script())
+        self.map_expander.add(self.script)
         self.map_expander.set_expanded(True)
         #expander.connect('notify::expanded', self.expanded_cb)
         self.vBox.pack_start(self.map_expander)
@@ -150,7 +150,7 @@
         self.add(self.vBox)
         self.show()
 
-    def update_frame(self, img):
+    def update_frame(self, img, point):
         """
         Updates the image
 
@@ -158,6 +158,11 @@
         - self: The main object pointer.
         - img: The IPLimage object.
         """
+
+        if img is None:
+            return False
+
+        #self.script.update_items(point)
         buff = gtk.gdk.pixbuf_new_from_data( img.imageData, gtk.gdk.COLORSPACE_RGB, False, 8, \
                                              int(img.width), int(img.height), img.widthStep )
 
@@ -322,6 +327,8 @@
         self.desp = 0
 
         self.pointer = [ 0, 0 ]
+
+        self.connect("motion_notify_event", self.motion_notify_event)
 #
 #     def registerArea( self, area ):
 #         """
@@ -351,6 +358,9 @@
 #         events.registerTrigger( {  "X" : X, "Y" : Y, "size" : size, "last" : 0,
 #                                    "callback" : callback, "args" : args, "kwds" : kwds })
 #
+    def motion_notify_event(self, *args):
+        print("PASA")
+
     def draw_rectangle( self, x, y, width, height, color ):
         """
         Draws a rectangle in the DrawingArea.

Modified: branches/ng/src/mousetrap/ui/scripts/screen.py
==============================================================================
--- branches/ng/src/mousetrap/ui/scripts/screen.py	(original)
+++ branches/ng/src/mousetrap/ui/scripts/screen.py	Sun Feb 22 18:24:52 2009
@@ -30,7 +30,8 @@
 
 import gtk
 import time
-from ui.main import CoordsGui
+import environment as env
+import lib.mouse as mouse
 from ui.widgets import Mapper
 
 # The name given for the config file
@@ -43,8 +44,63 @@
 class ScriptClass(Mapper):
 
     def __init__(self):
-        #CoordsGui.__init__(self)
-        Mapper.__init__(self, 100, 50)
+        Mapper.__init__(self, 200, 160)
+
+        self.point       = None
+        self.border_with = 0
+
+        self.connect("expose_event", self.expose_event)
+
+    def update_items(self, point):
+        self.point = point
+        self.calc_move()
+        self.queue_draw()
+        try:
+            pass
+        except:
+            pass
+
+    def expose_event(self, widget, event):
+        self.width, self.height = self.allocation[2], self.allocation[3]
+
+        self.draw_rectangle(self.border_with,
+                            self.border_with,
+                            self.width - 2*self.border_with,
+                            self.height - 2*self.border_with,
+                            self.style.fg[self.state],
+                            5.0)
+
+        self.center = { "x" : self.width / 2,
+                        "y" : self.height / 2 }
+
+        self.vscreen = { "x" : self.center["x"] - 40,
+                         "y" : self.center["y"] - 30,
+                         "width"  : 80,
+                         "height" : 60}
+
+        self.draw_rectangle( self.vscreen["x"], self.vscreen["y"],
+                             self.vscreen["width"], self.vscreen["height"],
+                             self.style.fg[self.state], 5.0)
+
+        if hasattr(self.point, "abs_diff"):
+            self.vpoint = { "x" : self.center["x"] - self.point.abs_diff.x,
+                            "y" : self.center["y"] + self.point.abs_diff.y }
+
+            self.draw_point( self.vpoint["x"], self.vpoint["y"], 2)
+
+    def calc_move(self):
+        if not hasattr(self, "vpoint"):
+            return False
+
+        x, y = mouse.position()
+
+        par = ["width", "height"]
+
+        new_x, new_y = [ (float(poss)/self.vscreen[par[i]])*env.screen[par[i]]
+                          for i,poss in enumerate([ (self.vscreen["width"]/2) - ( self.center["x"] - self.vpoint["x"]),
+                                                    (self.vscreen["height"]/2) - ( self.center["y"] - self.vpoint["y"] ) ])]
+
+        mouse.move( new_x, new_y)
 
     def prefferences(self):
         """
@@ -53,3 +109,4 @@
         Arguments:
         - self: the main object pointer.
         """
+        pass

Modified: branches/ng/src/mousetrap/ui/widgets.py
==============================================================================
--- branches/ng/src/mousetrap/ui/widgets.py	(original)
+++ branches/ng/src/mousetrap/ui/widgets.py	Sun Feb 22 18:24:52 2009
@@ -35,6 +35,7 @@
 import gtk
 import cairo
 from gtk import gdk
+from math import pi
 
 TEXT = 'A GtkWidget implemented in PyGTK'
 BORDER_WIDTH = 0
@@ -124,19 +125,34 @@
         if self.flags() & gtk.REALIZED:
             self.window.move_resize(*allocation)
 
-    def do_expose_event(self, event):
+    def expose_event(self, widget, event):
         # The do_expose_event is called when the widget is asked to draw itself
         # Remember that this will be called a lot of times, so it's usually
         # a good idea to write this code as optimized as it can be, don't
         # Create any resources in here.
 
-        x, y, w, h = self.allocation
-        self.draw_rectangle(BORDER_WIDTH,
-                            BORDER_WIDTH,
-                            w - 2*BORDER_WIDTH,
-                            h - 2*BORDER_WIDTH,
-                            self.style.fg[self.state],
-                            5.0)
+#         x, y, self.width, self.height = self.allocation
+#         self.draw_rectangle(BORDER_WIDTH,
+#                             BORDER_WIDTH,
+#                             self.width - 2*BORDER_WIDTH,
+#                             self.height - 2*BORDER_WIDTH,
+#                             self.style.fg[self.state],
+#                             5.0)
+#
+#         w = self.width / 2
+#         h = self.height / 2
+#
+#         self.draw_rectangle(60, 50, 80, 60, self.style.fg[self.state], 5.0)
+#         self.draw_point( w + point.abs_diff.x, h - point.abs_diff.y, 2)
+
+#         x, y, w, h = self.allocation
+#         self.draw_rectangle(200,
+#                             160,
+#                             100,
+#                             ,
+#                             self.style.fg[self.state],
+#                             5.0)
+        return True
 
         # And draw the text in the middle of the allocated space
 #         fontw, fonth = self._layout.get_pixel_size()
@@ -154,6 +170,26 @@
         cr.set_line_join(cairo.LINE_JOIN_ROUND)
         cr.stroke()
 
+    def draw_point(self, X, Y, size, color = 'green'):
+        """
+        Draws the point
+
+        Arguments:
+        - self: The main object pointer.
+        - X: The X possition.
+        - Y: The Y possition
+        - size: The point diameter.
+        - color: A RGB color tuple. E.g (255,255,255)
+        """
+
+        cr = self.window.cairo_create()
+        cr.set_source_rgb(0.7, 0.8, 0.1)
+        cr.arc(X, Y, size, 0, 2 * pi)
+        cr.fill_preserve()
+        cr.stroke()
+
+        return True
+
     def on_expose_show(self, area):
         """
         Here are registered the areas that should be shown by the expose event.

Modified: branches/ng/src/ocvfw/dev/camera.py
==============================================================================
--- branches/ng/src/ocvfw/dev/camera.py	(original)
+++ branches/ng/src/ocvfw/dev/camera.py	Sun Feb 22 18:24:52 2009
@@ -39,7 +39,7 @@
 
 class __Camera(ocv):
 
-    def init(self, idx=0, fps=100, async=False):
+    def init(self, idx=0 ):
         """
         Initialize the camera.
 
@@ -51,23 +51,6 @@
         """
 
         self.start_camera(idx)
-        self.set_async(fps, async)
-
-    def set_async(self, fps=100, async=False):
-        """
-        Sets/Unsets the asynchronous property.
-
-        Arguments:
-        - self: The main object pointer
-        - fps: The frames per second to be queried.
-        - async: Enable/Disable asynchronous image querying. Default: False
-        """
-
-        self.fps   = fps
-        self.async = async
-
-        if self.async:
-            gobject.timeout_add(self.fps, self.query_image)
 
 
 Camera = __Camera()
@@ -75,7 +58,7 @@
 
 class Capture(object):
 
-    def __init__(self, image=None):
+    def __init__(self, image=None, fps=100, async=False):
 
         self.__lock        = False
         self.__flip        = {}
@@ -98,6 +81,23 @@
         self.last_update   = 0
         self.last_duration = 0
 
+        self.set_async(fps, async)
+
+    def set_async(self, fps=100, async=False):
+        """
+        Sets/Unsets the asynchronous property.
+
+        Arguments:
+        - self: The main object pointer
+        - fps: The frames per second to be queried.
+        - async: Enable/Disable asynchronous image querying. Default: False
+        """
+
+        self.fps   = fps
+        self.async = async
+
+        if self.async:
+            gobject.timeout_add(self.fps, self.sync)
 
     def sync(self):
         """
@@ -107,8 +107,7 @@
         - self: The main object pointer.
         """
 
-        if not self.__camera.async:
-            self.__camera.query_image()
+        self.__camera.query_image()
 
         if not self.__image:
             self.__images_cn   = { 1 : cv.cvCreateImage ( self.__camera.imgSize, 8, 1 ),
@@ -118,15 +117,6 @@
         self.__color       = "bgr"
         self.__image_orig  = self.__image = self.__camera.img
 
-    # property
-    def image(self):
-        """
-        Returns the image ready to use
-
-        Arguments:
-        - self: The main object pointer.
-        """
-
         if self.__color != self.__color_set:
             self.__image = self.color("rgb")
 
@@ -139,6 +129,18 @@
         self.show_rectangles(self.rectangles())
 
         self.__image = self.resize(200, 160)
+
+        return self.async
+
+    # property
+    def image(self):
+        """
+        Returns the image ready to use
+
+        Arguments:
+        - self: The main object pointer.
+        """
+
         return self.__image
 
     def resize(self, width, height):
@@ -398,6 +400,7 @@
         self.__ocv = None
         self.last  = None
         self.diff  = None
+        self.orig  = cv.cvPoint( self.x, self.y )
 
     def set_opencv(self, opencv):
         """
@@ -417,8 +420,11 @@
             self.last = self.__ocv
 
             # Update the diff attr
-            self.diff = cv.cvPoint( self.last.x - self.x,
-                                    self.last.y - self.y )
+            self.rel_diff = cv.cvPoint( self.last.x - self.x,
+                                        self.last.y - self.y )
+
+            self.abs_diff = cv.cvPoint( self.x - self.orig.x,
+                                        self.y - self.orig.y )
 
         self.__ocv = opencv
 

Modified: branches/ng/src/ocvfw/idm/forehead.py
==============================================================================
--- branches/ng/src/ocvfw/idm/forehead.py	(original)
+++ branches/ng/src/ocvfw/idm/forehead.py	Sun Feb 22 18:24:52 2009
@@ -33,7 +33,7 @@
 class Module(object):
 
     def __init__(self, controller):
-        Camera.init(idx=0, async=False)
+        Camera.init(idx=0)
 
         self.img          = None
         self.ctr          = controller
@@ -52,6 +52,7 @@
         self.startMove    = None
         self.haar_cds     = { 'Face'  :  "../ocvfw/haars/haarcascade_frontalface_alt.xml",
                               'Eyes'  :  "../ocvfw/haars/frontalEyes35x16.xml",
+                              #'Eyes'  :  "../ocvfw/haars/haarcascade_eye_tree_eyeglasses.xml",
                               'Mouth' :  "../ocvfw/haars/Mouth.xml"}
 
         ##############################
@@ -66,7 +67,7 @@
         self.isMoving       = False
 
     def set_capture(self):
-        self.cap = Capture()
+        self.cap = Capture(async=True)
         self.cap.change(color="rgb")
 
     def calc_motion(self):
@@ -74,8 +75,14 @@
             self.get_forehead()
 
     def get_image(self):
-        self.cap.sync()
+        """
+        Sets the forehead point if needed and returns the formated image.
+
+        Arguments:
+        - self: The main object pointer
 
+        returns self.cap.image()
+        """
         if not hasattr(self.cap, "forehead"):
             self.get_forehead()
 
@@ -84,8 +91,12 @@
     def get_pointer(self):
         """
         Returns the new MousePosition
+
+        Arguments:
+        - self: The main object pointer
         """
-        return self.cap.forehead
+        if hasattr(self.cap, "forehead"):
+            return self.cap.forehead
 
     def get_forehead(self):
         eyes = False
@@ -99,7 +110,7 @@
             endF     = face[areas.index(max(areas))][1]
 
             # Shows the face rectangle
-            #self.cap.show_rectangles([Graphic("rect", "Face", ( startF.x, startF.y ), (endF.x, endF.y), parent=self.cap)])
+            #self.cap.add( Graphic("rect", "Face", ( startF.x, startF.y ), (endF.x, endF.y), parent=self.cap) )
 
             eyes = self.cap.get_area( self.haar_cds['Eyes'], {"start" : startF.x,
                                                          "end" : startF.y,
@@ -112,7 +123,7 @@
             point1, point2   = eyes[areas.index(max(areas))][0], eyes[areas.index(max(areas))][1]
 
             # Shows the eyes rectangle
-            #self.cap.show_rectangles([Graphic("rect", "Face", ( point1.x, point1.y ), (point2.x, point2.y), parent=self.cap)])
+            #self.cap.add(Graphic("rect", "Face", ( point1.x, point1.y ), (point2.x, point2.y), parent=self.cap))
 
             X, Y = ( (point1.x + point2.x) / 2 ), ( point1.y + ( (point1.y + point2.y) / 2 ) ) / 2
             self.cap.add( Point("point", "forehead", ( X, Y ), parent=self.cap, follow=True) )



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]