pilas.utils

Package pilas :: Module utils |
[hide private]
[frames] | no frames]

# Source Code for Module pilas.utils

      1  # -*- encoding: utf-8 -*-
      2  # Pilas engine - A video game framework.
      3  #
      4  # Copyright 2010 - Hugo Ruscitti
      5  # License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html)
      6  #
      7  # Website - http://www.pilas-engine.com.ar
      8  from __future__ import division
      9  import os
     10  import interpolaciones
     11  import sys
     12  import subprocess
     13  import math
     14  import uuid
     15  import pilas
     16
     17
     18  PATH = os.path.dirname(os.path.abspath(__file__))
     19
     20
     21 -def es_interpolacion(an_object):
     22      """Indica si un objeto se comportará como una interpolación.
     23
     24      :param an_object: El objeto a consultar.
     25      """
     26      return isinstance(an_object, interpolaciones.Interpolacion)
     27
     28
     29 -def obtener_ruta_al_recurso(ruta):
     30      """Busca la ruta a un archivo de recursos.
     31
     32      Los archivos de recursos (como las imagenes) se buscan en varios
     33      directorios (ver docstring de image.load), así que esta
     34      función intentará dar con el archivo en cuestión.
     35
     36      :param ruta: Ruta al archivo (recurso) a inspeccionar.
     37      """
     38
     39      dirs = ['./', os.path.dirname(sys.argv[0]), 'data', PATH, PATH + '/data']
     40
     41
     42      for x in dirs:
     43          full_path = os.path.join(x, ruta)
     44          #DEBUG: print "buscando en: '%s'" %(full_path)
     45
     46          if os.path.exists(full_path):
     47              return full_path
     48
     49      # Si no ha encontrado el archivo lo reporta.
     50      raise IOError("El archivo '%s' no existe." %(ruta))
     51
     52
     53 -def esta_en_sesion_interactiva():
     54      """Indica si pilas se ha ejecutado desde una consola interactiva de python."""
     55      import sys
     56      try:
     57          cursor = sys.ps1
     58          return True
     59      except AttributeError:
     60          try:
     61              in_ipython = sys.ipcompleter
     62              return True
     63          except AttributeError:
     64              if sys.stdin.__class__.__module__.startswith("idle"):
     65                  return True
     66
     67      return False
     68
     69 -def distancia(a, b):
     70      """Retorna la distancia entre dos numeros.
     71
     72          >>> distancia(30, 20)
     73          10
     74
     75      :param a: Un valor numérico.
     76      :param b: Un valor numérico.
     77      """
     78      return abs(b - a)
     79
     80 -def distancia_entre_dos_puntos((x1, y1), (x2, y2)):
     81      """Retorna la distancia entre dos puntos en dos dimensiones.
     82
     83      :param x1: Coordenada horizontal del primer punto.
     84      :param y1: Coordenada vertical del primer punto.
     85      :param x2: Coordenada horizontal del segundo punto.
     86      :param y2: Coordenada vertical del segundo punto.
     87      """
     88      return math.sqrt(distancia(x1, x2) ** 2 + distancia(y1, y2) ** 2)
     89
     90 -def distancia_entre_dos_actores(a, b):
     91      """Retorna la distancia en pixels entre dos actores.
     92
     93      :param a: El primer actor.
     94      :param b: El segundo actor.
     95      """
     96      return distancia_entre_dos_puntos((a.x, a.y), (b.x, b.y))
     97
     98 -def actor_mas_cercano_al_actor(actor):
     99      """Retorna el actor de la escena que esté mas cercano a otro indicado por parámetro.
    100
    101      :param actor: El actor tomado como referencia.
    102      """
    103      actor_mas_cercano = None
    104      distancia = 999999
    105      for actor_cercano in pilas.escena_actual().actores:
    106          if id(actor_cercano) != id(actor):
    107              if distancia_entre_dos_actores(actor,actor_cercano) < distancia:
    108                  actor_mas_cercano = actor_cercano
    109
    110      return actor_mas_cercano
    111
    112 -def colisionan(a, b):
    113      """Retorna True si dos actores estan en contacto.
    114
    115      :param a: Un actor.
    116      :param b: El segundo actor a verificar.
    117      """
    118      return distancia_entre_dos_actores(a, b) < a.radio_de_colision + b.radio_de_colision
    119
    120 -def interpolable(f):
    121      """Decorador que se aplica a un metodo para que permita animaciones de interpolaciones.
    122
    123      Ejemplo::
    124
    125          @interpolable
    126          def set_x(self, valor_x):
    127              [...]
    128
    129      :param f: Método sobre el que va a trabajar el interpolador.
    130      """
    131
    132      def inner(*args, **kwargs):
    133          value = args[1]
    134
    135          # Si le indican dos argumentos, el primer sera
    136          # el valor de la interpolacion y el segundo la
    137          # velocidad.
    138          if isinstance(value, tuple) and len(value) == 2:
    139              duracion = value[1]
    140              value = value[0]
    141          else:
    142              duracion = 1
    143
    144          if isinstance(value, list):
    145              value = interpolar(value, duracion=duracion, tipo='lineal')
    146          elif isinstance(value, xrange):
    147              value = interpolar(list(value), duracion=duracion, tipo='lineal')
    148
    149          if es_interpolacion(value):
    150              value.apply(args[0], function=f.__name__)
    151          else:
    152              f(args[0], value, **kwargs)
    153
    154      return inner
    155
    156 -def hacer_coordenada_mundo(x, y):
    157      """Convierte una coordenada de pantalla a una coordenada dentro del motor de física.
    158
    159      :param x: Coordenada horizontal.
    160      :param y: Coordenada vertical.
    161      """
    162      dx, dy = pilas.mundo.motor.centro_fisico()
    163      return (x + dx, dy - y)
    164
    165 -def hacer_coordenada_pantalla_absoluta(x, y):
    166      # TODO: Duplicado del codigo anterior?
    167      dx, dy = pilas.mundo.motor.centro_fisico()
    168      return (x + dx, dy - y)
    169
    170 -def listar_actores_en_consola():
    171      """Imprime una lista de todos los actores en la escena sobre la consola."""
    172      todos = pilas.escena_actual().actores
    173
    174      print "Hay %d actores en la escena:" %(len(todos))
    175      print ""
    176
    177      for s in todos:
    178          print "\t", s
    179
    180      print ""
    181
    182 -def obtener_angulo_entre(punto_a, punto_b):
    183      """Retorna el ángulo entro dos puntos de la pantalla.
    184
    185      :param punto_a: Una tupla con la coordenada del primer punto.
    186      :param punto_b: Una tupla con la coordenada del segundo punto.
    187      """
    188      (x, y) = punto_a
    189      (x1, y1) = punto_b
    190      return math.degrees(math.atan2(y1 - y, x1 -x))
    191
    192 -def convertir_de_posicion_relativa_a_fisica(x, y):
    193      dx, dy = pilas.mundo.motor.centro_fisico()
    194      return (x + dx, dy - y)
    195
    196 -def convertir_de_posicion_fisica_relativa(x, y):
    197      dx, dy = pilas.mundo.motor.centro_fisico()
    198      return (x - dx, dy - y)
    199
    200 -def calcular_tiempo_en_recorrer(distancia_en_pixeles, velocidad):
    201      """Calcula el tiempo que se tardará en recorrer una distancia en
    202      pixeles con una velocidad constante.
    203
    204      :param distancia_en_pixeles: La longitud a recorrer medida en pixels.
    205      :param velocidad: La velocida medida en pixels.
    206      """
    207      if (pilas.mundo.motor.canvas.fps.cuadros_por_segundo_numerico > 0):
    208          return (distancia_en_pixeles / (pilas.mundo.motor.canvas.fps.cuadros_por_segundo_numerico * velocidad))
    209      else:
    210          return 0
    211
    212 -def interpolar(valor_o_valores, duracion=1, demora=0, tipo='lineal'):
    213      """Retorna un objeto que representa cambios de atributos progresivos.
    214
    215      El resultado de esta función se puede aplicar a varios atributos
    216      de los actores, por ejemplo:
    217
    218          >>> bomba = pilas.actores.Bomba()
    219          >>> bomba.escala = pilas.interpolar(3)
    220
    221      Esta función también admite otros parámetros cómo:
    222
    223      :param duracion: es la cantidad de segundos que demorará toda la interpolación.
    224      :param demora: cuantos segundos se deben esperar antes de iniciar.
    225      :param tipo: es el algoritmo de la interpolación, puede ser 'lineal'.
    226      """
    227
    228      import interpolaciones
    229
    230      algoritmos = {
    231              'lineal': interpolaciones.Lineal,
    232              'aceleracion_gradual': interpolaciones.AceleracionGradual,
    233              'desaceleracion_gradual': interpolaciones.DesaceleracionGradual,
    234              'rebote_inicial': interpolaciones.ReboteInicial,
    235              'rebote_final': interpolaciones.ReboteFinal,
    236              'elastico_inicial': interpolaciones.ElasticoInicial,
    237              'elastico_final': interpolaciones.ElasticoFinal
    238              }
    239
    240      if algoritmos.has_key(tipo):
    241          clase = algoritmos[tipo]
    242      else:
    243          raise ValueError("El tipo de interpolacion %s es invalido" %(tipo))
    244
    245      # Permite que los valores de interpolacion sean un numero o una lista.
    246      if not isinstance(valor_o_valores, list):
    247          valor_o_valores = [valor_o_valores]
    248
    249      return clase(valor_o_valores, duracion, demora)
    250
    251
    252 -def deneter_interpolacion(objeto, propiedad):
    253      """Deteiene una interpolación iniciada en un campo de un objeto.
    254
    255         >>> pilas.utils.deneter_interpolacion(actor, 'y')
    256
    257      :param objeto: Actor del que se desea detener al interpolacion.
    258      :para propiedad: Cadena de texto que indica la propiedad del objeto cuya interpolación se desea terminar.
    259      """
    260      setter = 'set_' + propiedad
    261      try:
    262          getattr(objeto, setter)
    263          pilas.escena_actual().tweener.removeTweeningFromObjectField(objeto, setter)
    264      except:
    265          print "El obejto %s no tiene esa propiedad %s" %(objeto.__class__.__name__, setter)
    266
    267 -def obtener_area():
    268      """Retorna el area que ocupa la ventana"""
    269      return pilas.mundo.motor.obtener_area()
    270
    271 -def obtener_bordes():
    272      """Retorna los bordes de la pantalla en forma de tupla."""
    273      ancho, alto = pilas.mundo.motor.obtener_area()
    274      return -ancho/2, ancho/2, alto/2, -alto/2
    275
    276 -def obtener_area_de_texto(texto):
    277      """Informa el ancho y alto que necesitara un texto para imprimirse.
    278
    279      :param texto: La cadena de texto que se quiere imprimir.
    280      """
    281      return pilas.mundo.motor.obtener_area_de_texto(texto)
    282
    283 -def realizar_pruebas():
    284      """Imprime pruebas en pantalla para detectar si pilas tiene todas las dependencias instaladas."""
    285      print "Realizando pruebas de dependencias:"
    286      print ""
    287
    288      print "Box 2D:",
    289
    290      if pilas.fisica.obtener_version().startswith("2.1"):
    291          print "OK, versión", pilas.fisica.obtener_version()
    292      else:
    293          print "Error -> la versión está obsoleta, instale una versión de la serie 2.1"
    294
    295      print "pyqt:",
    296
    297      try:
    298          from PyQt4 import Qt
    299          print "OK, versión", Qt.PYQT_VERSION_STR
    300      except ImportError:
    301          print "Error -> no se encuentra pyqt."
    302
    303      print "pyqt con aceleracion:",
    304
    305      try:
    306          from PyQt4 import QtOpenGL
    307          from PyQt4.QtOpenGL import QGLWidget
    308          print "OK"
    309      except ImportError:
    310          print "Error -> no se encuentra pyqt4gl."
    311
    312      print "PIL para soporte de jpeg (opcional):",
    313
    314      try:
    315          from PIL import Image
    316          print "OK"
    317      except ImportError:
    318          print "Cuidado -> no se encuentra PIL."
    319
    320 -def ver_codigo(objeto, imprimir, retornar):
    321      """Imprime en pantalla el codigo fuente asociado a un objeto.
    322
    323      :param objeto: El objeto que se quiere inspeccionar.
    324      :param imprimir: Un valor True o False indicando si se quiere imprimir directamente sobre la pantalla.
    325      :param retornar: Un valor True o False indicando si se quiere obtener el código como un string.
    326      """
    327      import inspect
    328
    329      try:
    330          codigo = inspect.getsource(objeto.__class__)
    331      except TypeError:
    332          codigo = inspect.getsource(objeto)
    333
    334      if imprimir:
    335          print codigo
    336
    337      if retornar:
    338          return codigo
    339
    340 -def obtener_uuid():
    341      """Genera un identificador único."""
    342      return str(uuid.uuid4())
    343
    344 -def abrir_archivo_con_aplicacion_predeterminada(ruta_al_archivo):
    345      """Intenta abrir un archivo con la herramienta recomenda por el sistema operativo.
    346
    347      :param ruta_al_archivo: La ruta al archivo que se quiere abrir.
    348      """
    349      if sys.platform.startswith('darwin'):
    350          subprocess.call(('open', ruta_al_archivo))
    351      elif os.name == 'nt':
    352          os.startfile(ruta_al_archivo)
    353      elif os.name == 'posix':
    354          subprocess.call(('xdg-open', ruta_al_archivo))
    355
    356 -def centrar_ventana(widget):
    357      """Coloca la ventana o widget directamente en el centro del escritorio.
    358
    359      :param widget: Widget que representa la ventana.
    360      """
    361      from PyQt4 import QtGui
    362      desktop = QtGui.QApplication.desktop()
    363      widget.move(desktop.screen().rect().center() - widget.rect().center())
    364
    365 -def descargar_archivo_desde_internet(parent, url, archivo_destino):
    366      """Inicia la descarga de una archivo desde Internet.
    367
    368      :param parent: El widget que será padre de la ventana.
    369      :param url: La URL desde donde se descargará el archivo.
    370      :param archivo_destino: La ruta en donde se guardará el archivo.
    371      """
    372      import descargar
    373      ventana = descargar.Descargar(parent, url, archivo_destino)
    374      ventana.show()
    375
    376 -def imprimir_todos_los_eventos():
    377      """Muestra en consola los eventos activos y a quienes invocan"""
    378      import pilas
    379
    380      for x in dir(pilas.escena_actual()):
    381          attributo = getattr(pilas.escena_actual(), x)
    382
    383          if isinstance(attributo, pilas.evento.Evento):
    384              print "Evento:", attributo.nombre
    385              attributo.imprimir_funciones_conectadas()
    386              print ""
    387
    388 -def habilitar_depuracion():
    389      """Permite habilitar un breakpoint para depuracion una vez inicializado pilas."""
    390      from PyQt4.QtCore import pyqtRemoveInputHook
    391      from pdb import set_trace
    392      pyqtRemoveInputHook()
    393      set_trace()
    394
    395 -def mostrar_mensaje_de_error_y_salir(motivo):
    396      """Muestra un mensaje de error y termina con la ejecución de pilas.
    397
    398      :param motivo: Un mensaje que explica el problema o la razón del cierre de pilas.
    399      """
    400      from PyQt4 import QtGui
    401      app = QtGui.QApplication(sys.argv[:1])
    402      app.setApplicationName("pilas-engine error")
    403      main_window = QtGui.QMainWindow()
    404      main_window.show()
    405      main_window.raise_()
    406      QtGui.QMessageBox.critical(main_window, "Error", motivo)
    407      app.exit()
    408      sys.exit(1)
    409
    410 -def obtener_archivo_a_ejecutar_desde_argv():
    411      """Obtiene la ruta del archivo a ejecutar desde la linea de argumentos del programa."""
    412      import sys, copy
    413
    414      argv = copy.copy(sys.argv)
    415
    416      if '-i' in argv:
    417          argv.remove('-i')
    418
    419      return " ".join(sys.argv[1:])
    420