Package pilas :: Package motores :: Module motor_qt
[hide private]
[frames] | no frames]

Source Code for Module pilas.motores.motor_qt

   1  # -*- coding: 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   
   9  from PyQt4 import QtCore, QtGui 
  10  from PyQt4.QtCore import Qt 
  11  #from PyQt4.QtOpenGL import QGLWidget 
  12  from PyQt4.QtGui import QWidget as QGLWidget 
  13  from pilas import actores, colores, depurador, eventos, fps 
  14  from pilas import imagenes, simbolos, utils 
  15  import copy 
  16  import os 
  17  import pilas 
  18  import sys 
  19  import traceback 
20 21 22 -class Ventana(QtGui.QMainWindow):
23
24 - def __init__(self, parent=None):
25 QtGui.QMainWindow.__init__(self, parent) 26 self.setStyleSheet("QWidget {background-color : #222}")
27
28 - def set_canvas(self, canvas):
29 self.canvas = canvas 30 self.canvas.setParent(self)
31
32 - def resizeEvent(self, event):
33 self.canvas.resize_to(self.width(), self.height())
34
35 36 -class CanvasWidget(QGLWidget):
37
38 - def __init__(self, motor, lista_actores, ancho, alto, gestor_escenas, permitir_depuracion, rendimiento):
39 QGLWidget.__init__(self, None) 40 self.painter = QtGui.QPainter() 41 self.setMouseTracking(True) 42 43 self.pausa_habilitada = False 44 self.mouse_x = 0 45 self.mouse_y = 0 46 self.motor = motor 47 self.lista_actores = lista_actores 48 self.fps = fps.FPS(rendimiento, True) 49 50 if permitir_depuracion: 51 self.depurador = depurador.Depurador(motor.obtener_lienzo(), self.fps) 52 else: 53 self.depurador = depurador.DepuradorDeshabilitado() 54 55 self.original_width = ancho 56 self.original_height = alto 57 self.escala = 1 58 self.startTimer(1000/100.0) 59 60 self.gestor_escenas = gestor_escenas
61
62 - def resize_to(self, w, h):
63 escala_x = w / float(self.original_width) 64 escala_y = h / float(self.original_height) 65 escala = min(escala_x, escala_y) 66 67 final_w = self.original_width * escala 68 final_h = self.original_height * escala 69 self.escala = escala 70 71 x = w - final_w 72 y = h - final_h 73 74 self.setGeometry(x/2, y/2, final_w, final_h)
75
76 - def paintEvent(self, event):
77 self.painter.begin(self) 78 self.painter.scale(self.escala, self.escala) 79 80 self.painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing, True) 81 self.painter.setRenderHint(QtGui.QPainter.SmoothPixmapTransform, True) 82 self.painter.setRenderHint(QtGui.QPainter.Antialiasing, True) 83 84 self.painter.fillRect(0, 0, self.original_width, self.original_height, QtGui.QColor(128, 128, 128)) 85 self.depurador.comienza_dibujado(self.motor, self.painter) 86 87 actores_a_eliminar = [] 88 89 if self.gestor_escenas.escena_actual(): 90 actores_de_la_escena = self.gestor_escenas.escena_actual().actores 91 for actor in actores_de_la_escena: 92 #if actor._vivo: 93 try: 94 if not actor.esta_fuera_de_la_pantalla(): 95 actor.dibujar(self.painter) 96 except Exception: 97 print traceback.format_exc() 98 print sys.exc_info()[0] 99 actor.eliminar() 100 101 self.depurador.dibuja_al_actor(self.motor, self.painter, actor) 102 #else: 103 # actores_a_eliminar.append(actor) 104 105 #for x in actores_a_eliminar: 106 # actores_de_la_escena.remove(x) 107 108 self.depurador.termina_dibujado(self.motor, self.painter) 109 self.painter.end()
110
111 - def timerEvent(self, event):
112 try: 113 self._realizar_actualizacion_logica() 114 except Exception: 115 print traceback.format_exc() 116 print sys.exc_info()[0] 117 118 self.update()
119
121 for x in range(self.fps.actualizar()): 122 if not self.pausa_habilitada: 123 self._actualizar_eventos_y_actores() 124 self._actualizar_escena()
125
126 - def _actualizar_escena(self):
127 self.gestor_escenas.actualizar()
128
130 eventos.actualizar.emitir() 131 132 try: 133 for actor in self.gestor_escenas.escena_actual().actores: 134 actor.pre_actualizar() 135 actor.actualizar() 136 except Exception: 137 print traceback.format_exc() 138 print sys.exc_info()[0]
139
140 - def mouseMoveEvent(self, e):
141 escala = self.escala 142 x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala) 143 144 dx, dy = x - self.mouse_x, y - self.mouse_y 145 146 izquierda, derecha, arriba, abajo = utils.obtener_bordes() 147 148 #x = max(min(derecha, x), izquierda) 149 #y = max(min(arriba, y), abajo) 150 151 x += pilas.mundo.motor.camara_x 152 y += pilas.mundo.motor.camara_y 153 154 self.gestor_escenas.escena_actual().mueve_mouse.emitir(x=x, y=y, dx=dx, dy=dy) 155 156 self.mouse_x = x 157 self.mouse_y = y 158 self.depurador.cuando_mueve_el_mouse(x, y)
159
160 - def keyPressEvent(self, event):
161 codigo_de_tecla = self._obtener_codigo_de_tecla_normalizado(event.key()) 162 163 # Se mantiene este lanzador de eventos por la clase Control 164 if event.key() == QtCore.Qt.Key_Escape: 165 eventos.pulsa_tecla_escape.emitir() 166 if event.key() == QtCore.Qt.Key_P and event.modifiers() == QtCore.Qt.AltModifier: 167 self.alternar_pausa() 168 if event.key() == QtCore.Qt.Key_F and event.modifiers() == QtCore.Qt.AltModifier: 169 self.alternar_pantalla_completa() 170 171 eventos.pulsa_tecla.emitir(codigo=codigo_de_tecla, es_repeticion=event.isAutoRepeat(), texto=event.text()) 172 self.depurador.cuando_pulsa_tecla(codigo_de_tecla, event.text())
173
174 - def keyReleaseEvent(self, event):
175 codigo_de_tecla = self._obtener_codigo_de_tecla_normalizado(event.key()) 176 # Se mantiene este lanzador de eventos por la clase Control 177 eventos.suelta_tecla.emitir(codigo=codigo_de_tecla, es_repeticion=event.isAutoRepeat(), texto=event.text())
178
179 - def wheelEvent(self, e):
180 self.gestor_escenas.escena_actual().mueve_rueda.emitir(delta=e.delta() / 120)
181
182 - def mousePressEvent(self, e):
183 escala = self.escala 184 x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala) 185 boton_pulsado = e.button() 186 187 x += pilas.mundo.motor.camara_x 188 y += pilas.mundo.motor.camara_y 189 190 self.gestor_escenas.escena_actual().click_de_mouse.emitir(x=x, y=y, dx=0, dy=0, boton=boton_pulsado)
191
192 - def mouseReleaseEvent(self, e):
193 escala = self.escala 194 x, y = utils.convertir_de_posicion_fisica_relativa(e.pos().x()/escala, e.pos().y()/escala) 195 boton_pulsado = e.button() 196 197 x += pilas.mundo.motor.camara_x 198 y += pilas.mundo.motor.camara_y 199 200 self.gestor_escenas.escena_actual().termina_click.emitir(x=x, y=y, dx=0, dy=0, boton=boton_pulsado)
201
202 - def _obtener_codigo_de_tecla_normalizado(self, tecla_qt):
203 teclas = { 204 QtCore.Qt.Key_Left: simbolos.IZQUIERDA, 205 QtCore.Qt.Key_Right: simbolos.DERECHA, 206 QtCore.Qt.Key_Up: simbolos.ARRIBA, 207 QtCore.Qt.Key_Down: simbolos.ABAJO, 208 QtCore.Qt.Key_Space: simbolos.ESPACIO, 209 QtCore.Qt.Key_Return: simbolos.SELECCION, 210 QtCore.Qt.Key_F1: simbolos.F1, 211 QtCore.Qt.Key_F2: simbolos.F2, 212 QtCore.Qt.Key_F3: simbolos.F3, 213 QtCore.Qt.Key_F4: simbolos.F4, 214 QtCore.Qt.Key_F5: simbolos.F5, 215 QtCore.Qt.Key_F6: simbolos.F6, 216 QtCore.Qt.Key_F7: simbolos.F7, 217 QtCore.Qt.Key_F8: simbolos.F8, 218 QtCore.Qt.Key_F9: simbolos.F9, 219 QtCore.Qt.Key_F10: simbolos.F10, 220 QtCore.Qt.Key_F11: simbolos.F11, 221 QtCore.Qt.Key_F12: simbolos.F12, 222 QtCore.Qt.Key_A: simbolos.a, 223 QtCore.Qt.Key_B: simbolos.b, 224 QtCore.Qt.Key_C: simbolos.c, 225 QtCore.Qt.Key_D: simbolos.d, 226 QtCore.Qt.Key_E: simbolos.e, 227 QtCore.Qt.Key_F: simbolos.f, 228 QtCore.Qt.Key_G: simbolos.g, 229 QtCore.Qt.Key_H: simbolos.h, 230 QtCore.Qt.Key_I: simbolos.i, 231 QtCore.Qt.Key_J: simbolos.j, 232 QtCore.Qt.Key_K: simbolos.k, 233 QtCore.Qt.Key_L: simbolos.l, 234 QtCore.Qt.Key_M: simbolos.m, 235 QtCore.Qt.Key_N: simbolos.n, 236 QtCore.Qt.Key_O: simbolos.o, 237 QtCore.Qt.Key_P: simbolos.p, 238 QtCore.Qt.Key_Q: simbolos.q, 239 QtCore.Qt.Key_R: simbolos.r, 240 QtCore.Qt.Key_S: simbolos.s, 241 QtCore.Qt.Key_T: simbolos.t, 242 QtCore.Qt.Key_U: simbolos.u, 243 QtCore.Qt.Key_V: simbolos.v, 244 QtCore.Qt.Key_W: simbolos.w, 245 QtCore.Qt.Key_X: simbolos.x, 246 QtCore.Qt.Key_Y: simbolos.y, 247 QtCore.Qt.Key_Z: simbolos.z, 248 } 249 250 if teclas.has_key(tecla_qt): 251 return teclas[tecla_qt] 252 else: 253 return tecla_qt
254
255 - def pantalla_completa(self):
256 self.motor.ventana.showFullScreen()
257
258 - def pantalla_modo_ventana(self):
259 self.motor.ventana.showNormal()
260
261 - def esta_en_pantalla_completa(self):
262 return self.motor.ventana.isFullScreen()
263
264 - def alternar_pausa(self):
265 if self.pausa_habilitada: 266 self.pausa_habilitada = False 267 self.actor_pausa.eliminar() 268 eventos.pulsa_tecla.desconectar_por_id('tecla_en_pausa') 269 else: 270 self.pausa_habilitada = True 271 self.actor_pausa = actores.Pausa() 272 self.actor_pausa.fijo = True 273 self.id_evento = eventos.pulsa_tecla.conectar(self.avanzar_un_solo_cuadro_de_animacion, id='tecla_en_pausa')
274
275 - def avanzar_un_solo_cuadro_de_animacion(self, evento):
277
278 - def alternar_pantalla_completa(self):
279 """Permite cambiar el modo de video. 280 281 Si está en modo ventana, pasa a pantalla completa y viceversa. 282 """ 283 if self.esta_en_pantalla_completa(): 284 self.pantalla_modo_ventana() 285 else: 286 self.pantalla_completa()
287
288 -class CanvasWidgetSugar(CanvasWidget):
289
290 - def _iniciar_aplicacion(self):
291 self.app = None
292
293 - def ejecutar_bucle_principal(self, mundo, ignorar_errores):
294 pass
295
296 -class BaseActor(object):
297
298 - def __init__(self):
299 self._rotacion = 0 300 self._transparencia = 0 301 self.centro_x = 0 302 self.centro_y = 0 303 self._escala_x = 1 304 self._escala_y = 1 305 self._espejado = False 306 self.fijo = 0
307
308 - def definir_centro(self, x, y):
309 self.centro_x = x 310 self.centro_y = y
311
312 - def obtener_posicion(self):
313 return self.x, self.y
314
315 - def definir_posicion(self, x, y):
316 self.x, self.y = x, y
317
318 - def obtener_escala(self):
319 return self._escala_x
320
321 - def definir_escala(self, s):
322 self._escala_x = s 323 self._escala_y = s
324
325 - def definir_escala_x(self, s):
326 self._escala_x = s
327
328 - def definir_escala_y(self, s):
329 self._escala_y = s
330
331 - def definir_transparencia(self, nuevo_valor):
332 self._transparencia = nuevo_valor
333
334 - def obtener_transparencia(self):
335 return self._transparencia
336
337 - def obtener_rotacion(self):
338 return self._rotacion
339
340 - def definir_rotacion(self, r):
341 self._rotacion = r
342
343 - def set_espejado(self, espejado):
344 self._espejado = espejado
345
346 347 -class Imagen(object):
348
349 - def __init__(self, ruta):
350 self.ruta_original = ruta 351 352 if ruta.lower().endswith("jpeg") or ruta.lower().endswith("jpg"): 353 try: 354 self._imagen = self.cargar_jpeg(ruta) 355 except: 356 self._imagen = QtGui.QPixmap(ruta) 357 else: 358 self._imagen = QtGui.QPixmap(ruta)
359
360 - def cargar_jpeg(self, ruta):
361 from PIL import Image 362 import StringIO 363 364 pilImage = Image.open(ruta) 365 stringIO = StringIO.StringIO() 366 pilImage.save(stringIO, format="png") 367 368 pixmapImage = QtGui.QPixmap() 369 pixmapImage.loadFromData(stringIO.getvalue()) 370 371 return pixmapImage
372
373 - def ancho(self):
374 return self._imagen.size().width()
375
376 - def alto(self):
377 return self._imagen.size().height()
378
379 - def centro(self):
380 "Retorna una tupla con la coordenada del punto medio del la imagen." 381 return (self.ancho()/2, self.alto()/2)
382
383 - def avanzar(self):
384 pass
385
386 - def dibujar(self, painter, x, y, dx=0, dy=0, escala_x=1, escala_y=1, rotacion=0, transparencia=0):
387 """Dibuja la imagen sobre la ventana que muestra el motor. 388 389 x, y: indican la posicion dentro del mundo. 390 dx, dy: es el punto centro de la imagen (importante para rotaciones). 391 escala_x, escala_yindican cambio de tamano (1 significa normal). 392 rotacion: angulo de inclinacion en sentido de las agujas del reloj. 393 """ 394 395 painter.save() 396 centro_x, centro_y = pilas.mundo.motor.centro_fisico() 397 painter.translate(x + centro_x, centro_y - y) 398 painter.rotate(rotacion) 399 painter.scale(escala_x, escala_y) 400 401 if transparencia: 402 painter.setOpacity(1 - transparencia/100.0) 403 404 self._dibujar_pixmap(painter, -dx, -dy) 405 painter.restore()
406
407 - def _dibujar_pixmap(self, painter, x, y):
408 painter.drawPixmap(x, y, self._imagen)
409
410 - def __str__(self):
411 nombre_imagen = os.path.basename(self.ruta_original) 412 return "<Imagen del archivo '%s'>" %(nombre_imagen)
413
414 415 -class Grilla(Imagen):
416 417 """Representa una grilla regular, que se utiliza en animaciones. 418 419 La grilla regular se tiene que crear indicando la cantidad 420 de filas y columnas. Una vez definida se puede usar como 421 una imagen normal, solo que tiene dos metodos adicionales 422 para ``definir_cuadro`` y ``avanzar`` el cuadro actual. 423 """ 424
425 - def __init__(self, ruta, columnas=1, filas=1):
426 Imagen.__init__(self, ruta) 427 self.cantidad_de_cuadros = columnas * filas 428 self.columnas = columnas 429 self.filas = filas 430 self.cuadro_ancho = Imagen.ancho(self) / columnas 431 self.cuadro_alto = Imagen.alto(self) / filas 432 self.definir_cuadro(0)
433
434 - def ancho(self):
435 return self.cuadro_ancho
436
437 - def alto(self):
438 return self.cuadro_alto
439
440 - def _dibujar_pixmap(self, painter, x, y):
441 painter.drawPixmap(x, y, self._imagen, self.dx, self.dy, self.cuadro_ancho, self.cuadro_alto)
442
443 - def definir_cuadro(self, cuadro):
444 self._cuadro = cuadro 445 446 frame_col = cuadro % self.columnas 447 frame_row = cuadro / self.columnas 448 449 self.dx = frame_col * self.cuadro_ancho 450 self.dy = frame_row * self.cuadro_alto
451
452 - def avanzar(self):
453 ha_avanzado = True 454 cuadro_actual = self._cuadro + 1 455 456 if cuadro_actual >= self.cantidad_de_cuadros: 457 cuadro_actual = 0 458 ha_avanzado = False 459 460 self.definir_cuadro(cuadro_actual) 461 return ha_avanzado
462
463 - def obtener_cuadro(self):
464 return self._cuadro
465
466 - def dibujarse_sobre_una_pizarra(self, pizarra, x, y):
467 pizarra.pintar_parte_de_imagen(self, self.dx, self.dy, self.cuadro_ancho, self.cuadro_alto, x, y)
468
469 470 -class Texto(Imagen):
471 CACHE_FUENTES = {} 472
473 - def __init__(self, texto, magnitud, motor, vertical=False, fuente=None):
474 self.vertical = vertical 475 self.fuente = fuente 476 self._ancho, self._alto = motor.obtener_area_de_texto(texto, magnitud, vertical, fuente)
477
478 - def _dibujar_pixmap(self, painter, dx, dy):
479 if self.fuente: 480 nombre_de_fuente = Texto.cargar_fuente_desde_cache(self.fuente) 481 else: 482 nombre_de_fuente = painter.font().family() 483 484 fuente = QtGui.QFont(nombre_de_fuente, self.magnitud) 485 metrica = QtGui.QFontMetrics(fuente) 486 487 r, g, b, a = self.color.obtener_componentes() 488 painter.setPen(QtGui.QColor(r, g, b)) 489 painter.setFont(fuente) 490 491 if self.vertical: 492 lines = [t for t in self.texto] 493 else: 494 lines = self.texto.split('\n') 495 496 for line in lines: 497 painter.drawText(dx, dy + self._alto, line) 498 dy += metrica.height()
499 500 @classmethod
501 - def cargar_fuente_desde_cache(kclass, fuente_como_ruta):
502 """Carga o convierte una fuente para ser utilizada dentro del motor. 503 504 Permite a los usuarios referirse a las fuentes como ruta a archivos, sin 505 tener que preocuparse por el font-family. 506 507 :param fuente_como_ruta: Ruta al archivo TTF que se quiere utilizar. 508 509 Ejemplo: 510 511 >>> Texto.cargar_fuente_desde_cache('myttffile.ttf') 512 'Visitor TTF1' 513 """ 514 515 if not fuente_como_ruta in Texto.CACHE_FUENTES.keys(): 516 ruta_a_la_fuente = pilas.utils.obtener_ruta_al_recurso(fuente_como_ruta) 517 fuente_id = QtGui.QFontDatabase.addApplicationFont(ruta_a_la_fuente) 518 Texto.CACHE_FUENTES[fuente_como_ruta] = fuente_id 519 else: 520 fuente_id = Texto.CACHE_FUENTES[fuente_como_ruta] 521 522 return str(QtGui.QFontDatabase.applicationFontFamilies(fuente_id)[0])
523
524 - def ancho(self):
525 return self._ancho
526
527 - def alto(self):
528 return self._alto
529
530 531 -class Lienzo(Imagen):
532
533 - def __init__(self):
534 pass
535
536 - def texto(self, painter, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
537 "Imprime un texto respespetando el desplazamiento de la camara." 538 self.texto_absoluto(painter, cadena, x, y, magnitud, fuente, color)
539
540 - def texto_absoluto(self, painter, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
541 "Imprime un texto sin respetar al camara." 542 x, y = utils.hacer_coordenada_pantalla_absoluta(x, y) 543 544 r, g, b, a = color.obtener_componentes() 545 painter.setPen(QtGui.QColor(r, g, b)) 546 547 if not fuente: 548 fuente = painter.font().family() 549 550 painter.setFont(QtGui.QFont(fuente, magnitud)) 551 painter.drawText(x, y, cadena)
552
553 - def pintar(self, painter, color):
554 r, g, b, a = color.obtener_componentes() 555 ancho, alto = pilas.mundo.motor.obtener_area() 556 painter.fillRect(0, 0, ancho, alto, QtGui.QColor(r, g, b))
557
558 - def linea(self, painter, x0, y0, x1, y1, color=colores.negro, grosor=1):
559 x0, y0 = utils.hacer_coordenada_pantalla_absoluta(x0, y0) 560 x1, y1 = utils.hacer_coordenada_pantalla_absoluta(x1, y1) 561 562 r, g, b, a = color.obtener_componentes() 563 color = QtGui.QColor(r, g, b) 564 pen = QtGui.QPen(color, grosor) 565 painter.setPen(pen) 566 painter.drawLine(x0, y0, x1, y1)
567
568 - def poligono(self, motor, puntos, color=colores.negro, grosor=1, cerrado=False):
569 x, y = puntos[0] 570 if cerrado: 571 puntos.append((x, y)) 572 573 for p in puntos[1:]: 574 nuevo_x, nuevo_y = p 575 self.linea(motor, x, y, nuevo_x, nuevo_y, color, grosor) 576 x, y = nuevo_x, nuevo_y
577
578 - def cruz(self, painter, x, y, color=colores.negro, grosor=1):
579 t = 3 580 self.linea(painter, x - t, y - t, x + t, y + t, color, grosor) 581 self.linea(painter, x + t, y - t, x - t, y + t, color, grosor)
582
583 - def circulo(self, painter, x, y, radio, color=colores.negro, grosor=1):
584 x, y = utils.hacer_coordenada_pantalla_absoluta(x, y) 585 586 r, g, b, a = color.obtener_componentes() 587 color = QtGui.QColor(r, g, b) 588 pen = QtGui.QPen(color, grosor) 589 painter.setPen(pen) 590 painter.drawEllipse(x -radio, y-radio, radio*2, radio*2)
591
592 - def rectangulo(self, painter, x, y, ancho, alto, color=colores.negro, grosor=1):
593 x, y = utils.hacer_coordenada_pantalla_absoluta(x, y) 594 595 r, g, b, a = color.obtener_componentes() 596 color = QtGui.QColor(r, g, b) 597 pen = QtGui.QPen(color, grosor) 598 painter.setPen(pen) 599 painter.drawRect(x, y, ancho, alto)
600
601 602 -class Superficie(Imagen):
603
604 - def __init__(self, ancho, alto):
605 self._imagen = QtGui.QPixmap(ancho, alto) 606 self._imagen.fill(QtGui.QColor(255, 255, 255, 0)) 607 self.canvas = QtGui.QPainter()
608
609 - def pintar(self, color):
610 r, g, b, a = color.obtener_componentes() 611 self._imagen.fill(QtGui.QColor(r, g, b, a))
612
613 - def pintar_parte_de_imagen(self, imagen, origen_x, origen_y, ancho, alto, x, y):
614 self.canvas.begin(self._imagen) 615 self.canvas.drawPixmap(x, y, imagen._imagen, origen_x, origen_y, ancho, alto) 616 self.canvas.end()
617
618 - def pintar_imagen(self, imagen, x=0, y=0):
620
621 - def texto(self, cadena, x=0, y=0, magnitud=10, fuente=None, color=colores.negro):
622 self.canvas.begin(self._imagen) 623 r, g, b, a = color.obtener_componentes() 624 self.canvas.setPen(QtGui.QColor(r, g, b)) 625 dx = x 626 dy = y 627 628 if not fuente: 629 fuente = self.canvas.font().family() 630 631 font = QtGui.QFont(fuente, magnitud) 632 self.canvas.setFont(font) 633 metrica = QtGui.QFontMetrics(font) 634 635 for line in cadena.split('\n'): 636 self.canvas.drawText(dx, dy, line) 637 dy += metrica.height() 638 639 self.canvas.end()
640
641 - def circulo(self, x, y, radio, color=colores.negro, relleno=False, grosor=1):
642 self.canvas.begin(self._imagen) 643 644 r, g, b, a = color.obtener_componentes() 645 color = QtGui.QColor(r, g, b) 646 pen = QtGui.QPen(color, grosor) 647 self.canvas.setPen(pen) 648 649 if relleno: 650 self.canvas.setBrush(color) 651 652 self.canvas.drawEllipse(x -radio, y-radio, radio*2, radio*2) 653 self.canvas.end()
654
655 - def rectangulo(self, x, y, ancho, alto, color=colores.negro, relleno=False, grosor=1):
656 self.canvas.begin(self._imagen) 657 658 r, g, b, a = color.obtener_componentes() 659 color = QtGui.QColor(r, g, b) 660 pen = QtGui.QPen(color, grosor) 661 self.canvas.setPen(pen) 662 663 if relleno: 664 self.canvas.setBrush(color) 665 666 self.canvas.drawRect(x, y, ancho, alto) 667 self.canvas.end()
668
669 - def linea(self, x, y, x2, y2, color=colores.negro, grosor=1):
670 self.canvas.begin(self._imagen) 671 672 r, g, b, a = color.obtener_componentes() 673 color = QtGui.QColor(r, g, b) 674 pen = QtGui.QPen(color, grosor) 675 self.canvas.setPen(pen) 676 677 self.canvas.drawLine(x, y, x2, y2) 678 self.canvas.end()
679
680 - def poligono(self, puntos, color, grosor, cerrado=False):
681 x, y = puntos[0] 682 683 if cerrado: 684 puntos.append((x, y)) 685 686 for p in puntos[1:]: 687 nuevo_x, nuevo_y = p 688 self.linea(x, y, nuevo_x, nuevo_y, color, grosor) 689 x, y = nuevo_x, nuevo_y
690
691 - def dibujar_punto(self, x, y, color=colores.negro):
692 self.circulo(x, y, 3, color=color, relleno=True)
693
694 - def limpiar(self):
695 self._imagen.fill(QtGui.QColor(0, 0, 0, 0))
696
697 698 -class Actor(BaseActor):
699
700 - def __init__(self, imagen="sin_imagen.png", x=0, y=0):
701 702 if isinstance(imagen, str): 703 self.imagen = imagenes.cargar(imagen) 704 else: 705 self.imagen = imagen 706 707 self.x = x 708 self.y = y 709 BaseActor.__init__(self)
710
711 - def definir_imagen(self, imagen):
712 # permite que varios actores usen la misma grilla. 713 if isinstance(imagen, Grilla): 714 self.imagen = copy.copy(imagen) 715 else: 716 self.imagen = imagen
717
718 - def obtener_imagen(self):
719 return self.imagen
720
721 - def dibujar(self, painter):
722 escala_x, escala_y = self._escala_x, self._escala_y 723 724 if self._espejado: 725 escala_x *= -1 726 727 if not self.fijo: 728 x = self.x - pilas.mundo.motor.camara_x 729 y = self.y - pilas.mundo.motor.camara_y 730 else: 731 x = self.x 732 y = self.y 733 734 self.imagen.dibujar(painter, x, y, self.centro_x, self.centro_y, 735 escala_x, escala_y, self._rotacion, self._transparencia)
736
737 738 ## Backend: audio deshabilitado 739 740 -class SonidoDeshabilitado:
741 deshabilitado = True 742
743 - def __init__(self, player, ruta):
744 pass
745
746 - def reproducir(self):
747 pass
748
749 - def detener(self):
750 pass
751
752 - def pausar(self):
753 pass
754
755 - def continuar(self):
756 pass
757
758 -class MusicaDeshabilitada(SonidoDeshabilitado):
759
760 - def __init__(self, media, ruta):
761 SonidoDeshabilitado.__init__(self, media, ruta)
762
763 ## Backend: gstreamer 764 765 -class SonidoGST:
766 deshabilitado = False 767
768 - def __init__(self, player, ruta):
769 self.ruta = ruta 770 self.sonido = player 771 self.sonido.set_property('uri','file://'+ruta)
772
773 - def reproducir(self):
774 import gst 775 if not self.deshabilitado: 776 self.sonido.set_state(gst.STATE_NULL) 777 self.sonido.set_state(gst.STATE_PLAYING)
778
779 - def detener(self):
780 import gst 781 self.sonido.set_state(gst.STATE_NULL)
782
783 784 -class MusicaGST(SonidoGST):
785
786 - def __init__(self, media, ruta):
787 SonidoGST.__init__(self, media, ruta)
788
789 ## Backend: audio phonon 790 791 -class SonidoPhonon:
792 deshabilitado = False 793
794 - def __init__(self, media, ruta):
795 from PyQt4 import phonon 796 self.media = media 797 self.ruta = ruta 798 799 self.source = phonon.Phonon.MediaSource(ruta) 800 self.sonido = phonon.Phonon.createPlayer(phonon.Phonon.GameCategory, self.source)
801
802 - def reproducir(self):
803 if not self.deshabilitado: 804 self.sonido.seek(0) 805 self.sonido.play()
806
807 - def detener(self):
808 "Detiene el audio." 809 self.sonido.stop()
810
811 - def pausar(self):
812 "Hace una pausa del audio." 813 self.sonido.pause()
814
815 - def continuar(self):
816 "Continúa reproduciendo el audio." 817 if not self.deshabilitado: 818 self.sonido.play()
819
820 821 -class MusicaPhonon(SonidoPhonon):
822
823 - def __init__(self, media, ruta):
824 SonidoPhonon.__init__(self, media, ruta)
825
826 827 -class Motor(object):
828 """Representa la ventana principal de pilas. 829 830 Esta clase construirá el objeto apuntado por el atributo 831 ``pilas.motor``, asi que será el representante de todas 832 las funcionalidades multimedia. 833 834 Internamente, este motor, tratará de usar OpenGl para acelerar 835 el dibujado en pantalla si la tarjeta de video lo soporta. 836 """ 837
838 - def __init__(self, usar_motor, permitir_depuracion, audio):
839 if usar_motor not in ['qtwidget', 'qtsugar']: 840 self._iniciar_aplicacion() 841 842 self.usar_motor = usar_motor 843 844 self.nombre = usar_motor 845 self.permitir_depuracion = permitir_depuracion 846 847 self._inicializar_variables() 848 self._inicializar_sistema_de_audio(audio)
849
850 - def _iniciar_aplicacion(self):
851 self.app = QtGui.QApplication([]) 852 self.app.setApplicationName("pilas")
853
854 - def _inicializar_variables(self):
855 self.camara_x = 0 856 self.camara_y = 0
857
858 - def _inicializar_sistema_de_audio(self, audio):
859 sistemas_de_sonido = ['deshabilitado', 'phonon', 'gst'] 860 861 if audio not in sistemas_de_sonido: 862 error = "El sistema de audio '%s' es invalido" %(audio) 863 sugerencia = ". Use alguno de los siguientes: %s" %(str(sistemas_de_sonido)) 864 raise Exception(error + sugerencia) 865 866 if audio == 'gst': 867 try: 868 import gst 869 except ImportError: 870 print "Nota: El sistema de audio gstreamer no está disponible, usando phonon en su lugar." 871 audio = 'phonon' 872 873 if audio == 'deshabilitado': 874 self.player = None 875 self.clase_sonido = SonidoDeshabilitado 876 self.clase_musica = MusicaDeshabilitada 877 elif audio == 'phonon': 878 from PyQt4 import phonon 879 self.media = phonon.Phonon.MediaObject() 880 self.audio = phonon.Phonon.AudioOutput(phonon.Phonon.MusicCategory) 881 self.path = phonon.Phonon.createPath(self.media, self.audio) 882 self.player = self.media 883 self.clase_sonido = SonidoPhonon 884 self.clase_musica = MusicaPhonon 885 elif audio == 'gst': 886 import gst 887 self.player = gst.element_factory_make("playbin", "player") 888 self.clase_sonido = SonidoGST 889 self.clase_musica = MusicaGST
890
891 - def terminar(self):
892 self.ventana.close()
893
894 - def iniciar_ventana(self, ancho, alto, titulo, pantalla_completa, gestor_escenas, rendimiento):
895 self.ventana = Ventana() 896 self.ventana.resize(ancho, alto) 897 898 if self.usar_motor in ['qtwidget', 'qtsugar']: 899 mostrar_ventana = False 900 self.canvas = CanvasWidgetSugar(self, actores.todos, ancho, alto, gestor_escenas, self.permitir_depuracion, rendimiento) 901 else: 902 mostrar_ventana = True 903 self.canvas = CanvasWidget(self, actores.todos, ancho, alto, gestor_escenas, self.permitir_depuracion, rendimiento) 904 905 self.ventana.set_canvas(self.canvas) 906 self.canvas.setFocus() 907 908 self.ancho_original = ancho 909 self.alto_original = alto 910 self.titulo = titulo 911 self.ventana.setWindowTitle(self.titulo) 912 913 if mostrar_ventana: 914 self.ventana.show() 915 self.ventana.raise_() 916 917 if pantalla_completa: 918 self.canvas.pantalla_completa()
919
920 - def modificar_ventana(self, ancho, alto, titulo, pantalla_completa):
921 self.titulo = titulo 922 self.ventana.setWindowTitle(self.titulo) 923 self.canvas.original_width = ancho 924 self.canvas.original_height = alto 925 self.ventana.resize(ancho, alto) 926 927 if pantalla_completa: 928 self.canvas.pantalla_completa() 929 else: 930 self.canvas.pantalla_modo_ventana()
931
932 - def ocultar_puntero_del_mouse(self):
933 self.canvas.setCursor(QtGui.QCursor(Qt.BlankCursor))
934
935 - def mostrar_puntero_del_mouse(self):
936 self.canvas.setCursor(QtGui.QCursor(Qt.ArrowCursor))
937
938 - def ejecutar_bucle_principal(self, mundo, ignorar_errores):
939 if getattr(self, 'app', None): 940 sys.exit(self.app.exec_())
941
942 - def definir_centro_de_la_camara(self, x, y):
943 self.camara_x = x 944 self.camara_y = y
945
947 return (self.camara_x, self.camara_y)
948
949 - def centro_fisico(self):
950 "Centro de la ventana para situar el punto (0, 0)" 951 return self.ancho_original/2, self.alto_original/2
952
953 - def obtener_area(self):
954 return (self.ancho_original, self.alto_original)
955
956 - def obtener_area_de_texto(self, texto, magnitud=10, vertical=False, fuente=None):
957 ancho = 0 958 alto = 0 959 960 if fuente: 961 nombre_de_fuente = Texto.cargar_fuente_desde_cache(fuente) 962 else: 963 nombre_de_fuente = '' 964 965 fuente = QtGui.QFont(nombre_de_fuente, magnitud) 966 metrica = QtGui.QFontMetrics(fuente) 967 968 if vertical: 969 lineas = [t for t in texto] 970 else: 971 lineas = texto.split('\n') 972 973 for linea in lineas: 974 ancho = max(ancho, metrica.width(linea)) 975 alto += metrica.height() 976 977 return ancho, alto
978
979 - def obtener_actor(self, imagen, x, y):
980 return Actor(imagen, x, y)
981
982 - def obtener_texto(self, texto, magnitud, vertical=False, fuente=None):
983 return Texto(texto, magnitud, self, vertical, fuente)
984
985 - def obtener_grilla(self, ruta, columnas, filas):
986 return Grilla(ruta, columnas, filas)
987
988 - def cargar_sonido(self, ruta):
989 return self.clase_sonido(self.player, ruta)
990
991 - def cargar_musica(self, ruta):
992 return self.clase_musica(self.player, ruta)
993
994 - def deshabilitar_sonido(self, estado=True):
995 self.clase_sonido.deshabilitado = estado
996
997 - def deshabilitar_musica(self, estado=True):
998 self.clase_musica.deshabilitado = estado
999
1000 - def cargar_imagen(self, ruta):
1001 return Imagen(ruta)
1002
1003 - def obtener_lienzo(self):
1004 return Lienzo()
1005
1006 - def obtener_superficie(self, ancho, alto):
1007 return Superficie(ancho, alto)
1008