예제 #1
0
  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)
예제 #2
0
파일: Display.py 프로젝트: TheKathan/pi3d
    def __init__(self, tkwin=None, use_pygame=False):
        """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.
    *use_pygame*
      Flag to opt for pygame

    """
        if Display.INSTANCE is not None:
            assert ALLOW_MULTIPLE_DISPLAYS
            LOGGER.warning('A second instance of Display was created')
        else:
            Display.INSTANCE = self

        self.tkwin = tkwin
        if pi3d.PLATFORM == pi3d.PLATFORM_PI:
            use_pygame = False
        elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS:
            try:
                import pygame
                use_pygame = True  # for Windows
            except ImportError:
                LOGGER.warning('Do you need to install pygame?')
                use_pygame = False

        pi3d.USE_PYGAME = use_pygame

        self.sprites = []
        self.sprites_to_load = set()
        self.sprites_to_unload = set()

        self.tidy_needed = False
        self.textures_dict = {}
        self.vbufs_dict = {}
        self.ebufs_dict = {}
        self.last_shader = None
        self.last_textures = [
            None, None, None
        ]  # if more than 3 used this will break in Buffer.draw()
        self.external_mouse = None

        if (pi3d.PLATFORM != pi3d.PLATFORM_PI
                and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID
                and not pi3d.USE_PYGAME):
            self.event_list = []
            self.ev = xlib.XEvent()
        elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID:
            self.android = Pi3dApp()

        self.opengl = DisplayOpenGL()
        self.max_width, self.max_height = self.opengl.width, self.opengl.height
        self.first_time = True
        self.is_running = True
        self.lock = threading.RLock()

        LOGGER.debug(pi3d.STARTUP_MESSAGE)
예제 #3
0
  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)
예제 #4
0
    def __init__(self, tkwin=None):
        """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
        if Display.INSTANCE is not None:
            assert ALLOW_MULTIPLE_DISPLAYS
            LOGGER.warning('A second instance of Display was created')
        else:
            Display.INSTANCE = self

        self.tkwin = tkwin

        self.sprites = []
        self.sprites_to_load = set()
        self.sprites_to_unload = set()

        self.tidy_needed = False
        self.textures_dict = {}
        self.vbufs_dict = {}
        self.ebufs_dict = {}
        self.last_shader = None
        self.last_textures = [
            None, None, None
        ]  # if more than 3 used this will break in Buffer.draw()
        self.external_mouse = None

        if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID
                and PLATFORM != PLATFORM_WINDOWS):
            self.event_list = []
            self.ev = xlib.XEvent()
        elif PLATFORM == PLATFORM_ANDROID:
            self.android = Pi3dApp()

        self.opengl = DisplayOpenGL()
        self.max_width, self.max_height = self.opengl.width, self.opengl.height
        self.first_time = True
        self.is_running = True
        self.lock = threading.RLock()

        LOGGER.debug(STARTUP_MESSAGE)
예제 #5
0
  def __init__(self, tkwin=None, use_pygame=False):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.
    *use_pygame*
      Flag to opt for pygame

    """
    if Display.INSTANCE is not None:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin
    if pi3d.PLATFORM == pi3d.PLATFORM_PI:
      use_pygame = False
    elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS:
      try:
        import pygame
        use_pygame = True # for Windows
      except ImportError:
        LOGGER.warning('Do you need to install pygame?')
        use_pygame = False

    pi3d.USE_PYGAME = use_pygame

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.tidy_needed = False
    self.textures_dict = {}
    self.vbufs_dict = {}
    self.ebufs_dict = {}
    self.last_shader = None
    self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw()
    self.external_mouse = None
    self.offscreen_tex = False # used in Buffer.draw() to force reload of textures

    if (pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and
        not pi3d.USE_PYGAME):
      self.event_list = []
      self.ev = xlib.XEvent()
    elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID:
      self.android = Pi3dApp()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(pi3d.STARTUP_MESSAGE)
예제 #6
0
파일: Display.py 프로젝트: agarmart/pi3d
  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE is not None:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.tidy_needed = False
    self.textures_dict = {}
    self.vbufs_dict = {}
    self.ebufs_dict = {}
    self.last_shader = None
    self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw()
    self.external_mouse = None

    if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID and
        PLATFORM != PLATFORM_WINDOWS):
      self.event_list = []
      self.ev = xlib.XEvent()
    elif PLATFORM == PLATFORM_ANDROID:
      self.android = Pi3dApp()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)
예제 #7
0
  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.tidy_needed = False
    self.textures_dict = {}
    self.vbufs_dict = {}
    self.ebufs_dict = {}
    self.external_mouse = None

    if PLATFORM != PLATFORM_PI:
      self.event_list = []
      self.ev = xlib.XEvent()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)
예제 #8
0
class Display(object):
    """This is the central control object of the pi3d system and an instance
  must be created before some of the other class methods are called.
  """
    INSTANCE = None
    """The current unique instance of Display."""
    def __init__(self, tkwin=None, use_pygame=False):
        """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.
    *use_pygame*
      Flag to opt for pygame

    """
        if Display.INSTANCE is not None:
            assert ALLOW_MULTIPLE_DISPLAYS
            LOGGER.warning('A second instance of Display was created')
        else:
            Display.INSTANCE = self

        self.tkwin = tkwin
        if PLATFORM == PLATFORM_PI:
            use_pygame = False
        elif use_pygame or PLATFORM == PLATFORM_WINDOWS:
            try:
                import pygame
                use_pygame = True  # for Windows
            except ImportError:
                LOGGER.warning('Do you need to install pygame?')
                use_pygame = False

        pi3d.USE_PYGAME = use_pygame

        self.sprites = []
        self.sprites_to_load = set()
        self.sprites_to_unload = set()

        self.tidy_needed = False
        self.textures_dict = {}
        self.vbufs_dict = {}
        self.ebufs_dict = {}
        self.last_shader = None
        self.last_textures = [None for _ in range(8)
                              ]  # 8 is max no. texture2D on broadcom GPU
        self.external_mouse = None
        self.offscreen_tex = False  # used in Buffer.draw() to force reload of textures

        if (PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID
                and not pi3d.USE_PYGAME):
            self.event_list = []
            self.ev = xlib.XEvent()
        elif PLATFORM == PLATFORM_ANDROID:
            self.android = Pi3dApp()

        self.opengl = DisplayOpenGL()
        self.max_width, self.max_height = self.opengl.width, self.opengl.height
        self.first_time = True
        self.is_running = True
        self.lock = threading.RLock()

        LOGGER.debug(STARTUP_MESSAGE)

    def loop_running(self):
        """*loop_running* is the main event loop for the Display.

    Most pi3d code will look something like this::

      DISPLAY = Display.create()

      # Initialize objects and variables here.
      # ...

      while DISPLAY.loop_running():
        # Update the frame, using DISPLAY.time for the current time.
        # ...

        # Check for quit, then call DISPLAY.stop.
        if some_quit_condition():
          DISPLAY.stop()

    ``Display.loop_running()`` **must** be called on the main Python thread,
    or else white screens and program crashes are likely.

    The Display loop can run in two different modes - *free* or *framed*.

    If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when
    it finishes one frame, it immediately starts working on the next frame.

    If ``Display.frames_per_second`` is a positive number then the Display is
    *framed* - when the Display finishes one frame before the next frame_time,
    it waits till the next frame starts.

    A free Display gives the highest frame rate, but it will also consume more
    CPU, to the detriment of other threads or other programs.  There is also
    the significant drawback that the framerate will fluctuate as the numbers of
    CPU cycles consumed per loop, resulting in jerky motion and animations.

    A framed Display has a consistent if smaller number of frames, and also
    allows for potentially much smoother motion and animation.  The ability to
    throttle down the number of frames to conserve CPU cycles is essential
    for programs with other important threads like audio.

    ``Display.frames_per_second`` can be set at construction in
    ``Display.create`` or changed on-the-fly during the execution of the
    program.  If ``Display.frames_per_second`` is set too high, the Display
    doesn't attempt to "catch up" but simply runs freely.

    """
        if self.is_running:
            if self.first_time:
                self.time = time.time()
                self.first_time = False
            else:
                self._loop_end()  # Finish the previous loop.
            self._loop_begin()
        else:
            self._loop_end()
            self.destroy()

        return self.is_running

    def resize(self, x=0, y=0, w=0, h=0):
        """Reshape the window with the given coordinates."""
        if w <= 0:
            w = self.max_width
        if h <= 0:
            h = self.max_height
        self.width = w
        self.height = h

        self.left = x
        self.top = y
        self.right = x + w
        self.bottom = y + h
        self.opengl.resize(x, y, w, h, self.layer)

    def change_layer(self, layer=0):
        self.layer = layer
        self.opengl.change_layer(layer)

    def add_sprites(self, *sprites):
        """Add one or more sprites to this Display."""
        with self.lock:
            self.sprites_to_load.update(sprites)

    def remove_sprites(self, *sprites):
        """Remove one or more sprites from this Display."""
        with self.lock:
            self.sprites_to_unload.update(sprites)

    def stop(self):
        """Stop the Display."""
        self.is_running = False

    def destroy(self):
        """Destroy the current Display and reset Display.INSTANCE."""
        self._tidy()
        self.stop()
        try:
            self.opengl.destroy(self)
        except:
            pass
        if self.external_mouse:
            try:
                self.external_mouse.stop()
            except:
                pass
        try:
            self.mouse.stop()
        except:
            pass
        try:
            self.tkwin.destroy()
        except:
            pass
        Display.INSTANCE = None
        try:
            import pygame  # NB seems to be needed on some setups (64 bit anaconda windows!)
            pygame.quit()
        except:
            pass

    def clear(self):
        """Clear the Display."""
        # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0)
        opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    def set_background(self, r, g, b, alpha):
        """Set the Display background. **NB the actual drawing of the background
    happens during the rendering of the framebuffer by the shader so if no
    draw() is done by anything during each Display loop the screen will
    remain black** If you want to see just the background you will have to
    draw() something out of view (i.e. behind) the Camera.

    *r, g, b*
      Color values for the display
    *alpha*
      Opacity of the color.  An alpha of 0 means a transparent background,
      an alpha of 1 means full opaque.
    """
        opengles.glClearColor(c_float(r), c_float(g), c_float(b),
                              c_float(alpha))
        opengles.glColorMask(1, 1, 1, int(alpha < 1.0))
        # Switches off alpha blending with desktop (is there a bug in the driver?)

    def mouse_position(self):
        """The current mouse position as a tuple."""
        # TODO: add: Now deprecated in favor of pi3d.events
        if self.mouse:
            return self.mouse.position()
        elif self.tkwin:
            return self.tkwin.winfo_pointerxy()
        else:
            return -1, -1

    def _loop_begin(self):
        # TODO(rec):  check if the window was resized and resize it, removing
        # code from MegaStation to here.
        if pi3d.USE_PYGAME:
            import pygame  # although done in __init__ ...python namespaces aarg!!!
            if pygame.event.get(pygame.QUIT):
                self.destroy()
        elif PLATFORM != PLATFORM_PI and PLATFORM != PLATFORM_ANDROID:
            n = xlib.XEventsQueued(self.opengl.d, xlib.QueuedAfterFlush)
            for _ in range(n):
                xlib.XNextEvent(self.opengl.d, self.ev)
                if self.ev.type == KeyPress or self.ev.type == KeyRelease:
                    self.event_list.append(self.ev)
                elif self.ev.type == ClientMessage:
                    if (self.ev.xclient.data.l[0] ==
                            self.opengl.WM_DELETE_WINDOW.value):
                        self.destroy()
        self.clear()
        with self.lock:
            self.sprites_to_load, to_load = set(), self.sprites_to_load
            self.sprites.extend(to_load)
        self._for_each_sprite(lambda s: s.load_opengl(), to_load)

        if MARK_CAMERA_CLEAN_ON_EACH_LOOP:
            from pi3d.Camera import Camera
            #camera = Camera.instance()
            #if camera is not None:
            #  camera.was_moved = False
            cameras = Camera.all_instances()
            if cameras is not None:
                for camera in cameras:
                    camera.was_moved = False

        if self.tidy_needed:
            self._tidy()

    def _tidy(self):
        to_del = []
        for i in self.textures_dict:
            tex = self.textures_dict[i]
            if tex[1] == 1:
                opengles.glDeleteTextures(1, byref(tex[0]))
                to_del.append(i)
        for i in to_del:
            del self.textures_dict[i]
        to_del = []
        for i in self.vbufs_dict:
            vbuf = self.vbufs_dict[i]
            if vbuf[1] == 1:
                opengles.glDeleteBuffers(1, byref(vbuf[0]))
                to_del.append(i)
        for i in to_del:
            del self.vbufs_dict[i]
        to_del = []
        for i in self.ebufs_dict:
            ebuf = self.ebufs_dict[i]
            if ebuf[1] == 1:
                opengles.glDeleteBuffers(1, byref(ebuf[0]))
                to_del.append(i)
        for i in to_del:
            del self.ebufs_dict[i]
        self.tidy_needed = False

    def _loop_end(self):
        if pi3d.USE_PYGAME:
            import pygame
            pygame.event.clear()

        with self.lock:
            self.sprites_to_unload, to_unload = set(), self.sprites_to_unload
            if to_unload:
                self.sprites = [s for s in self.sprites if s not in to_unload]

        t = time.time()
        self._for_each_sprite(lambda s: s.repaint(t))

        self.swap_buffers()

        for sprite in to_unload:
            sprite.unload_opengl()

        if getattr(self, 'frames_per_second', 0):
            self.time += 1.0 / self.frames_per_second
            delta = self.time - time.time()
            if delta > 0:
                time.sleep(delta)

    def _for_each_sprite(self, function, sprites=None):
        if sprites is None:
            sprites = self.sprites
        for s in sprites:
            try:
                function(s)
            except:
                LOGGER.error(traceback.format_exc())
                if RAISE_EXCEPTIONS:
                    raise

    def __del__(self):
        try:
            self.destroy()
        except:
            pass  # often something has already been deleted leading to various None objects

    def swap_buffers(self):
        self.opengl.swap_buffers()
예제 #9
0
class Display(object):
  """This is the central control object of the pi3d system and an instance
  must be created before some of the other class methods are called.
  """
  INSTANCE = None
  """The current unique instance of Display."""

  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)

  def loop_running(self):
    """*loop_running* is the main event loop for the Display.

    Most pi3d code will look something like this::

      DISPLAY = Display.create()

      # Initialize objects and variables here.
      # ...

      while DISPLAY.loop_running():
        # Update the frame, using DISPLAY.time for the current time.
        # ...

        # Check for quit, then call DISPLAY.stop.
        if some_quit_condition():
          DISPLAY.stop()

    ``Display.loop_running()`` **must** be called on the main Python thread,
    or else white screens and program crashes are likely.

    The Display loop can run in two different modes - *free* or *framed*.

    If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when
    it finishes one frame, it immediately starts working on the next frame.

    If ``Display.frames_per_second`` is a positive number then the Display is
    *framed* - when the Display finishes one frame before the next frame_time,
    it waits till the next frame starts.

    A free Display gives the highest frame rate, but it will also consume more
    CPU, to the detriment of other threads or other programs.  There is also
    the significant drawback that the framerate will fluctuate as the numbers of
    CPU cycles consumed per loop, resulting in jerky motion and animations.

    A framed Display has a consistent if smaller number of frames, and also
    allows for potentially much smoother motion and animation.  The ability to
    throttle down the number of frames to conserve CPU cycles is essential
    for programs with other important threads like audio.

    ``Display.frames_per_second`` can be set at construction in
    ``Display.create`` or changed on-the-fly during the execution of the
    program.  If ``Display.frames_per_second`` is set too high, the Display
    doesn't attempt to "catch up" but simply runs freely.

    """
    if self.is_running:
      if self.first_time:
        self.time = time.time()
        self.first_time = False
      else:
        self._loop_end()  # Finish the previous loop.
      self._loop_begin()
    else:
      self._loop_end()
      self.destroy()

    return self.is_running

  def resize(self, x=0, y=0, w=0, h=0):
    """Reshape the window with the given coordinates."""
    if w <= 0:
      w = display.max_width
    if h <= 0:
      h = display.max_height
    self.width = w
    self.height = h

    self.left = x
    self.top = y
    self.right = x + w
    self.bottom = y + h
    self.opengl.resize(x, y, w, h)

  def add_sprites(self, *sprites):
    """Add one or more sprites to this Display."""
    with self.lock:
      self.sprites_to_load.update(sprites)

  def remove_sprites(self, *sprites):
    """Remove one or more sprites from this Display."""
    with self.lock:
      self.sprites_to_unload.update(sprites)

  def stop(self):
    """Stop the Display."""
    self.is_running = False

  def destroy(self):
    """Destroy the current Display and reset Display.INSTANCE."""
    self.stop()
    try:
      self.opengl.destroy()
    except:
      pass
    try:
      self.mouse.stop()
    except:
      pass
    try:
      self.tkwin.destroy()
    except:
      pass
    Display.INSTANCE = None

  def clear(self):
    """Clear the Display."""
    # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0)
    opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

  def set_background(self, r, g, b, alpha):
    """Set the Display background. **NB the actual drawing of the background
    happens during the rendering of the framebuffer by the shader so if no
    draw() is done by anything during each Display loop the screen will
    remain black** If you want to see just the background you will have to
    draw() something out of view (i.e. behind) the Camera.

    *r, g, b*
      Color values for the display
    *alpha*
      Opacity of the color.  An alpha of 0 means a transparent background,
      an alpha of 1 means full opaque.
    """
    opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha))
    opengles.glColorMask(1, 1, 1, int(alpha < 1.0))
    # Switches off alpha blending with desktop (is there a bug in the driver?)

  def mouse_position(self):
    """The current mouse position as a tuple."""
    # TODO: add: Now deprecated in favor of pi3d.events
    if self.mouse:
      return self.mouse.position()
    elif self.tkwin:
      return self.tkwin.winfo_pointerxy()
    else:
      return -1, -1

  def _loop_begin(self):
    # TODO(rec):  check if the window was resized and resize it, removing
    # code from MegaStation to here.
    self.clear()
    with self.lock:
      self.sprites_to_load, to_load = set(), self.sprites_to_load
      self.sprites.extend(to_load)
    self._for_each_sprite(lambda s: s.load_opengl(), to_load)

    if MARK_CAMERA_CLEAN_ON_EACH_LOOP:
      from pi3d.Camera import Camera
      camera = Camera.instance()
      if camera:
        camera.was_moved = False

  def _loop_end(self):
    with self.lock:
      self.sprites_to_unload, to_unload = set(), self.sprites_to_unload
      if to_unload:
        self.sprites = [s for s in self.sprites if s not in to_unload]

    t = time.time()
    self._for_each_sprite(lambda s: s.repaint(t))

    self.swap_buffers()

    for sprite in to_unload:
      sprite.unload_opengl()

    if getattr(self, 'frames_per_second', 0):
      self.time += 1.0 / self.frames_per_second
      delta = self.time - time.time()
      if delta > 0:
        time.sleep(delta)

  def _for_each_sprite(self, function, sprites=None):
    if sprites is None:
      sprites = self.sprites
    for s in sprites:
      try:
        function(s)
      except:
        LOGGER.error(traceback.format_exc())
        if RAISE_EXCEPTIONS:
          raise

  def __del__(self):
    self.destroy()

  def swap_buffers(self):
    self.opengl.swap_buffers()
예제 #10
0
class Display(object):
  """This is the central control object of the pi3d system and an instance
  must be created before some of the other class methods are called.
  """
  INSTANCE = None
  """The current unique instance of Display."""

  def __init__(self, tkwin=None):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.

    """
    if Display.INSTANCE:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(STARTUP_MESSAGE)

  def loop_running(self):
    """*loop_running* is the main event loop for the Display.

    Most pi3d code will look something like this::

      DISPLAY = Display.create()

      # Initialize objects and variables here.
      # ...

      while DISPLAY.loop_running():
        # Update the frame, using DISPLAY.time for the current time.
        # ...

        # Check for quit, then call DISPLAY.stop.
        if some_quit_condition():
          DISPLAY.stop()

    ``Display.loop_running()`` **must** be called on the main Python thread,
    or else white screens and program crashes are likely.

    The Display loop can run in two different modes - *free* or *framed*.

    If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when
    it finishes one frame, it immediately starts working on the next frame.

    If ``Display.frames_per_second`` is a positive number then the Display is
    *framed* - when the Display finishes one frame before the next frame_time,
    it waits till the next frame starts.

    A free Display gives the highest frame rate, but it will also consume more
    CPU, to the detriment of other threads or other programs.  There is also
    the significant drawback that the framerate will fluctuate as the numbers of
    CPU cycles consumed per loop, resulting in jerky motion and animations.

    A framed Display has a consistent if smaller number of frames, and also
    allows for potentially much smoother motion and animation.  The ability to
    throttle down the number of frames to conserve CPU cycles is essential
    for programs with other important threads like audio.

    ``Display.frames_per_second`` can be set at construction in
    ``Display.create`` or changed on-the-fly during the execution of the
    program.  If ``Display.frames_per_second`` is set too high, the Display
    doesn't attempt to "catch up" but simply runs freely.

    """
    if self.is_running:
      if self.first_time:
        self.time = time.time()
        self.first_time = False
      else:
        self._loop_end()  # Finish the previous loop.
      self._loop_begin()
    else:
      self._loop_end()
      self.destroy()

    return self.is_running

  def resize(self, x=0, y=0, w=0, h=0):
    """Reshape the window with the given coordinates."""
    if w <= 0:
      w = display.max_width
    if h <= 0:
      h = display.max_height
    self.width = w
    self.height = h

    self.left = x
    self.top = y
    self.right = x + w
    self.bottom = y + h
    self.opengl.resize(x, y, w, h)

  def add_sprites(self, *sprites):
    """Add one or more sprites to this Display."""
    with self.lock:
      self.sprites_to_load.update(sprites)

  def remove_sprites(self, *sprites):
    """Remove one or more sprites from this Display."""
    with self.lock:
      self.sprites_to_unload.update(sprites)

  def stop(self):
    """Stop the Display."""
    self.is_running = False

  def destroy(self):
    """Destroy the current Display and reset Display.INSTANCE."""
    self.stop()
    try:
      self.opengl.destroy()
    except:
      pass
    try:
      self.mouse.stop()
    except:
      pass
    try:
      self.tkwin.destroy()
    except:
      pass
    Display.INSTANCE = None

  def clear(self):
    """Clear the Display."""
    # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0)
    opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

  def set_background(self, r, g, b, alpha):
    """Set the Display background. **NB the actual drawing of the background
    happens during the rendering of the framebuffer by the shader so if no
    draw() is done by anything during each Display loop the screen will
    remain black** If you want to see just the background you will have to
    draw() something out of view (i.e. behind) the Camera.

    *r, g, b*
      Color values for the display
    *alpha*
      Opacity of the color.  An alpha of 0 means a transparent background,
      an alpha of 1 means full opaque.
    """
    opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha))
    opengles.glColorMask(1, 1, 1, int(alpha < 1.0))
    # Switches off alpha blending with desktop (is there a bug in the driver?)

  def mouse_position(self):
    """The current mouse position as a tuple."""
    # TODO: add: Now deprecated in favor of pi3d.events
    if self.mouse:
      return self.mouse.position()
    elif self.tkwin:
      return self.tkwin.winfo_pointerxy()
    else:
      return -1, -1

  def _loop_begin(self):
    # TODO(rec):  check if the window was resized and resize it, removing
    # code from MegaStation to here.
    self.clear()
    with self.lock:
      self.sprites_to_load, to_load = set(), self.sprites_to_load
      self.sprites.extend(to_load)
    self._for_each_sprite(lambda s: s.load_opengl(), to_load)

    if MARK_CAMERA_CLEAN_ON_EACH_LOOP:
      from pi3d.Camera import Camera
      camera = Camera.instance()
      if camera:
        camera.was_moved = False

  def _loop_end(self):
    with self.lock:
      self.sprites_to_unload, to_unload = set(), self.sprites_to_unload
      if to_unload:
        self.sprites = [s for s in self.sprites if s not in to_unload]

    t = time.time()
    self._for_each_sprite(lambda s: s.repaint(t))

    self.swap_buffers()

    for sprite in to_unload:
      sprite.unload_opengl()

    if getattr(self, 'frames_per_second', 0):
      self.time += 1.0 / self.frames_per_second
      delta = self.time - time.time()
      if delta > 0:
        time.sleep(delta)

  def _for_each_sprite(self, function, sprites=None):
    if sprites is None:
      sprites = self.sprites
    for s in sprites:
      try:
        function(s)
      except:
        LOGGER.error(traceback.format_exc())
        if RAISE_EXCEPTIONS:
          raise

  def __del__(self):
    self.destroy()

  def swap_buffers(self):
    self.opengl.swap_buffers()
예제 #11
0
파일: Display.py 프로젝트: bpow/pi3d
class Display(object):
  """This is the central control object of the pi3d system and an instance
  must be created before some of the other class methods are called.
  """
  INSTANCE = None
  """The current unique instance of Display."""

  def __init__(self, tkwin=None, use_pygame=False):
    """
    Constructs a raw Display.  Use pi3d.Display.create to create an initialized
    Display.

    *tkwin*
      An optional Tk window.
    *use_pygame*
      Flag to opt for pygame

    """
    if Display.INSTANCE is not None:
      assert ALLOW_MULTIPLE_DISPLAYS
      LOGGER.warning('A second instance of Display was created')
    else:
      Display.INSTANCE = self

    self.tkwin = tkwin
    if pi3d.PLATFORM == pi3d.PLATFORM_PI:
      use_pygame = False
    elif use_pygame or pi3d.PLATFORM == pi3d.PLATFORM_WINDOWS:
      try:
        import pygame
        use_pygame = True # for Windows
      except ImportError:
        LOGGER.warning('Do you need to install pygame?')
        use_pygame = False

    pi3d.USE_PYGAME = use_pygame

    self.sprites = []
    self.sprites_to_load = set()
    self.sprites_to_unload = set()

    self.tidy_needed = False
    self.textures_dict = {}
    self.vbufs_dict = {}
    self.ebufs_dict = {}
    self.last_shader = None
    self.last_textures = [None, None, None] # if more than 3 used this will break in Buffer.draw()
    self.external_mouse = None

    if (pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and
        not pi3d.USE_PYGAME):
      self.event_list = []
      self.ev = xlib.XEvent()
    elif pi3d.PLATFORM == pi3d.PLATFORM_ANDROID:
      self.android = Pi3dApp()

    self.opengl = DisplayOpenGL()
    self.max_width, self.max_height = self.opengl.width, self.opengl.height
    self.first_time = True
    self.is_running = True
    self.lock = threading.RLock()

    LOGGER.debug(pi3d.STARTUP_MESSAGE)

  def loop_running(self):
    """*loop_running* is the main event loop for the Display.

    Most pi3d code will look something like this::

      DISPLAY = Display.create()

      # Initialize objects and variables here.
      # ...

      while DISPLAY.loop_running():
        # Update the frame, using DISPLAY.time for the current time.
        # ...

        # Check for quit, then call DISPLAY.stop.
        if some_quit_condition():
          DISPLAY.stop()

    ``Display.loop_running()`` **must** be called on the main Python thread,
    or else white screens and program crashes are likely.

    The Display loop can run in two different modes - *free* or *framed*.

    If ``DISPLAY.frames_per_second`` is empty or 0 then the loop runs *free* - when
    it finishes one frame, it immediately starts working on the next frame.

    If ``Display.frames_per_second`` is a positive number then the Display is
    *framed* - when the Display finishes one frame before the next frame_time,
    it waits till the next frame starts.

    A free Display gives the highest frame rate, but it will also consume more
    CPU, to the detriment of other threads or other programs.  There is also
    the significant drawback that the framerate will fluctuate as the numbers of
    CPU cycles consumed per loop, resulting in jerky motion and animations.

    A framed Display has a consistent if smaller number of frames, and also
    allows for potentially much smoother motion and animation.  The ability to
    throttle down the number of frames to conserve CPU cycles is essential
    for programs with other important threads like audio.

    ``Display.frames_per_second`` can be set at construction in
    ``Display.create`` or changed on-the-fly during the execution of the
    program.  If ``Display.frames_per_second`` is set too high, the Display
    doesn't attempt to "catch up" but simply runs freely.

    """
    if self.is_running:
      if self.first_time:
        self.time = time.time()
        self.first_time = False
      else:
        self._loop_end()  # Finish the previous loop.
      self._loop_begin()
    else:
      self._loop_end()
      self.destroy()

    return self.is_running

  def resize(self, x=0, y=0, w=0, h=0):
    """Reshape the window with the given coordinates."""
    if w <= 0:
      w = self.max_width
    if h <= 0:
      h = self.max_height
    self.width = w
    self.height = h

    self.left = x
    self.top = y
    self.right = x + w
    self.bottom = y + h
    self.opengl.resize(x, y, w, h)

  def add_sprites(self, *sprites):
    """Add one or more sprites to this Display."""
    with self.lock:
      self.sprites_to_load.update(sprites)

  def remove_sprites(self, *sprites):
    """Remove one or more sprites from this Display."""
    with self.lock:
      self.sprites_to_unload.update(sprites)

  def stop(self):
    """Stop the Display."""
    self.is_running = False

  def destroy(self):
    """Destroy the current Display and reset Display.INSTANCE."""
    self._tidy()
    self.stop()
    try:
      self.opengl.destroy(self)
    except:
      pass
    if self.external_mouse:
      try:
        self.external_mouse.stop()
      except:
        pass_
    try:
      self.mouse.stop()
    except:
      pass
    try:
      self.tkwin.destroy()
    except:
      pass
    Display.INSTANCE = None
    try:
      import pygame # NB seems to be needed on some setups (64 bit anaconda windows!)
      pygame.quit()
    except:
      pass

  def clear(self):
    """Clear the Display."""
    # opengles.glBindFramebuffer(GL_FRAMEBUFFER,0)
    pi3d.opengles.glClear(pi3d.GL_COLOR_BUFFER_BIT | pi3d.GL_DEPTH_BUFFER_BIT)

  def set_background(self, r, g, b, alpha):
    """Set the Display background. **NB the actual drawing of the background
    happens during the rendering of the framebuffer by the shader so if no
    draw() is done by anything during each Display loop the screen will
    remain black** If you want to see just the background you will have to
    draw() something out of view (i.e. behind) the Camera.

    *r, g, b*
      Color values for the display
    *alpha*
      Opacity of the color.  An alpha of 0 means a transparent background,
      an alpha of 1 means full opaque.
    """
    pi3d.opengles.glClearColor(c_float(r), c_float(g), c_float(b), c_float(alpha))
    pi3d.opengles.glColorMask(1, 1, 1, int(alpha < 1.0))
    # Switches off alpha blending with desktop (is there a bug in the driver?)

  def mouse_position(self):
    """The current mouse position as a tuple."""
    # TODO: add: Now deprecated in favor of pi3d.events
    if self.mouse:
      return self.mouse.position()
    elif self.tkwin:
      return self.tkwin.winfo_pointerxy()
    else:
      return -1, -1

  def _loop_begin(self):
    # TODO(rec):  check if the window was resized and resize it, removing
    # code from MegaStation to here.
    if pi3d.USE_PYGAME:
      import pygame # although done in __init__ ...python namespaces aarg!!!
      if pygame.event.get(pygame.QUIT):
        self.destroy()
    elif pi3d.PLATFORM != pi3d.PLATFORM_PI and pi3d.PLATFORM != pi3d.PLATFORM_ANDROID:
      n = xlib.XEventsQueued(self.opengl.d, xlib.QueuedAfterFlush)
      for i in range(n):
        if xlib.XCheckMaskEvent(self.opengl.d, KeyPressMask, self.ev):
          self.event_list.append(self.ev)
        else:
          xlib.XNextEvent(self.opengl.d, self.ev)
          if self.ev.type == ClientMessage:
            if (self.ev.xclient.data.l[0] == self.opengl.WM_DELETE_WINDOW.value):
              self.destroy()
    self.clear()
    with self.lock:
      self.sprites_to_load, to_load = set(), self.sprites_to_load
      self.sprites.extend(to_load)
    self._for_each_sprite(lambda s: s.load_opengl(), to_load)

    if MARK_CAMERA_CLEAN_ON_EACH_LOOP:
      from pi3d.Camera import Camera
      camera = Camera.instance()
      if camera is not None:
        camera.was_moved = False

    if self.tidy_needed:
      self._tidy()

  def _tidy(self):
    to_del = []
    for i in self.textures_dict:
      tex = self.textures_dict[i]
      LOGGER.debug('tex0=%s tex1=%s',tex[0], tex[1])
      if tex[1] == 1:
        pi3d.opengles.glDeleteTextures(1, byref(tex[0]))
        to_del.append(i)
    for i in to_del:
      del self.textures_dict[i]
    to_del = []
    for i in self.vbufs_dict:
      vbuf = self.vbufs_dict[i]
      if vbuf[1] == 1:
        pi3d.opengles.glDeleteBuffers(1, byref(vbuf[0]))
        to_del.append(i)
    for i in to_del:
      del self.vbufs_dict[i]
    to_del = []
    for i in self.ebufs_dict:
      ebuf = self.ebufs_dict[i]
      if ebuf[1] == 1:
        pi3d.opengles.glDeleteBuffers(1, byref(ebuf[0]))
        to_del.append(i)
    for i in to_del:
      del self.ebufs_dict[i]
    self.tidy_needed = False

  def _loop_end(self):
    if pi3d.USE_PYGAME:
      import pygame
      pygame.event.clear()
      
    with self.lock:
      self.sprites_to_unload, to_unload = set(), self.sprites_to_unload
      if to_unload:
        self.sprites = [s for s in self.sprites if s not in to_unload]

    t = time.time()
    self._for_each_sprite(lambda s: s.repaint(t))

    self.swap_buffers()

    for sprite in to_unload:
      sprite.unload_opengl()

    if getattr(self, 'frames_per_second', 0):
      self.time += 1.0 / self.frames_per_second
      delta = self.time - time.time()
      if delta > 0:
        time.sleep(delta)

  def _for_each_sprite(self, function, sprites=None):
    if sprites is None:
      sprites = self.sprites
    for s in sprites:
      try:
        function(s)
      except:
        LOGGER.error(traceback.format_exc())
        if RAISE_EXCEPTIONS:
          raise

  def __del__(self):
    self.destroy()

  def swap_buffers(self):
    self.opengl.swap_buffers()