diff --git a/doc/sources/guide/android.rst b/doc/sources/guide/android.rst index 8caae5576..7f832ed4c 100644 --- a/doc/sources/guide/android.rst +++ b/doc/sources/guide/android.rst @@ -162,4 +162,5 @@ Tablets - Samsung Galaxy Tab - Motorola Xoom +- Asus EeePad Transformer diff --git a/doc/sources/guide/environment.rst b/doc/sources/guide/environment.rst new file mode 100644 index 000000000..45fc7873d --- /dev/null +++ b/doc/sources/guide/environment.rst @@ -0,0 +1,87 @@ +.. _environment: + +Controling the environment +========================== + +Many environment variables are available to control the initialization and +behavior of Kivy. + +For example, for restricting text rendering to cairo implementation:: + + $ KIVY_TEXT=cairo python main.py + +Environment variable can be set before importing kivy:: + + import os + os.environ['KIVY_TEXT'] = 'cairo' + import kivy + +Configuration +------------- + +KIVY_USE_DEFAULTCONFIG + If this name is found in environ, Kivy will not read the user config file. + +Path control +------------ + +.. versionadded:: 1.0.7 + +You can control where is located default directory of modules, extensions, and +kivy datas. + +KIVY_DATA_DIR + Location of the Kivy data, default to `/data` + +KIVY_EXTS_DIR + Location of the Kivy extensions, default to `/extensions` + +KIVY_MODULES_DIR + Location of the Kivy modules, default to `/modules` + +Restrict core to specific implementation +---------------------------------------- + +:mod:`kivy.core` try to select the best implementation available for your +platform. For testing or custom installation, you might want to restrict the +selector to a specific implementation. + +KIVY_WINDOW + Implementation to use for creating the Window + + Values: pygame + +KIVY_TEXT + Implementation to use for rendering text + + Values: pil, cairo, pygame + +KIVY_VIDEO + Implementation to use for rendering video + + Values: gstreamer, pyglet + +KIVY_AUDIO + Implementation to use for playing audio + + Values: gstreamer, pygame + +KIVY_IMAGE + Implementation to use for reading image + + Values: pil, pygame + +KIVY_CAMERA + Implementation to use for reading camera + + Values: gstreamer, opencv, videocapture + +KIVY_SPELLING + Implementation to use for spelling + + Values: enchant, osxappkit + +KIVY_CLIPBOARD + Implementation to use for clipboard management + + Values: pygame, dummy diff --git a/kivy/__init__.py b/kivy/__init__.py index 32719b4c1..ef8a9fdf0 100644 --- a/kivy/__init__.py +++ b/kivy/__init__.py @@ -23,9 +23,9 @@ See http://kivy.org for more information. __all__ = ( 'require', 'kivy_configure', 'kivy_register_post_configuration', - 'kivy_options', 'kivy_base_dir', 'kivy_libs_dir', + 'kivy_options', 'kivy_base_dir', 'kivy_modules_dir', 'kivy_data_dir', 'kivy_shader_dir', - 'kivy_providers_dir', 'kivy_icons_dir', 'kivy_home_dir', + 'kivy_icons_dir', 'kivy_home_dir', 'kivy_config_fn', 'kivy_usermodules_dir', ) @@ -194,7 +194,6 @@ kivy_options = { 'audio': ('pygame', 'gstreamer', ), 'image': ('osxcoreimage', 'pil', 'pygame'), 'camera': ('opencv', 'gstreamer', 'videocapture'), - 'svg': ('squirtle', ), 'spelling': ('enchant', 'osxappkit', ), 'clipboard': ('pygame', 'dummy'), } @@ -215,18 +214,17 @@ for option in kivy_options: # Extract all needed path in kivy #: Kivy directory kivy_base_dir = dirname(sys.modules[__name__].__file__) -#: Kivy external libraries directory -kivy_libs_dir = join(kivy_base_dir, 'lib') #: Kivy modules directory -kivy_modules_dir = join(kivy_base_dir, 'modules') +kivy_modules_dir = environ.get('KIVY_MODULES_DIR', + join(kivy_base_dir, 'modules')) #: Kivy extension directory -kivy_exts_dir = join(kivy_base_dir, 'extensions') +kivy_exts_dir = environ.get('KIVY_EXTS_DIR', + join(kivy_base_dir, 'extensions')) #: Kivy data directory -kivy_data_dir = join(kivy_base_dir, 'data') +kivy_data_dir = environ.get('KIVY_DATA_DIR', + join(kivy_base_dir, 'data')) #: Kivy glsl shader directory kivy_shader_dir = join(kivy_data_dir, 'glsl') -#: Kivy input provider directory -kivy_providers_dir = join(kivy_base_dir, 'input', 'providers') #: Kivy icons config path (don't remove the last '') kivy_icons_dir = join(kivy_data_dir, 'icons', '') #: Kivy user-home storage directory @@ -236,9 +234,6 @@ kivy_config_fn = None #: Kivy user modules directory kivy_usermodules_dir = None -# Add libs to pythonpath -sys.path = [kivy_libs_dir] + sys.path - # Don't go further if we generate documentation if basename(sys.argv[0]) in ('sphinx-build', 'autobuild.py'): environ['KIVY_DOC'] = '1' @@ -285,7 +280,7 @@ if not 'KIVY_DOC_INCLUDE' in environ: sys.argv = sys.argv[:1] try: - opts, args = getopt(sys_argv[1:], 'hp:fkawFem:snr:dc:', + opts, args = getopt(sys_argv[1:], 'hp:fkawFem:sr:dc:', ['help', 'fullscreen', 'windowed', 'fps', 'event', 'module=', 'save', 'fake-fullscreen', 'auto-fullscreen', 'display=', 'size=', 'rotate=', 'config=', 'debug']) @@ -348,8 +343,6 @@ if not 'KIVY_DOC_INCLUDE' in environ: need_save = True elif opt in ('-r', '--rotation'): Config.set('graphics', 'rotation', arg) - elif opt in ('-n', ): - kivy_options['shadow_window'] = False elif opt in ('-d', '--debug'): level = LOG_LEVELS.get('debug') Logger.setLevel(level=level) diff --git a/kivy/clock.py b/kivy/clock.py index b2709c6b7..2fd346e05 100644 --- a/kivy/clock.py +++ b/kivy/clock.py @@ -339,7 +339,7 @@ class ClockBase(object): # call that function to release all the direct reference to any callback # and replace it with a weakref events = self._events - for cid in events: + for cid in events.keys()[:]: [x.release() for x in events[cid] if x.callback is not None] def _remove_empty(self): diff --git a/kivy/core/image/img_osxcoreimage.py b/kivy/core/image/img_osxcoreimage.py index ffd24908c..7584b6b44 100644 --- a/kivy/core/image/img_osxcoreimage.py +++ b/kivy/core/image/img_osxcoreimage.py @@ -20,6 +20,7 @@ class ImageLoaderOSXCoreImage(ImageLoaderBase): def extensions(): '''Return accepted extension for this loader''' # See http://www.pythonware.com/library/pil/handbook/index.htm + # XXX return ('bmp', 'bufr', 'cur', 'dcx', 'fits', 'fl', 'fpx', 'gbr', 'gd', 'gif', 'grib', 'hdf5', 'ico', 'im', 'imt', 'iptc', 'jpeg', 'jpg', 'mcidas', 'mic', 'mpeg', 'msp', 'pcd', @@ -27,7 +28,7 @@ class ImageLoaderOSXCoreImage(ImageLoaderBase): 'tga', 'tiff', 'wal', 'wmf', 'xbm', 'xpm', 'xv') def load(self, filename): - ret = osxcoreimage.load_raw_image_data(filename) + ret = osxcoreimage.load_image_data(filename) if ret is None: Logger.warning('Image: Unable to load image <%s>' % filename) raise Exception('Unable to load image') diff --git a/kivy/core/image/img_pil.py b/kivy/core/image/img_pil.py index 657fba532..9bd529e12 100644 --- a/kivy/core/image/img_pil.py +++ b/kivy/core/image/img_pil.py @@ -7,7 +7,10 @@ __all__ = ('ImageLoaderPIL', ) try: from PIL import Image except: - raise + try: + import Image + except: + raise from kivy.logger import Logger from . import ImageLoaderBase, ImageData, ImageLoader diff --git a/kivy/core/image/osxcoreimage.pyx b/kivy/core/image/osxcoreimage.pyx index 278fd26ba..42924c16b 100644 --- a/kivy/core/image/osxcoreimage.pyx +++ b/kivy/core/image/osxcoreimage.pyx @@ -1,25 +1,94 @@ +from array import array from libcpp cimport bool ctypedef unsigned long size_t ctypedef signed long CFIndex + +cdef extern from "stdlib.h": + void* calloc(size_t, size_t) + + cdef extern from "Python.h": object PyString_FromStringAndSize(char *s, Py_ssize_t len) +#cdef extern from "CoreGraphics/CGDataProvider.h": +cdef extern from "ApplicationServices/ApplicationServices.h": + ctypedef void *CFDataRef + # XXX + # char or int? + unsigned char * CFDataGetBytePtr(CFDataRef) + ctypedef void *CGDataProviderRef + CFDataRef CGDataProviderCopyData(CGDataProviderRef) + ctypedef void *CGDataProviderRef + CFDataRef CGDataProviderCopyData(CGDataProviderRef) + ctypedef void *CGImageRef + CGDataProviderRef CGImageGetDataProvider(CGImageRef) + # guessing these, because of no wifi: + size_t CGImageGetWidth(CGImageRef) + size_t CGImageGetHeight(CGImageRef) + size_t CGImageGetBitsPerPixel(CGImageRef) + int CGImageGetAlphaInfo(CGImageRef) + int kCGImageAlphaNone + int kCGImageAlphaNoneSkipLast + int kCGImageAlphaNoneSkipFirst + int kCGImageAlphaFirst + int kCGImageAlphaLast + int kCGImageAlphaPremultipliedLast + int kCGImageAlphaPremultipliedFirst + int kCGBitmapByteOrder32Host + + ctypedef void *CGColorSpaceRef + CGColorSpaceRef CGImageGetColorSpace(CGImageRef image) + + + CGColorSpaceRef CGColorSpaceCreateDeviceRGB() + + ctypedef void *CGContextRef + + void CGContextTranslateCTM(CGContextRef, float, float) + void CGContextScaleCTM (CGContextRef, float, float) + + ctypedef struct CGPoint: + float x + float y + + ctypedef struct CGSize: + float width + float height + + ctypedef struct CGRect: + CGPoint origin + CGSize size + + CGRect CGRectMake(float, float, float, float) + + CGContextRef CGBitmapContextCreate( + void *data, + size_t width, + size_t height, + size_t bitsPerComponent, + size_t bytesPerRow, + CGColorSpaceRef colorspace, + unsigned int bitmapInfo + ) + + #void CGContextSetBlendMode (CGContextRef, int) + void CGContextDrawImage(CGContextRef, CGRect, CGImageRef) + + int kCGBlendModeCopy + void CGContextSetBlendMode(CGContextRef, int) + + cdef extern from "CoreFoundation/CFBase.h": ctypedef void *CFAllocatorRef -cdef extern from "CoreFoundation/CFData.h": - ctypedef void *CFDataRef - # XXX - # char or int? - unsigned char * CFDataGetBytePtr(CFDataRef) +#cdef extern from "CoreFoundation/CFData.h": -cdef extern from "CoreGraphics/CGDataProvider.h": - ctypedef void *CGDataProviderRef - CFDataRef CGDataProviderCopyData(CGDataProviderRef) +#cdef extern from "CoreGraphics/CGDataProvider.h": +#cdef extern from "QuartzCore/QuartzCore.h": cdef extern from "CoreFoundation/CFURL.h": @@ -53,7 +122,8 @@ cdef extern from "CoreGraphics/CGImage.h": int kCGImageAlphaNone -cdef extern from "ImageIO/CGImageSource.h": +#cdef extern from "ImageIO/CGImageSource.h": +cdef extern from "QuartzCore/QuartzCore.h": ctypedef void *CGImageSourceRef CGImageSourceRef CGImageSourceCreateWithURL(CFURLRef, CFDictionaryRef) @@ -61,57 +131,48 @@ cdef extern from "ImageIO/CGImageSource.h": size_t, CFDictionaryRef) -def load_raw_image_data(bytes _url): + +def load_image_data(bytes _url): cdef CFURLRef url url = CFURLCreateFromFileSystemRepresentation(NULL, _url, len(_url), 0) - cdef CGImageSourceRef myImageSourceRef - # or maybe: UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath]; - # see iphone3d book - myImageSourceRef = CGImageSourceCreateWithURL(url, NULL) - if myImageSourceRef == NULL: - print 'myImageSourceRef is NULL' - return None + cdef CGImageSourceRef myImageSourceRef = CGImageSourceCreateWithURL(url, NULL) + cdef CGImageRef myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL) - cdef CGImageRef myImageRef - myImageRef = CGImageSourceCreateImageAtIndex (myImageSourceRef, 0, NULL) - if myImageRef == NULL: - print 'myImageRef is NULL' - return None + cdef size_t width = CGImageGetWidth(myImageRef) + cdef size_t height = CGImageGetHeight(myImageRef) + cdef CGRect rect = CGRectMake(0, 0, width, height) + cdef void * myData = calloc(width * 4, height) + cdef CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB() + cdef CGContextRef myBitmapContext = CGBitmapContextCreate(myData, + width, height, 8, + width*4, space, + # endianness: kCGBitmapByteOrder32Little = (2 << 12) + #(2 << 12) | kCGImageAlphaPremultipliedLast) + kCGBitmapByteOrder32Host + | + # XXX first or last? in + # the docs they use + # first + kCGImageAlphaNoneSkipFirst) - cdef int width = CGImageGetWidth(myImageRef) - cdef int height = CGImageGetHeight(myImageRef) - cdef int hasAlpha = CGImageGetAlphaInfo(myImageRef) != kCGImageAlphaNone + # This is necessary as the image would be vertically flipped otherwise + CGContextTranslateCTM(myBitmapContext, 0, height) + CGContextScaleCTM(myBitmapContext, 1, -1) - # correctly detect the image type !!! - imgtype = 'rgb' - typesize = 3 - if hasAlpha > 0: - imgtype = 'rgba' - typesize = 4 + CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy) + CGContextDrawImage(myBitmapContext, rect, myImageRef) + #CGContextRelease(myBitmapContext) - cdef CFDataRef data - data = CGDataProviderCopyData(CGImageGetDataProvider(myImageRef)) + r_data = PyString_FromStringAndSize( myData, width * height * 4) - r_data = PyString_FromStringAndSize(CFDataGetBytePtr(data), - width * height * typesize) + # XXX + # kivy doesn't like to process 'bgra' data. we swap manually to 'rgba'. + # would be better to fix this in texture.pyx + a = array('b', r_data) + a[0::4], a[2::4] = a[2::4], a[0::4] + r_data = a.tostring() + imgtype = 'rgba' - # XXX clean image object - - print 'Image:', _url, width, height, imgtype return (width, height, imgtype, r_data) -# -# bool hasAlpha = CGImageGetAlphaInfo(cgImage) != kCGImageAlphaNone; CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); switch (CGColorSpaceGetModel(colorSpace)) { -# case kCGColorSpaceModelMonochrome: description.Format = -# hasAlpha ? TextureFormatGrayAlpha : TextureFormatGray; break; -# case kCGColorSpaceModelRGB: description.Format = -# Texture Formats and Types -# | 189 -# } -# hasAlpha ? TextureFormatRgba : TextureFormatRgb; break; -# default: assert(!"Unsupported color space."); break; -# } description.BitsPerComponent = CGImageGetBitsPerComponent(cgImage); -# return description; -# -# diff --git a/kivy/core/window/window_sdl.py b/kivy/core/window/window_sdl.py index a023ab53b..420083e18 100644 --- a/kivy/core/window/window_sdl.py +++ b/kivy/core/window/window_sdl.py @@ -138,6 +138,7 @@ class WindowSDL(WindowBase): if event is None: continue + print 'sdl received', event action, args = event[0], event[1:] if action == 'quit': EventLoop.quit = True diff --git a/kivy/data/images/testpattern.png b/kivy/data/images/testpattern.png new file mode 100644 index 000000000..b0d72db64 Binary files /dev/null and b/kivy/data/images/testpattern.png differ diff --git a/kivy/event.pyx b/kivy/event.pyx index 072a15c52..d6e0ba3f8 100644 --- a/kivy/event.pyx +++ b/kivy/event.pyx @@ -47,7 +47,7 @@ cdef class EventDispatcher(object): def on_swipe_callback(*largs): print 'my swipe is called', largs w = MyWidget() - w.dispatch_event('on_swipe') + w.dispatch('on_swipe') ''' if not event_type.startswith('on_'): diff --git a/kivy/ext/__init__.py b/kivy/ext/__init__.py index 3a38d160b..4d7b827ab 100644 --- a/kivy/ext/__init__.py +++ b/kivy/ext/__init__.py @@ -52,7 +52,7 @@ import sys import imp from glob import glob from os import listdir, mkdir, sep, environ -from os.path import join, isdir, exists +from os.path import join, isdir, exists, dirname from zipfile import ZipFile from shutil import move @@ -67,8 +67,8 @@ if not 'KIVY_DOC' in environ: NEED_UNZIP = True -# XXX platform check? def load(extname, version): + # XXX platform check? '''Use this function to tell Kivy to load a specific version of the given Extension. This is different from kivy's require() in that it will always use the exact same major version you specify, even if a newer (major) @@ -203,7 +203,10 @@ def unzip_extensions(): for epath in EXTENSION_PATHS: if not isdir(epath): - mkdir(epath) + try: + mkdir(epath) + except OSError: + continue files = [] else: files = listdir(epath) @@ -235,48 +238,32 @@ def unzip_extensions(): "extracted manually, just moving the zip.") already_unzipped = True - members = zipf.namelist() - - def get_root_member(zipf): - root = None - multiple = False - for m1 in members: - for m2 in members: - if not m2.startswith(m1): - break - else: - if root != None: - multiple = True - root = m1 - if not root or multiple: - # There either is no root or there is more than one root. - # We require only one root, so the extension is malformed. - Logger.warn("Malformed extension '%s'! Skipping it." % extname) - return - # return root name without the trailing slash - return root[:-1] - - root = get_root_member(zipf) - if not root: - # Skip this extension as we told the user - continue + # Filter the namelist of zipfile to take only the members that start + # with the extension name (MyExt/...) + members = [x for x in zipf.namelist() \ + if x.startswith(extname + '/')] if not already_unzipped: # Unzip the extension try: + cache_directories = [] mkdir(join(epath, extdir)) - # I know that there is zipf.extract() and zipf.extractall(), but on - # OSX, Python 2.6 is the default and in that version, both methods - # have a bug. Fixed in 2.7 only. So use this workaround until apple - # upgrades its python. See http://bugs.python.org/issue4710 + # I know that there is zipf.extract() and zipf.extractall(), + # but on OSX, Python 2.6 is the default and in that version, + # both methods have a bug. Fixed in 2.7 only. So use this + # workaround until apple upgrades its python. See + # http://bugs.python.org/issue4710 for member in members: - # In zipfiles, folders always end with '/' regardless of the OS + # In zipfiles, folders always end with '/' regardless + # of the OS mempath = join(epath, extdir, member) - if member.endswith('/') and not exists(mempath): - mkdir(join(epath, extdir, member[:-1])) - else: - with open(join(epath, extdir, member), 'wb') as memberfd: - memberfd.write(zipf.read(member)) + directory = dirname(mempath) + if not directory in cache_directories: + cache_directories.append(directory) + if not exists(directory): + mkdir(join(epath, extdir, directory)) + with open(join(epath, extdir, member), 'wb') as fd: + fd.write(zipf.read(member)) except Exception, e: # Catch any error, e.g. non-writable directory, etc. Logger.error("Failed installing extension " + \ diff --git a/kivy/graphics/context_instructions.pyx b/kivy/graphics/context_instructions.pyx index 9dda9b72e..8c48fd374 100644 --- a/kivy/graphics/context_instructions.pyx +++ b/kivy/graphics/context_instructions.pyx @@ -172,51 +172,71 @@ cdef class Color(ContextInstruction): self.set_state('color', [1.0, 1.0, 1.0, 1.0]) property rgba: + '''RGBA color, list of 4 values in 0-1 range + ''' def __get__(self): return self.context_state['color'] def __set__(self, rgba): self.set_state('color', map(float,rgba)) property rgb: + '''RGB color, list of 3 values in 0-1 range, alpha will be 1. + ''' def __get__(self): return self.rgba[:-1] def __set__(self, rgb): self.rgba = (rgb[0], rgb[1], rgb[2], 1.0) property r: + '''Red component, between 0-1 + ''' def __get__(self): return self.rgba[0] def __set__(self, r): self.rgba = [r, self.g, self.b, self.a] property g: + '''Green component, between 0-1 + ''' def __get__(self): return self.rgba[1] def __set__(self, g): self.rgba = [self.r, g, self.b, self.a] property b: + '''Blue component, between 0-1 + ''' def __get__(self): return self.rgba[2] def __set__(self, b): self.rgba = [self.r, self.g, b, self.a] property a: + '''Alpha component, between 0-1 + ''' def __get__(self): return self.rgba[3] def __set__(self, a): self.rgba = [self.r, self.g, self.b, a] property hsv: + '''HSV color, list of 3 values in 0-1 range, alpha will be 1. + ''' def __get__(self): - return rgb_to_hsv(self.r, self.h, self.b) + return rgb_to_hsv(self.r, self.g, self.b) def __set__(self, x): self.rgb = hsv_to_rgb(x[0], x[1], x[2]) property h: + '''Hue component, between 0-1 + ''' def __get__(self): return self.hsv[0] def __set__(self, x): self.hsv = [x, self.s, self.v] property s: + '''Saturation component, between 0-1 + ''' def __get__(self): return self.hsv[1] def __set__(self, x): self.hsv = [self.h, x, self.v] property v: + '''Value component, between 0-1 + ''' def __get__(self): return self.hsv[2] def __set__(self, x): diff --git a/kivy/graphics/texture.pyx b/kivy/graphics/texture.pyx index ce59058f0..c1b07c4dc 100644 --- a/kivy/graphics/texture.pyx +++ b/kivy/graphics/texture.pyx @@ -594,6 +594,7 @@ cdef class Texture: with nogil: glBindTexture(target, self._id) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glTexSubImage2D(target, 0, x, y, w, h, glfmt, glbufferfmt, cdata) glFlush() diff --git a/kivy/graphics/vertex_instructions.pyx b/kivy/graphics/vertex_instructions.pyx index 0407e7acb..4bfaf57f6 100644 --- a/kivy/graphics/vertex_instructions.pyx +++ b/kivy/graphics/vertex_instructions.pyx @@ -512,20 +512,32 @@ cdef class BorderImage(Rectangle): cdef class Ellipse(Rectangle): '''A 2D ellipse. + .. versionadded:: 1.0.7 added angle_start + angle_end + :Parameters: `segments`: int, default to 180 Define how much segment is needed for drawing the ellipse. The drawing will be smoother if you have lot of segment. + `angle_start`: int default to 0 + Specifies the starting angle, in degrees, of the disk portion + `angle_end`: int default to 360 + Specifies the ending angle, in degrees, of the disk portion ''' cdef int _segments + cdef float _angle_start + cdef float _angle_end def __init__(self, *args, **kwargs): Rectangle.__init__(self, **kwargs) + self.batch.set_mode('triangle_fan') self._segments = kwargs.get('segments', 180) + self._angle_start = kwargs.get('angle_start', 0) + self._angle_end = kwargs.get('angle_end', 360) cdef void build(self): cdef list tc = self.tex_coords - cdef int i + cdef int i, angle_dir + cdef float angle_start, angle_end, angle_range cdef float x, y, angle, rx, ry, ttx, tty, tx, ty, tw, th cdef vertex_t *vertices = NULL cdef int *indices = NULL @@ -539,40 +551,53 @@ cdef class Ellipse(Rectangle): rx = 0.5 * self.w ry = 0.5 * self.h - vertices = malloc((count + 1) * sizeof(vertex_t)) + vertices = malloc((count + 2) * sizeof(vertex_t)) if vertices == NULL: raise MemoryError('vertices') - indices = malloc(3 * count * sizeof(int)) + indices = malloc((count + 2) * sizeof(int)) if indices == NULL: free(vertices) raise MemoryError('indices') - for i in xrange(count): - # rad = deg * (pi / 180), where pi/180 = 0.0174... - angle = i * 360.0/self._segments *0.017453292519943295 - x = (self.x+rx)+ (rx*cos(angle)) - y = (self.y+ry)+ (ry*sin(angle)) + # calculate the start/end angle in radians, and adapt the range + if self.angle_end > self.angle_start: + angle_dir = 1 + else: + angle_dir = -1 + # rad = deg * (pi / 180), where pi/180 = 0.0174... + angle_start = (self._angle_start % 361) * 0.017453292519943295 + angle_end = (self._angle_end % 361) * 0.017453292519943295 + if angle_end > angle_start: + angle_range = angle_end - angle_start + else: + angle_range = angle_start - angle_end + angle_range = angle_range / self._segments + + # add start vertice in the middle + x = self.x + rx + y = self.y + ry + ttx = ((x - self.x) / self.w) * tw + tx + tty = ((y - self.y) / self.h) * th + ty + vertices[0].x = self.x + rx + vertices[0].y = self.y + ry + vertices[0].s0 = ttx + vertices[0].t0 = tty + indices[0] = 0 + + for i in xrange(1, count + 2): + angle = angle_start + (angle_dir * (i - 1) * angle_range) + x = (self.x+rx)+ (rx*sin(angle)) + y = (self.y+ry)+ (ry*cos(angle)) ttx = ((x-self.x)/self.w)*tw + tx tty = ((y-self.y)/self.h)*th + ty vertices[i].x = x vertices[i].y = y vertices[i].s0 = ttx vertices[i].t0 = tty - indices[i * 3] = i - indices[i * 3 + 1] = count - indices[i * 3 + 2] = (i + 1) % count + indices[i] = i - #add last verte in the middle - x, y = self.x+rx, self.y+ry - ttx = ((x-self.x)/self.w)*tw + tx - tty = ((y-self.y)/self.h)*th + ty - vertices[count].x = x - vertices[count].y = y - vertices[count].s0 = ttx - vertices[count].t0 = tty - - self.batch.set_data(vertices, count + 1, indices, count * 3) + self.batch.set_data(vertices, count + 2, indices, count + 2) free(vertices) free(indices) @@ -585,3 +610,22 @@ cdef class Ellipse(Rectangle): def __set__(self, value): self._segments = value self.flag_update() + + property angle_start: + '''Angle start of the ellipse in degrees, default to 0 + ''' + def __get__(self): + return self._angle_start + def __set__(self, value): + self._angle_start = value + self.flag_update() + + property angle_end: + '''Angle end of the ellipse in degrees, default to 360 + ''' + def __get__(self): + return self._angle_end + def __set__(self, value): + self._angle_end = value + self.flag_update() + diff --git a/kivy/input/providers/tuio.py b/kivy/input/providers/tuio.py index c069e5fbb..eec287c8b 100644 --- a/kivy/input/providers/tuio.py +++ b/kivy/input/providers/tuio.py @@ -11,7 +11,7 @@ refer to http://tuio.org -- The specification should be of special interest. __all__ = ('TuioMotionEventProvider', 'Tuio2dCurMotionEvent', 'Tuio2dObjMotionEvent') -import osc +from kivy.lib import osc from collections import deque from kivy.input.provider import MotionEventProvider from kivy.input.factory import MotionEventFactory diff --git a/kivy/lib/__init__.py b/kivy/lib/__init__.py index 3053b8aaf..0063cfe87 100644 --- a/kivy/lib/__init__.py +++ b/kivy/lib/__init__.py @@ -4,8 +4,7 @@ External libraries Kivy come with other python/c libraries : - - squirtle (modified / optimized) - oscAPI (modified / optimized) - - transformation (C) + - mtdev ''' diff --git a/kivy/properties.pyx b/kivy/properties.pyx index cbd1ec300..da20f6ebe 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -29,7 +29,7 @@ Kivy's property classes support: Better Memory Management The same instance of a property is shared across multiple widget - instances. The value storage is independent of the Widget. + instances. ''' @@ -42,7 +42,6 @@ __all__ = ('Property', 'ObjectProperty', 'BooleanProperty', 'BoundedNumericProperty', 'OptionProperty', 'ReferenceListProperty', 'AliasProperty') - cdef class Property: '''Base class for building more complex properties. @@ -78,13 +77,11 @@ cdef class Property: cdef str _name cdef int allownone cdef object defaultvalue - cdef dict storage def __cinit__(self): self._name = '' self.allownone = 0 self.defaultvalue = None - self.storage = {} def __init__(self, defaultvalue, **kw): self.defaultvalue = defaultvalue @@ -121,32 +118,33 @@ cdef class Property: d = dict() self._name = name self.init_storage(d) - self.storage[obj.__uid] = d + obj.__storage[name] = d cpdef link_deps(self, object obj, str name): pass + """ cpdef unlink(self, obj): '''Destroy the storage of a widget ''' - if obj in self.storage: - del self.storage[obj.__uid] + if self._name in obj.__storage: + del obj.__storage[self._name] + """ cpdef bind(self, obj, observer): '''Add a new observer to be called only when the value is changed ''' - observers = self.storage[obj.__uid]['observers'] + observers = obj.__storage[self._name]['observers'] if not observer in observers: observers.append(observer) cpdef unbind(self, obj, observer): '''Remove the observer from our widget observer list ''' - if obj.__uid not in self.storage: - return - observers = self.storage[obj.__uid]['observers'] - if observer in observers: - observers.remove(observer) + observers = obj.__storage[self._name]['observers'] + for obj in observers[:]: + if observer in observers: + observers.remove(obj) def __set__(self, obj, val): self.set(obj, val) @@ -163,7 +161,7 @@ cdef class Property: '''Set a new value for the property ''' value = self.convert(obj, value) - d = self.storage[obj.__uid] + d = obj.__storage[self._name] realvalue = d['value'] if self.compare_value(realvalue, value): return False @@ -175,7 +173,7 @@ cdef class Property: cpdef get(self, obj): '''Return the value of the property ''' - return self.storage[obj.__uid]['value'] + return obj.__storage[self._name]['value'] # # Private part @@ -189,7 +187,7 @@ cdef class Property: bool, True if the value correctly validates. ''' if x is None: - if not self.storage[obj.__uid]['allownone']: + if not obj.__storage[self._name]['allownone']: raise ValueError('None is not allowed') else: return True @@ -203,8 +201,8 @@ cdef class Property: cdef dispatch(self, obj): '''Dispatch the value change to all observers ''' - observers = self.storage[obj.__uid]['observers'] - value = self.storage[obj.__uid]['value'] + observers = obj.__storage[self._name]['observers'] + value = obj.__storage[self._name]['value'] for observer in observers: observer(obj, value) @@ -244,6 +242,9 @@ cdef class StringProperty(Property): raise ValueError('StringProperty<%s> accepts only str/unicode' % self.name) +cdef observable_list_dispatch(object self): + cdef Property prop = self.prop + prop.dispatch(self.obj) class ObservableList(list): # Internal class to observe changes inside a native python list. @@ -254,69 +255,56 @@ class ObservableList(list): def __setitem__(self, key, value): list.__setitem__(self, key, value) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __delitem__(self, key): list.__delitem__(self, key) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __setslice__(self, *largs): list.__setslice__(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __delslice__(self, *largs): list.__delslice__(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __iadd__(self, *largs): list.__iadd__(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __imul__(self, *largs): list.__imul__(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def __add__(self, *largs): cdef object result = list.__add__(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) return result def append(self, *largs): list.append(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def remove(self, *largs): list.remove(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def insert(self, *largs): list.insert(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def pop(self, *largs): list.pop(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def extend(self, *largs): list.extend(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) def sort(self, *largs): list.sort(self, *largs) - cdef Property prop = self.prop - prop.dispatch(self.obj) + observable_list_dispatch(self) cdef class ListProperty(Property): @@ -332,7 +320,7 @@ cdef class ListProperty(Property): ''' cpdef link(self, object obj, str name): Property.link(self, obj, name) - storage = self.storage[obj.__uid] + storage = obj.__storage[self._name] storage['value'] = ObservableList(self, obj, storage['value']) cdef check(self, obj, value): @@ -456,7 +444,7 @@ cdef class OptionProperty(Property): cdef check(self, obj, value): if Property.check(self, obj, value): return True - valid_options = self.storage[obj.__uid]['options'] + valid_options = obj.__storage[self._name]['options'] if value not in valid_options: raise ValueError('OptionProperty<%s> have an invalid option %r. ' 'Must be one of: %s' % (self.name, @@ -492,13 +480,15 @@ cdef class ReferenceListProperty(Property): for prop in self.properties: prop.bind(obj, self.trigger_change) + """ cpdef unlink(self, obj): for prop in self.properties: prop.unbind(obj, self.trigger_change) Property.unlink(self, obj) + """ cpdef trigger_change(self, obj, value): - s = self.storage[obj.__uid] + s = obj.__storage[self._name] if s['stop_event']: return p = s['properties'] @@ -512,14 +502,14 @@ cdef class ReferenceListProperty(Property): return list(value) cdef check(self, obj, value): - if len(value) != len(self.storage[obj.__uid]['properties']): + if len(value) != len(obj.__storage[self._name]['properties']): raise ValueError('ReferenceListProperty<%s> value length is ' 'immutable' % self.name) cpdef set(self, obj, _value): cdef int idx cdef list value - storage = self.storage[obj.__uid] + storage = obj.__storage[self._name] value = self.convert(obj, _value) if self.compare_value(storage['value'], value): return False @@ -537,7 +527,7 @@ cdef class ReferenceListProperty(Property): return True cpdef get(self, obj): - s = self.storage[obj.__uid] + s = obj.__storage[self._name] p = s['properties'] s['value'] = [p[x].get(obj) for x in xrange(len(p))] return s['value'] @@ -589,24 +579,26 @@ cdef class AliasProperty(Property): oprop = getattr(obj.__class__, prop) oprop.bind(obj, self.trigger_change) + """ cpdef unlink(self, obj): for prop in self.bind_objects: oprop = getattr(obj.__class__, prop) oprop.unbind(obj, self.trigger_change) Property.unlink(self, obj) + """ cpdef trigger_change(self, obj, value): - self.storage[obj.__uid]['value'] = self.get(obj) + obj.__storage[self._name]['value'] = self.get(obj) self.dispatch(obj) cdef check(self, obj, value): return True cpdef get(self, obj): - return self.storage[obj.__uid]['getter'](obj) + return obj.__storage[self._name]['getter'](obj) cpdef set(self, obj, value): - if self.storage[obj.__uid]['setter'](obj, value): - self.storage[obj.__uid]['value'] = self.get(obj) + if obj.__storage[self._name]['setter'](obj, value): + obj.__storage[self._name]['value'] = self.get(obj) self.dispatch(obj) diff --git a/kivy/tests/test_properties.py b/kivy/tests/test_properties.py index 3add15787..c5c0ebb3f 100644 --- a/kivy/tests/test_properties.py +++ b/kivy/tests/test_properties.py @@ -11,6 +11,7 @@ class Widget(object): def __init__(self, **kwargs): super(Widget, self).__init__(**kwargs) self.__dict__['__uid'] = 1 + self.__dict__['__storage'] = {} wid = Widget() diff --git a/kivy/tools/highlight/kivy.vim b/kivy/tools/highlight/kivy.vim new file mode 100644 index 000000000..11cdff435 --- /dev/null +++ b/kivy/tools/highlight/kivy.vim @@ -0,0 +1,38 @@ +" Vim syntax file +" Language: Kivy +" Maintainer: George Sebastian <11george.s@gmail.com> +" Last Change: 2011 May 1 + +" For version 5.x: Clear all syntax items. +" For version 6.x: Quit when a syntax file was already loaded. +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn match kivyPreProc /#:.*/ +syn match kivyComment /#.*/ +syn match kivyRule /<\I\i*\(,\s*\I\i*\)*>:/ +syn match kivyAttribute /\<\I\i*\>/ nextgroup=kivyValue + +syn include @pyth $VIMRUNTIME/syntax/python.vim +syn region kivyValue start=":" end=/$/ contains=@pyth skipwhite + +syn region kivyAttribute matchgroup=kivyIdent start=/[\a_][\a\d_]*:/ end=/$/ contains=@pyth skipwhite + +if version >= 508 || !exists("did_python_syn_inits") + if version <= 508 + let did_python_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink kivyPreproc PreProc + HiLink kivyComment Comment + HiLink kivyRule Function + HiLink kivyIdent Statement + HiLink kivyAttribute Label + delcommand HiLink +endif diff --git a/kivy/uix/boxlayout.py b/kivy/uix/boxlayout.py index ed7bb9927..81f806cb2 100644 --- a/kivy/uix/boxlayout.py +++ b/kivy/uix/boxlayout.py @@ -26,7 +26,7 @@ layout, and the second should be 30%. :: The `size_hint` represent the size available after substracting all the fixed size. For example, if you have 3 widgets (width is 200px, - 50%, 50%), and if the layout have a width of 600px : + 50%, 50%), and if the layout have a width of 800px : - the first widget width will be 200px - the second widget width will be 300px diff --git a/kivy/uix/camera.py b/kivy/uix/camera.py index 15df21548..803c56fec 100644 --- a/kivy/uix/camera.py +++ b/kivy/uix/camera.py @@ -107,7 +107,7 @@ class Camera(Image): if not self._camera: return if value: - self._camera.play() + self._camera.start() else: self._camera.stop() diff --git a/kivy/uix/image.py b/kivy/uix/image.py index 162cdffa1..35d488580 100644 --- a/kivy/uix/image.py +++ b/kivy/uix/image.py @@ -114,7 +114,7 @@ class Image(Widget): tw, th = self.texture.size # ensure that the width is always maximized to the containter width - iw = w if tw < w else w + iw = w if tw < w else tw # calculate the appropriate height ih = iw / ratio # if the height is too higher, take the height of the container diff --git a/kivy/uix/togglebutton.py b/kivy/uix/togglebutton.py index 0b89553cc..a6ab5c2ff 100644 --- a/kivy/uix/togglebutton.py +++ b/kivy/uix/togglebutton.py @@ -43,7 +43,7 @@ class ToggleButton(Button): def on_group(self, *largs): groups = ToggleButton.__groups if self._previous_group: - groups.remove(self) + groups[self._previous_group].remove(self) group = self._previous_group = self.group if not group in groups: groups[group] = [] diff --git a/kivy/uix/widget.py b/kivy/uix/widget.py index 0448264c0..797fdb1ca 100644 --- a/kivy/uix/widget.py +++ b/kivy/uix/widget.py @@ -100,6 +100,7 @@ class Widget(EventDispatcher): # of doing that without require any python code. :) Widget.__widget_uid += 1 self.__dict__['__uid'] = Widget.__widget_uid + self.__dict__['__storage'] = {} cp = Widget.__cache_properties if __cls__ not in cp: @@ -128,13 +129,6 @@ class Widget(EventDispatcher): # Then, return the class instance return self - def __del__(self): - # The thing here, since the storage of the property is inside the - # Property class, we must remove ourself from the storage of each - # Property. The usage is faster, the creation / deletion is longer. - for attr in self.__properties.itervalues(): - attr.unlink(self) - def __init__(self, **kwargs): super(Widget, self).__init__() diff --git a/setup.py b/setup.py index cfc727e69..8866f8c67 100644 --- a/setup.py +++ b/setup.py @@ -107,7 +107,7 @@ ext_modules = [] # list all files to compile pyx_files = [] pxd_files = [] -kivy_libs_dir = realpath(kivy.kivy_libs_dir) +kivy_libs_dir = realpath(join(kivy.kivy_base_dir, 'libs')) for root, dirnames, filenames in walk(join(dirname(__file__), 'kivy')): # ignore lib directory if realpath(root).startswith(kivy_libs_dir): @@ -124,7 +124,7 @@ if not have_cython: # add cython core extension modules if cython is available if True: - libraries = [] + libraries = ['m'] include_dirs = [] extra_link_args = [] if platform == 'win32': @@ -183,7 +183,7 @@ if True: sdl_extra_link_args += ['-framework', 'ImageIO'] else: sdl_includes = ['/usr/local/include/SDL'] - sdl_extra_link_args += ['-L', '/usr/local/lib/'] + sdl_extra_link_args += ['-L/usr/local/lib/'] pxd_core = [x for x in pxd_files if not 'graphics' in x] pxd_graphics = [x for x in pxd_files if 'graphics' in x] @@ -193,6 +193,7 @@ if True: ext_files = [pyx] ext_libraries = libraries[:] ext_include_dirs = include_dirs[:] + ext_extra_compile_args = [] ext_extra_link_args = extra_link_args[:] ext_extra_compile_args = [] @@ -205,6 +206,7 @@ if True: ext_libraries += sdl_libraries ext_include_dirs += sdl_includes ext_extra_link_args += sdl_extra_link_args + elif pyx.endswith('osxcoreimage.pyx') or pyx.endswith('osxcoreimage.c'): if c_options['use_ios'] is False: continue @@ -215,6 +217,7 @@ if True: ext_extra_link_args += ['-framework', 'QuartzCore'] ext_extra_link_args += ['-framework', 'ImageIO'] ext_extra_compile_args += ['-isysroot', '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk'] + elif 'graphics' in pyx: ext_files += pxd_graphics else: @@ -224,6 +227,7 @@ if True: module_name, ext_files, libraries=ext_libraries, + extra_compile_args=ext_extra_compile_args, include_dirs=ext_include_dirs, extra_compile_args=ext_extra_compile_args, extra_link_args=ext_extra_link_args)) @@ -274,6 +278,7 @@ setup( 'kivy.core.text', 'kivy.core.video', 'kivy.core.window', + 'kivy.ext', 'kivy.graphics', 'kivy.input', 'kivy.input.postproc',