shenjack
d84b490b99
Add | more formatter and some more Fix | type mis match sync pyglet Enhance | logger with Template add lib-not-dr as requirement sync pyglet sync pyglet Add | add lto=yes to nuitka_build just incase sync pyglet sync lib_not_dr Remove | external requirement lib-not-dr some logger sync lib-not-dr sync pyglet sync lib-not-dr sync lib-not-dr sync pyglet sync pyglet Fix | console thread been block Update DR rs and DR sdk sync lib not dr sync lib-not-dr sync lib-not-dr sync pyglet and lib-not-dr sync pyglet 0.1.8 sync lib not dr logger almost done? almost! sync pyglet (clicpboard support!) sync lib not dr sync lib not dr color code and sync pyglet do not show memory and progress building localy sync pyglet synclibs
1930 lines
66 KiB
Python
1930 lines
66 KiB
Python
"""Windowing and user-interface events.
|
|
|
|
This module allows applications to create and display windows with an
|
|
OpenGL context. Windows can be created with a variety of border styles
|
|
or set fullscreen.
|
|
|
|
You can register event handlers for keyboard, mouse and window events.
|
|
For games and kiosks you can also restrict the input to your windows,
|
|
for example disabling users from switching away from the application
|
|
with certain key combinations or capturing and hiding the mouse.
|
|
|
|
Getting started
|
|
---------------
|
|
|
|
Call the Window constructor to create a new window::
|
|
|
|
from pyglet.window import Window
|
|
win = Window(width=960, height=540)
|
|
|
|
Attach your own event handlers::
|
|
|
|
@win.event
|
|
def on_key_press(symbol, modifiers):
|
|
# ... handle this event ...
|
|
|
|
Place drawing code for the window within the `Window.on_draw` event handler::
|
|
|
|
@win.event
|
|
def on_draw():
|
|
# ... drawing code ...
|
|
|
|
Call `pyglet.app.run` to enter the main event loop (by default, this
|
|
returns when all open windows are closed)::
|
|
|
|
from pyglet import app
|
|
app.run()
|
|
|
|
Creating a game window
|
|
----------------------
|
|
|
|
Use :py:meth:`~pyglet.window.Window.set_exclusive_mouse` to hide the mouse
|
|
cursor and receive relative mouse movement events. Specify ``fullscreen=True``
|
|
as a keyword argument to the :py:class:`~pyglet.window.Window` constructor to
|
|
render to the entire screen rather than opening a window::
|
|
|
|
win = Window(fullscreen=True)
|
|
win.set_exclusive_mouse()
|
|
|
|
Working with multiple screens
|
|
-----------------------------
|
|
|
|
By default, fullscreen windows are opened on the primary display (typically
|
|
set by the user in their operating system settings). You can retrieve a list
|
|
of attached screens and select one manually if you prefer. This is useful for
|
|
opening a fullscreen window on each screen::
|
|
|
|
display = pyglet.canvas.get_display()
|
|
screens = display.get_screens()
|
|
windows = []
|
|
for screen in screens:
|
|
windows.append(window.Window(fullscreen=True, screen=screen))
|
|
|
|
Specifying a screen has no effect if the window is not fullscreen.
|
|
|
|
Specifying the OpenGL context properties
|
|
----------------------------------------
|
|
|
|
Each window has its own context which is created when the window is created.
|
|
You can specify the properties of the context before it is created
|
|
by creating a "template" configuration::
|
|
|
|
from pyglet import gl
|
|
# Create template config
|
|
config = gl.Config()
|
|
config.stencil_size = 8
|
|
config.aux_buffers = 4
|
|
# Create a window using this config
|
|
win = window.Window(config=config)
|
|
|
|
To determine if a given configuration is supported, query the screen (see
|
|
above, "Working with multiple screens")::
|
|
|
|
configs = screen.get_matching_configs(config)
|
|
if not configs:
|
|
# ... config is not supported
|
|
else:
|
|
win = window.Window(config=configs[0])
|
|
|
|
"""
|
|
|
|
import sys
|
|
from typing import Tuple
|
|
|
|
import pyglet
|
|
import pyglet.window.key
|
|
import pyglet.window.mouse
|
|
|
|
from pyglet import gl
|
|
from pyglet.math import Mat4
|
|
from pyglet.event import EventDispatcher
|
|
from pyglet.window import key, event
|
|
from pyglet.graphics import shader
|
|
|
|
|
|
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
|
|
|
|
|
class WindowException(Exception):
|
|
"""The root exception for all window-related errors."""
|
|
pass
|
|
|
|
|
|
class NoSuchDisplayException(WindowException):
|
|
"""An exception indicating the requested display is not available."""
|
|
pass
|
|
|
|
|
|
class NoSuchConfigException(WindowException):
|
|
"""An exception indicating the requested configuration is not
|
|
available."""
|
|
pass
|
|
|
|
|
|
class NoSuchScreenModeException(WindowException):
|
|
"""An exception indicating the requested screen resolution could not be
|
|
met."""
|
|
pass
|
|
|
|
|
|
class MouseCursorException(WindowException):
|
|
"""The root exception for all mouse cursor-related errors."""
|
|
pass
|
|
|
|
|
|
class MouseCursor:
|
|
"""An abstract mouse cursor."""
|
|
|
|
#: Indicates if the cursor is drawn
|
|
#: using OpenGL, or natively.
|
|
gl_drawable = True
|
|
hw_drawable = False
|
|
|
|
def draw(self, x, y):
|
|
"""Abstract render method.
|
|
|
|
The cursor should be drawn with the "hot" spot at the given
|
|
coordinates. The projection is set to the pyglet default (i.e.,
|
|
orthographic in window-space), however no other aspects of the
|
|
state can be assumed.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
X coordinate of the mouse pointer's hot spot.
|
|
`y` : int
|
|
Y coordinate of the mouse pointer's hot spot.
|
|
|
|
"""
|
|
pass
|
|
|
|
|
|
class DefaultMouseCursor(MouseCursor):
|
|
"""The default mouse cursor set by the operating system."""
|
|
gl_drawable = False
|
|
hw_drawable = True
|
|
|
|
|
|
class ImageMouseCursor(MouseCursor):
|
|
"""A user-defined mouse cursor created from an image.
|
|
|
|
Use this class to create your own mouse cursors and assign them
|
|
to windows. Cursors can be drawn by OpenGL, or optionally passed
|
|
to the OS to render natively. There are no restrictions on cursors
|
|
drawn by OpenGL, but natively rendered cursors may have some
|
|
platform limitations (such as color depth, or size). In general,
|
|
reasonably sized cursors will render correctly
|
|
"""
|
|
def __init__(self, image, hot_x=0, hot_y=0, acceleration=False):
|
|
"""Create a mouse cursor from an image.
|
|
|
|
:Parameters:
|
|
`image` : `pyglet.image.AbstractImage`
|
|
Image to use for the mouse cursor. It must have a
|
|
valid ``texture`` attribute.
|
|
`hot_x` : int
|
|
X coordinate of the "hot" spot in the image relative to the
|
|
image's anchor. May be clamped to the maximum image width
|
|
if ``acceleration=True``.
|
|
`hot_y` : int
|
|
Y coordinate of the "hot" spot in the image, relative to the
|
|
image's anchor. May be clamped to the maximum image height
|
|
if ``acceleration=True``.
|
|
`acceleration` : int
|
|
If True, draw the cursor natively instead of usign OpenGL.
|
|
The image may be downsampled or color reduced to fit the
|
|
platform limitations.
|
|
"""
|
|
self.texture = image.get_texture()
|
|
self.hot_x = hot_x
|
|
self.hot_y = hot_y
|
|
|
|
self.gl_drawable = not acceleration
|
|
self.hw_drawable = acceleration
|
|
|
|
def draw(self, x, y):
|
|
gl.glEnable(gl.GL_BLEND)
|
|
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
|
|
self.texture.blit(x - self.hot_x, y - self.hot_y, 0)
|
|
gl.glDisable(gl.GL_BLEND)
|
|
|
|
|
|
def _PlatformEventHandler(data):
|
|
"""Decorator for platform event handlers.
|
|
|
|
Apply giving the platform-specific data needed by the window to associate
|
|
the method with an event. See platform-specific subclasses of this
|
|
decorator for examples.
|
|
|
|
The following attributes are set on the function, which is returned
|
|
otherwise unchanged:
|
|
|
|
_platform_event
|
|
True
|
|
_platform_event_data
|
|
List of data applied to the function (permitting multiple decorators
|
|
on the same method).
|
|
"""
|
|
def _event_wrapper(f):
|
|
f._platform_event = True
|
|
if not hasattr(f, '_platform_event_data'):
|
|
f._platform_event_data = []
|
|
f._platform_event_data.append(data)
|
|
return f
|
|
|
|
return _event_wrapper
|
|
|
|
|
|
def _ViewEventHandler(f):
|
|
f._view = True
|
|
return f
|
|
|
|
|
|
class _WindowMetaclass(type):
|
|
"""Sets the _platform_event_names class variable on the window
|
|
subclass.
|
|
"""
|
|
|
|
def __init__(cls, name, bases, dict):
|
|
cls._platform_event_names = set()
|
|
for base in bases:
|
|
if hasattr(base, '_platform_event_names'):
|
|
cls._platform_event_names.update(base._platform_event_names)
|
|
for name, func in dict.items():
|
|
if hasattr(func, '_platform_event'):
|
|
cls._platform_event_names.add(name)
|
|
super(_WindowMetaclass, cls).__init__(name, bases, dict)
|
|
|
|
|
|
class BaseWindow(EventDispatcher, metaclass=_WindowMetaclass):
|
|
"""Platform-independent application window.
|
|
|
|
A window is a "heavyweight" object occupying operating system resources.
|
|
The "client" or "content" area of a window is filled entirely with
|
|
an OpenGL viewport. Applications have no access to operating system
|
|
widgets or controls; all rendering must be done via OpenGL.
|
|
|
|
Windows may appear as floating regions or can be set to fill an entire
|
|
screen (fullscreen). When floating, windows may appear borderless or
|
|
decorated with a platform-specific frame (including, for example, the
|
|
title bar, minimize and close buttons, resize handles, and so on).
|
|
|
|
While it is possible to set the location of a window, it is recommended
|
|
that applications allow the platform to place it according to local
|
|
conventions. This will ensure it is not obscured by other windows,
|
|
and appears on an appropriate screen for the user.
|
|
|
|
To render into a window, you must first call its :py:meth:`.switch_to`
|
|
method to make it the active OpenGL context. If you use only one
|
|
window in your application, you can skip this step as it will always
|
|
be the active context.
|
|
"""
|
|
|
|
# Filled in by metaclass with the names of all methods on this (sub)class
|
|
# that are platform event handlers.
|
|
_platform_event_names = set()
|
|
|
|
#: The default window style.
|
|
WINDOW_STYLE_DEFAULT = None
|
|
#: The window style for pop-up dialogs.
|
|
WINDOW_STYLE_DIALOG = 'dialog'
|
|
#: The window style for tool windows.
|
|
WINDOW_STYLE_TOOL = 'tool'
|
|
#: A window style without any decoration.
|
|
WINDOW_STYLE_BORDERLESS = 'borderless'
|
|
#: A window style for transparent, interactable windows
|
|
WINDOW_STYLE_TRANSPARENT = 'transparent'
|
|
#: A window style for transparent, topmost, click-through-able overlays
|
|
WINDOW_STYLE_OVERLAY = 'overlay'
|
|
|
|
#: The default mouse cursor.
|
|
CURSOR_DEFAULT = None
|
|
#: A crosshair mouse cursor.
|
|
CURSOR_CROSSHAIR = 'crosshair'
|
|
#: A pointing hand mouse cursor.
|
|
CURSOR_HAND = 'hand'
|
|
#: A "help" mouse cursor; typically a question mark and an arrow.
|
|
CURSOR_HELP = 'help'
|
|
#: A mouse cursor indicating that the selected operation is not permitted.
|
|
CURSOR_NO = 'no'
|
|
#: A mouse cursor indicating the element can be resized.
|
|
CURSOR_SIZE = 'size'
|
|
#: A mouse cursor indicating the element can be resized from the top
|
|
#: border.
|
|
CURSOR_SIZE_UP = 'size_up'
|
|
#: A mouse cursor indicating the element can be resized from the
|
|
#: upper-right corner.
|
|
CURSOR_SIZE_UP_RIGHT = 'size_up_right'
|
|
#: A mouse cursor indicating the element can be resized from the right
|
|
#: border.
|
|
CURSOR_SIZE_RIGHT = 'size_right'
|
|
#: A mouse cursor indicating the element can be resized from the lower-right
|
|
#: corner.
|
|
CURSOR_SIZE_DOWN_RIGHT = 'size_down_right'
|
|
#: A mouse cursor indicating the element can be resized from the bottom
|
|
#: border.
|
|
CURSOR_SIZE_DOWN = 'size_down'
|
|
#: A mouse cursor indicating the element can be resized from the lower-left
|
|
#: corner.
|
|
CURSOR_SIZE_DOWN_LEFT = 'size_down_left'
|
|
#: A mouse cursor indicating the element can be resized from the left
|
|
#: border.
|
|
CURSOR_SIZE_LEFT = 'size_left'
|
|
#: A mouse cursor indicating the element can be resized from the upper-left
|
|
#: corner.
|
|
CURSOR_SIZE_UP_LEFT = 'size_up_left'
|
|
#: A mouse cursor indicating the element can be resized vertically.
|
|
CURSOR_SIZE_UP_DOWN = 'size_up_down'
|
|
#: A mouse cursor indicating the element can be resized horizontally.
|
|
CURSOR_SIZE_LEFT_RIGHT = 'size_left_right'
|
|
#: A text input mouse cursor (I-beam).
|
|
CURSOR_TEXT = 'text'
|
|
#: A "wait" mouse cursor; typically an hourglass or watch.
|
|
CURSOR_WAIT = 'wait'
|
|
#: The "wait" mouse cursor combined with an arrow.
|
|
CURSOR_WAIT_ARROW = 'wait_arrow'
|
|
|
|
#: True if the user has attempted to close the window.
|
|
#:
|
|
#: :deprecated: Windows are closed immediately by the default
|
|
#: :py:meth:`~pyglet.window.Window.on_close` handler when `pyglet.app.event_loop` is being
|
|
#: used.
|
|
has_exit = False
|
|
|
|
#: Window display contents validity. The :py:mod:`pyglet.app` event loop
|
|
#: examines every window each iteration and only dispatches the :py:meth:`~pyglet.window.Window.on_draw`
|
|
#: event to windows that have `invalid` set. By default, windows always
|
|
#: have `invalid` set to ``True``.
|
|
#:
|
|
#: You can prevent redundant redraws by setting this variable to ``False``
|
|
#: in the window's :py:meth:`~pyglet.window.Window.on_draw` handler, and setting it to True again in
|
|
#: response to any events that actually do require a window contents
|
|
#: update.
|
|
#:
|
|
#: :type: bool
|
|
#: .. versionadded:: 1.1
|
|
invalid = True
|
|
|
|
# Instance variables accessible only via properties
|
|
_width = None
|
|
_height = None
|
|
_caption = None
|
|
_resizable = False
|
|
_style = WINDOW_STYLE_DEFAULT
|
|
_fullscreen = False
|
|
_visible = False
|
|
_vsync = False
|
|
_file_drops = False
|
|
_screen = None
|
|
_config = None
|
|
_context = None
|
|
_projection_matrix = pyglet.math.Mat4()
|
|
_view_matrix = pyglet.math.Mat4()
|
|
_viewport = 0, 0, 0, 0
|
|
|
|
# Used to restore window size and position after fullscreen
|
|
_windowed_size = None
|
|
_windowed_location = None
|
|
|
|
_minimum_size = None
|
|
_maximum_size = None
|
|
|
|
_keyboard_exclusive = False
|
|
|
|
# Subclasses should update these after relevant events
|
|
_mouse_cursor = DefaultMouseCursor()
|
|
_mouse_x = 0
|
|
_mouse_y = 0
|
|
_mouse_visible = True
|
|
_mouse_exclusive = False
|
|
_mouse_in_window = False
|
|
|
|
_event_queue = None
|
|
_enable_event_queue = True # overridden by EventLoop.
|
|
_allow_dispatch_event = False # controlled by dispatch_events stack frame
|
|
|
|
# Class attributes
|
|
_default_width = 960
|
|
_default_height = 540
|
|
|
|
# Create a default ShaderProgram, so the Window instance can
|
|
# update the `WindowBlock` UBO shared by all default shaders.
|
|
_default_vertex_source = """#version 150 core
|
|
in vec4 position;
|
|
|
|
uniform WindowBlock
|
|
{
|
|
mat4 projection;
|
|
mat4 view;
|
|
} window;
|
|
|
|
void main()
|
|
{
|
|
gl_Position = window.projection * window.view * position;
|
|
}
|
|
"""
|
|
_default_fragment_source = """#version 150 core
|
|
out vec4 color;
|
|
|
|
void main()
|
|
{
|
|
color = vec4(1.0, 0.0, 0.0, 1.0);
|
|
}
|
|
"""
|
|
|
|
def __init__(self,
|
|
width=None,
|
|
height=None,
|
|
caption=None,
|
|
resizable=False,
|
|
style=WINDOW_STYLE_DEFAULT,
|
|
fullscreen=False,
|
|
visible=True,
|
|
vsync=True,
|
|
file_drops=False,
|
|
display=None,
|
|
screen=None,
|
|
config=None,
|
|
context=None,
|
|
mode=None):
|
|
"""Create a window.
|
|
|
|
All parameters are optional, and reasonable defaults are assumed
|
|
where they are not specified.
|
|
|
|
The `display`, `screen`, `config` and `context` parameters form
|
|
a hierarchy of control: there is no need to specify more than
|
|
one of these. For example, if you specify `screen` the `display`
|
|
will be inferred, and a default `config` and `context` will be
|
|
created.
|
|
|
|
`config` is a special case; it can be a template created by the
|
|
user specifying the attributes desired, or it can be a complete
|
|
`config` as returned from `Screen.get_matching_configs` or similar.
|
|
|
|
The context will be active as soon as the window is created, as if
|
|
`switch_to` was just called.
|
|
|
|
:Parameters:
|
|
`width` : int
|
|
Width of the window, in pixels. Defaults to 960, or the
|
|
screen width if `fullscreen` is True.
|
|
`height` : int
|
|
Height of the window, in pixels. Defaults to 540, or the
|
|
screen height if `fullscreen` is True.
|
|
`caption` : str or unicode
|
|
Initial caption (title) of the window. Defaults to
|
|
``sys.argv[0]``.
|
|
`resizable` : bool
|
|
If True, the window will be resizable. Defaults to False.
|
|
`style` : int
|
|
One of the ``WINDOW_STYLE_*`` constants specifying the
|
|
border style of the window.
|
|
`fullscreen` : bool
|
|
If True, the window will cover the entire screen rather
|
|
than floating. Defaults to False.
|
|
`visible` : bool
|
|
Determines if the window is visible immediately after
|
|
creation. Defaults to True. Set this to False if you
|
|
would like to change attributes of the window before
|
|
having it appear to the user.
|
|
`vsync` : bool
|
|
If True, buffer flips are synchronised to the primary screen's
|
|
vertical retrace, eliminating flicker.
|
|
`display` : `Display`
|
|
The display device to use. Useful only under X11.
|
|
`screen` : `Screen`
|
|
The screen to use, if in fullscreen.
|
|
`config` : `pyglet.gl.Config`
|
|
Either a template from which to create a complete config,
|
|
or a complete config.
|
|
`context` : `pyglet.gl.Context`
|
|
The context to attach to this window. The context must
|
|
not already be attached to another window.
|
|
`mode` : `ScreenMode`
|
|
The screen will be switched to this mode if `fullscreen` is
|
|
True. If None, an appropriate mode is selected to accomodate
|
|
`width` and `height.`
|
|
|
|
"""
|
|
EventDispatcher.__init__(self)
|
|
self._event_queue = []
|
|
|
|
if not display:
|
|
display = pyglet.canvas.get_display()
|
|
|
|
if not screen:
|
|
screen = display.get_default_screen()
|
|
|
|
if not config:
|
|
for template_config in [gl.Config(double_buffer=True, depth_size=24, major_version=3, minor_version=3),
|
|
gl.Config(double_buffer=True, depth_size=16, major_version=3, minor_version=3),
|
|
None]:
|
|
try:
|
|
config = screen.get_best_config(template_config)
|
|
break
|
|
except NoSuchConfigException:
|
|
pass
|
|
if not config:
|
|
raise NoSuchConfigException('No standard config is available.')
|
|
|
|
# Necessary on Windows. More investigation needed:
|
|
if style in ('transparent', 'overlay'):
|
|
config.alpha = 8
|
|
|
|
if not config.is_complete():
|
|
config = screen.get_best_config(config)
|
|
|
|
if not context:
|
|
context = config.create_context(gl.current_context)
|
|
|
|
# Set these in reverse order to above, to ensure we get user preference
|
|
self._context = context
|
|
self._config = self._context.config
|
|
|
|
# XXX deprecate config's being screen-specific
|
|
if hasattr(self._config, 'screen'):
|
|
self._screen = self._config.screen
|
|
else:
|
|
self._screen = screen
|
|
self._display = self._screen.display
|
|
|
|
if fullscreen:
|
|
if width is None and height is None:
|
|
self._windowed_size = self._default_width, self._default_height
|
|
width, height = self._set_fullscreen_mode(mode, width, height)
|
|
if not self._windowed_size:
|
|
self._windowed_size = width, height
|
|
else:
|
|
if width is None:
|
|
width = self._default_width
|
|
if height is None:
|
|
height = self._default_height
|
|
|
|
self._width = width
|
|
self._height = height
|
|
self._resizable = resizable
|
|
self._fullscreen = fullscreen
|
|
self._style = style
|
|
if pyglet.options['vsync'] is not None:
|
|
self._vsync = pyglet.options['vsync']
|
|
else:
|
|
self._vsync = vsync
|
|
|
|
self._file_drops = file_drops
|
|
self._caption = caption or sys.argv[0]
|
|
|
|
from pyglet import app
|
|
app.windows.add(self)
|
|
self._create()
|
|
|
|
self.switch_to()
|
|
|
|
self._create_projection()
|
|
|
|
if visible:
|
|
self.set_visible(True)
|
|
self.activate()
|
|
|
|
def _create_projection(self):
|
|
self._default_program = shader.ShaderProgram(
|
|
shader.Shader(self._default_vertex_source, 'vertex'),
|
|
shader.Shader(self._default_fragment_source, 'fragment'))
|
|
|
|
self.ubo = self._default_program.uniform_blocks['WindowBlock'].create_ubo()
|
|
|
|
self._viewport = 0, 0, *self.get_framebuffer_size()
|
|
|
|
self.view = Mat4()
|
|
self.projection = Mat4.orthogonal_projection(0, self._width, 0, self._height, -255, 255)
|
|
|
|
def __del__(self):
|
|
# Always try to clean up the window when it is dereferenced.
|
|
# Makes sure there are no dangling pointers or memory leaks.
|
|
# If the window is already closed, pass silently.
|
|
try:
|
|
self.close()
|
|
except: # XXX Avoid a NoneType error if already closed.
|
|
pass
|
|
|
|
def __repr__(self):
|
|
return f'{self.__class__.__name__}=(width={self.width}, height={self.height})'
|
|
|
|
def _create(self):
|
|
raise NotImplementedError('abstract')
|
|
|
|
def _recreate(self, changes):
|
|
"""Recreate the window with current attributes.
|
|
|
|
:Parameters:
|
|
`changes` : list of str
|
|
List of attribute names that were changed since the last
|
|
`_create` or `_recreate`. For example, ``['fullscreen']``
|
|
is given if the window is to be toggled to or from fullscreen.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
# Public methods (sort alphabetically):
|
|
def activate(self):
|
|
"""Attempt to restore keyboard focus to the window.
|
|
|
|
Depending on the window manager or operating system, this may not
|
|
be successful. For example, on Windows XP an application is not
|
|
allowed to "steal" focus from another application. Instead, the
|
|
window's taskbar icon will flash, indicating it requires attention.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
@staticmethod
|
|
def clear():
|
|
"""Clear the window.
|
|
|
|
This is a convenience method for clearing the color and depth
|
|
buffer. The window must be the active context (see
|
|
:py:meth:`.switch_to`).
|
|
"""
|
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
|
|
|
|
def close(self):
|
|
"""Close the window.
|
|
|
|
After closing the window, the GL context will be invalid. The
|
|
window instance cannot be reused once closed. To re-use windows,
|
|
see :py:meth:`.set_visible` instead.
|
|
|
|
The :py:meth:`pyglet.app.EventLoop.on_window_close` event is
|
|
dispatched by the :py:attr:`pyglet.app.event_loop` when this method
|
|
is called.
|
|
"""
|
|
from pyglet import app
|
|
if not self._context:
|
|
return
|
|
app.windows.remove(self)
|
|
self._context.destroy()
|
|
self._config = None
|
|
self._context = None
|
|
if app.event_loop:
|
|
app.event_loop.dispatch_event('on_window_close', self)
|
|
self._event_queue = []
|
|
|
|
def dispatch_event(self, *args):
|
|
if not self._enable_event_queue or self._allow_dispatch_event:
|
|
super().dispatch_event(*args)
|
|
else:
|
|
self._event_queue.append(args)
|
|
|
|
def dispatch_events(self):
|
|
"""Poll the operating system event queue for new events and call
|
|
attached event handlers.
|
|
|
|
This method is provided for legacy applications targeting pyglet 1.0,
|
|
and advanced applications that must integrate their event loop
|
|
into another framework.
|
|
|
|
Typical applications should use :py:func:`pyglet.app.run`.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def draw(self, dt: float):
|
|
"""Redraw the Window contents.
|
|
|
|
This method will first call the :py:meth:`~pyglet.window.Window.`switch_to`
|
|
method to make the GL context current. It then dispatches the
|
|
:py:meth:`~pyglet.window.Window.on_draw` and
|
|
:py:meth:`~pyglet.window.Window.on_refresh`
|
|
events. Finally, it calls the :py:meth:`~pyglet.window.Window.flip`
|
|
method to swap the front and back OpenGL buffers.
|
|
"""
|
|
self.switch_to()
|
|
self.dispatch_event('on_draw')
|
|
self.dispatch_event('on_refresh', dt)
|
|
self.flip()
|
|
|
|
def draw_mouse_cursor(self):
|
|
"""Draw the custom mouse cursor.
|
|
|
|
If the current mouse cursor has ``drawable`` set, this method
|
|
is called before the buffers are flipped to render it.
|
|
|
|
There is little need to override this method; instead, subclass
|
|
:py:class:`MouseCursor` and provide your own
|
|
:py:meth:`~MouseCursor.draw` method.
|
|
"""
|
|
# Draw mouse cursor if set and visible.
|
|
|
|
if self._mouse_cursor.gl_drawable and self._mouse_visible and self._mouse_in_window:
|
|
# TODO: consider projection differences
|
|
self._mouse_cursor.draw(self._mouse_x, self._mouse_y)
|
|
|
|
def flip(self):
|
|
"""Swap the OpenGL front and back buffers.
|
|
|
|
Call this method on a double-buffered window to update the
|
|
visible display with the back buffer. Windows are
|
|
double-buffered by default unless you turn this feature off.
|
|
|
|
The contents of the back buffer are undefined after this operation.
|
|
|
|
The default :py:attr:`~pyglet.app.event_loop` automatically
|
|
calls this method after the window's
|
|
:py:meth:`~pyglet.window.Window.on_draw` event.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def get_framebuffer_size(self):
|
|
"""Return the size in actual pixels of the Window framebuffer.
|
|
|
|
When using HiDPI screens, the size of the Window's framebuffer
|
|
can be higher than that of the Window size requested. If you
|
|
are performing operations that require knowing the actual number
|
|
of pixels in the window, this method should be used instead of
|
|
:py:func:`Window.get_size()`. For example, setting the Window
|
|
projection or setting the glViewport size.
|
|
|
|
:rtype: (int, int)
|
|
:return: The width and height of the Window's framebuffer, in pixels.
|
|
"""
|
|
return self.get_size()
|
|
|
|
def get_location(self):
|
|
"""Return the current position of the window.
|
|
|
|
:rtype: (int, int)
|
|
:return: The distances of the left and top edges from their respective
|
|
edges on the virtual desktop, in pixels.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def get_pixel_ratio(self):
|
|
"""Return the framebuffer/window size ratio.
|
|
|
|
Some platforms and/or window systems support subpixel scaling,
|
|
making the framebuffer size larger than the window size.
|
|
Retina screens on OS X and Gnome on Linux are some examples.
|
|
|
|
On a Retina systems the returned ratio would usually be 2.0 as a
|
|
window of size 500 x 500 would have a framebuffer of 1000 x 1000.
|
|
Fractional values between 1.0 and 2.0, as well as values above
|
|
2.0 may also be encountered.
|
|
|
|
:rtype: float
|
|
:return: The framebuffer/window size ratio
|
|
"""
|
|
return self.get_framebuffer_size()[0] / self.width
|
|
|
|
def get_size(self) -> Tuple[int, int]:
|
|
"""Return the current size of the window.
|
|
|
|
This does not include the windows' border or title bar.
|
|
|
|
:rtype: (int, int)
|
|
:return: The width and height of the window, in pixels.
|
|
"""
|
|
return self._width, self._height
|
|
|
|
def get_system_mouse_cursor(self, name):
|
|
"""Obtain a system mouse cursor.
|
|
|
|
Use `set_mouse_cursor` to make the cursor returned by this method
|
|
active. The names accepted by this method are the ``CURSOR_*``
|
|
constants defined on this class.
|
|
|
|
:Parameters:
|
|
`name` : str
|
|
Name describing the mouse cursor to return. For example,
|
|
``CURSOR_WAIT``, ``CURSOR_HELP``, etc.
|
|
|
|
:rtype: `MouseCursor`
|
|
:return: A mouse cursor which can be used with `set_mouse_cursor`.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def get_clipboard_text(self) -> str:
|
|
"""Access the system clipboard and attempt to retrieve text.
|
|
|
|
:rtype: `str`
|
|
:return: A string from the clipboard. String will be empty if no text found.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def set_clipboard_text(self, text: str):
|
|
"""Access the system clipboard and set a text string as the clipboard data.
|
|
|
|
This will clear the existing clipboard.
|
|
|
|
:Parameters:
|
|
`text` : str
|
|
Text you want to place in the clipboard.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def minimize(self):
|
|
"""Minimize the window.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def maximize(self):
|
|
"""Maximize the window.
|
|
|
|
The behaviour of this method is somewhat dependent on the user's
|
|
display setup. On a multi-monitor system, the window may maximize
|
|
to either a single screen or the entire virtual desktop.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def on_close(self):
|
|
"""Default on_close handler."""
|
|
self.has_exit = True
|
|
from pyglet import app
|
|
if app.event_loop.is_running:
|
|
self.close()
|
|
|
|
def on_key_press(self, symbol, modifiers):
|
|
"""Default on_key_press handler."""
|
|
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
|
|
key.MOD_CAPSLOCK |
|
|
key.MOD_SCROLLLOCK)):
|
|
self.dispatch_event('on_close')
|
|
|
|
def on_resize(self, width, height):
|
|
"""A default resize event handler.
|
|
|
|
This default handler updates the GL viewport to cover the entire
|
|
window. The bottom-left corner is (0, 0) and the top-right
|
|
corner is the width and height of the window's framebuffer.
|
|
In addition, the projection matrix is set to an orghogonal
|
|
projection based on the same dimensions.
|
|
"""
|
|
gl.glViewport(0, 0, *self.get_framebuffer_size())
|
|
self.projection = Mat4.orthogonal_projection(0, width, 0, height, -255, 255)
|
|
|
|
def set_caption(self, caption):
|
|
"""Set the window's caption.
|
|
|
|
The caption appears in the titlebar of the window, if it has one,
|
|
and in the taskbar on Windows and many X11 window managers.
|
|
|
|
:Parameters:
|
|
`caption` : str or unicode
|
|
The caption to set.
|
|
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def set_fullscreen(self, fullscreen=True, screen=None, mode=None, width=None, height=None):
|
|
"""Toggle to or from fullscreen.
|
|
|
|
After toggling fullscreen, the GL context should have retained its
|
|
state and objects, however the buffers will need to be cleared and
|
|
redrawn.
|
|
|
|
If `width` and `height` are specified and `fullscreen` is True, the
|
|
screen may be switched to a different resolution that most closely
|
|
matches the given size. If the resolution doesn't match exactly,
|
|
a higher resolution is selected and the window will be centered
|
|
within a black border covering the rest of the screen.
|
|
|
|
:Parameters:
|
|
`fullscreen` : bool
|
|
True if the window should be made fullscreen, False if it
|
|
should be windowed.
|
|
`screen` : Screen
|
|
If not None and fullscreen is True, the window is moved to the
|
|
given screen. The screen must belong to the same display as
|
|
the window.
|
|
`mode` : `ScreenMode`
|
|
The screen will be switched to the given mode. The mode must
|
|
have been obtained by enumerating `Screen.get_modes`. If
|
|
None, an appropriate mode will be selected from the given
|
|
`width` and `height`.
|
|
`width` : int
|
|
Optional width of the window. If unspecified, defaults to the
|
|
previous window size when windowed, or the screen size if
|
|
fullscreen.
|
|
|
|
.. versionadded:: 1.2
|
|
`height` : int
|
|
Optional height of the window. If unspecified, defaults to
|
|
the previous window size when windowed, or the screen size if
|
|
fullscreen.
|
|
|
|
.. versionadded:: 1.2
|
|
"""
|
|
if (fullscreen == self._fullscreen and
|
|
(screen is None or screen is self._screen) and
|
|
(width is None or width == self._width) and
|
|
(height is None or height == self._height)):
|
|
return
|
|
|
|
if not self._fullscreen:
|
|
# Save windowed size
|
|
self._windowed_size = self.get_size()
|
|
self._windowed_location = self.get_location()
|
|
|
|
if fullscreen and screen is not None:
|
|
assert screen.display is self.display
|
|
self._screen = screen
|
|
|
|
self._fullscreen = fullscreen
|
|
if self._fullscreen:
|
|
self._width, self._height = self._set_fullscreen_mode(mode, width, height)
|
|
else:
|
|
self.screen.restore_mode()
|
|
|
|
self._width, self._height = self._windowed_size
|
|
if width is not None:
|
|
self._width = width
|
|
if height is not None:
|
|
self._height = height
|
|
|
|
self._recreate(['fullscreen'])
|
|
|
|
if not self._fullscreen and self._windowed_location:
|
|
# Restore windowed location.
|
|
self.set_location(*self._windowed_location)
|
|
|
|
def _set_fullscreen_mode(self, mode, width, height):
|
|
if mode is not None:
|
|
self.screen.set_mode(mode)
|
|
if width is None:
|
|
width = self.screen.width
|
|
if height is None:
|
|
height = self.screen.height
|
|
elif width is not None or height is not None:
|
|
if width is None:
|
|
width = 0
|
|
if height is None:
|
|
height = 0
|
|
mode = self.screen.get_closest_mode(width, height)
|
|
if mode is not None:
|
|
self.screen.set_mode(mode)
|
|
elif self.screen.get_modes():
|
|
# Only raise exception if mode switching is at all possible.
|
|
raise NoSuchScreenModeException(f'No mode matching {width}x{height}')
|
|
else:
|
|
width = self.screen.width
|
|
height = self.screen.height
|
|
return width, height
|
|
|
|
def set_minimum_size(self, width: int, height: int) -> None:
|
|
"""Set the minimum size of the window.
|
|
|
|
Once set, the user will not be able to resize the window smaller
|
|
than the given dimensions. There is no way to remove the
|
|
minimum size constraint on a window (but you could set it to 0,0).
|
|
|
|
The behaviour is undefined if the minimum size is set larger than
|
|
the current size of the window.
|
|
|
|
The window size does not include the border or title bar.
|
|
|
|
:Parameters:
|
|
`width` : int
|
|
Minimum width of the window, in pixels.
|
|
`height` : int
|
|
Minimum height of the window, in pixels.
|
|
|
|
"""
|
|
if width < 1 or height < 1:
|
|
raise ValueError('width and height must be positive integers')
|
|
|
|
self._minimum_size = width, height
|
|
|
|
def set_maximum_size(self, width: int, height: int) -> None:
|
|
"""Set the maximum size of the window.
|
|
|
|
Once set, the user will not be able to resize the window larger
|
|
than the given dimensions. There is no way to remove the
|
|
maximum size constraint on a window (but you could set it to a large
|
|
value).
|
|
|
|
The behaviour is undefined if the maximum size is set smaller than
|
|
the current size of the window.
|
|
|
|
The window size does not include the border or title bar.
|
|
|
|
:Parameters:
|
|
`width` : int
|
|
Maximum width of the window, in pixels.
|
|
`height` : int
|
|
Maximum height of the window, in pixels.
|
|
|
|
"""
|
|
if width < 1 or height < 1:
|
|
raise ValueError('width and height must be positive integers')
|
|
|
|
self._maximum_size = width, height
|
|
|
|
def set_size(self, width: int, height: int) -> None:
|
|
"""Resize the window.
|
|
|
|
The behaviour is undefined if the window is not resizable, or if
|
|
it is currently fullscreen.
|
|
|
|
The window size does not include the border or title bar.
|
|
|
|
:Parameters:
|
|
`width` : int
|
|
New width of the window, in pixels.
|
|
`height` : int
|
|
New height of the window, in pixels.
|
|
|
|
"""
|
|
if self._fullscreen:
|
|
raise WindowException('Cannot set size of fullscreen window.')
|
|
if width < 1 or height < 1:
|
|
raise ValueError('width and height must be positive integers')
|
|
|
|
self._width, self._height = width, height
|
|
|
|
def set_location(self, x, y):
|
|
"""Set the position of the window.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance of the left edge of the window from the left edge
|
|
of the virtual desktop, in pixels.
|
|
`y` : int
|
|
Distance of the top edge of the window from the top edge of
|
|
the virtual desktop, in pixels.
|
|
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
def set_visible(self, visible: bool = True) -> None:
|
|
"""Show or hide the window.
|
|
|
|
:Parameters:
|
|
`visible` : bool
|
|
If True, the window will be shown; otherwise it will be
|
|
hidden.
|
|
"""
|
|
self._visible = visible
|
|
|
|
def set_vsync(self, vsync: bool) -> None:
|
|
"""Enable or disable vertical sync control.
|
|
|
|
When enabled, this option ensures flips from the back to the front
|
|
buffer are performed only during the vertical retrace period of the
|
|
primary display. This can prevent "tearing" or flickering when
|
|
the buffer is updated in the middle of a video scan.
|
|
|
|
Note that LCD monitors have an analogous time in which they are not
|
|
reading from the video buffer; while it does not correspond to
|
|
a vertical retrace it has the same effect.
|
|
|
|
Also note that with multi-monitor systems the secondary monitor
|
|
cannot be synchronised to, so tearing and flicker cannot be avoided
|
|
when the window is positioned outside of the primary display.
|
|
|
|
:Parameters:
|
|
`vsync` : bool
|
|
If True, vsync is enabled, otherwise it is disabled.
|
|
|
|
"""
|
|
self._vsync = vsync
|
|
|
|
def set_mouse_visible(self, visible=True):
|
|
"""Show or hide the mouse cursor.
|
|
|
|
The mouse cursor will only be hidden while it is positioned within
|
|
this window. Mouse events will still be processed as usual.
|
|
|
|
:Parameters:
|
|
`visible` : bool
|
|
If True, the mouse cursor will be visible, otherwise it
|
|
will be hidden.
|
|
|
|
"""
|
|
self._mouse_visible = visible
|
|
self.set_mouse_platform_visible()
|
|
|
|
def set_mouse_platform_visible(self, platform_visible=None):
|
|
"""Set the platform-drawn mouse cursor visibility. This is called
|
|
automatically after changing the mouse cursor or exclusive mode.
|
|
|
|
Applications should not normally need to call this method, see
|
|
`set_mouse_visible` instead.
|
|
|
|
:Parameters:
|
|
`platform_visible` : bool or None
|
|
If None, sets platform visibility to the required visibility
|
|
for the current exclusive mode and cursor type. Otherwise,
|
|
a bool value will override and force a visibility.
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def set_mouse_cursor(self, cursor=None):
|
|
"""Change the appearance of the mouse cursor.
|
|
|
|
The appearance of the mouse cursor is only changed while it is
|
|
within this window.
|
|
|
|
:Parameters:
|
|
`cursor` : `MouseCursor`
|
|
The cursor to set, or None to restore the default cursor.
|
|
|
|
"""
|
|
if cursor is None:
|
|
cursor = DefaultMouseCursor()
|
|
self._mouse_cursor = cursor
|
|
self.set_mouse_platform_visible()
|
|
|
|
def set_exclusive_mouse(self, exclusive=True):
|
|
"""Hide the mouse cursor and direct all mouse events to this
|
|
window.
|
|
|
|
When enabled, this feature prevents the mouse leaving the window. It
|
|
is useful for certain styles of games that require complete control of
|
|
the mouse. The position of the mouse as reported in subsequent events
|
|
is meaningless when exclusive mouse is enabled; you should only use
|
|
the relative motion parameters ``dx`` and ``dy``.
|
|
|
|
:Parameters:
|
|
`exclusive` : bool
|
|
If True, exclusive mouse is enabled, otherwise it is disabled.
|
|
|
|
"""
|
|
self._mouse_exclusive = exclusive
|
|
|
|
def set_exclusive_keyboard(self, exclusive=True):
|
|
"""Prevent the user from switching away from this window using
|
|
keyboard accelerators.
|
|
|
|
When enabled, this feature disables certain operating-system specific
|
|
key combinations such as Alt+Tab (Command+Tab on OS X). This can be
|
|
useful in certain kiosk applications, it should be avoided in general
|
|
applications or games.
|
|
|
|
:Parameters:
|
|
`exclusive` : bool
|
|
If True, exclusive keyboard is enabled, otherwise it is
|
|
disabled.
|
|
|
|
"""
|
|
self._keyboard_exclusive = exclusive
|
|
|
|
def set_icon(self, *images):
|
|
"""Set the window icon.
|
|
|
|
If multiple images are provided, one with an appropriate size
|
|
will be selected (if the correct size is not provided, the image
|
|
will be scaled).
|
|
|
|
Useful sizes to provide are 16x16, 32x32, 64x64 (Mac only) and
|
|
128x128 (Mac only).
|
|
|
|
:Parameters:
|
|
`images` : sequence of `pyglet.image.AbstractImage`
|
|
List of images to use for the window icon.
|
|
|
|
"""
|
|
pass
|
|
|
|
def switch_to(self):
|
|
"""Make this window the current OpenGL rendering context.
|
|
|
|
Only one OpenGL context can be active at a time. This method
|
|
sets the current window context as the active one.
|
|
|
|
In most cases, you should use this method instead of directly
|
|
calling :py:meth:`pyglet.gl.Context.set_current`. The latter
|
|
will not perform platform-specific state management tasks for
|
|
you.
|
|
"""
|
|
raise NotImplementedError('abstract')
|
|
|
|
# Attributes (sort alphabetically):
|
|
@property
|
|
def caption(self):
|
|
"""The window caption (title). Read-only.
|
|
|
|
:type: str
|
|
"""
|
|
return self._caption
|
|
|
|
@property
|
|
def resizeable(self):
|
|
"""True if the window is resizable. Read-only.
|
|
|
|
:type: bool
|
|
"""
|
|
return self._resizable
|
|
|
|
@property
|
|
def style(self):
|
|
"""The window style; one of the ``WINDOW_STYLE_*`` constants.
|
|
Read-only.
|
|
|
|
:type: int
|
|
"""
|
|
return self._style
|
|
|
|
@property
|
|
def fullscreen(self):
|
|
"""True if the window is currently fullscreen. Read-only.
|
|
|
|
:type: bool
|
|
"""
|
|
return self._fullscreen
|
|
|
|
@property
|
|
def visible(self):
|
|
"""True if the window is currently visible. Read-only.
|
|
|
|
:type: bool
|
|
"""
|
|
return self._visible
|
|
|
|
@property
|
|
def vsync(self):
|
|
"""True if buffer flips are synchronised to the screen's vertical
|
|
retrace. Read-only.
|
|
|
|
:type: bool
|
|
"""
|
|
return self._vsync
|
|
|
|
@property
|
|
def display(self):
|
|
"""The display this window belongs to. Read-only.
|
|
|
|
:type: :py:class:`Display`
|
|
"""
|
|
return self._display
|
|
|
|
@property
|
|
def screen(self):
|
|
"""The screen this window is fullscreen in. Read-only.
|
|
|
|
:type: :py:class:`Screen`
|
|
"""
|
|
return self._screen
|
|
|
|
@property
|
|
def config(self):
|
|
"""A GL config describing the context of this window. Read-only.
|
|
|
|
:type: :py:class:`pyglet.gl.Config`
|
|
"""
|
|
return self._config
|
|
|
|
@property
|
|
def context(self):
|
|
"""The OpenGL context attached to this window. Read-only.
|
|
|
|
:type: :py:class:`pyglet.gl.Context`
|
|
"""
|
|
return self._context
|
|
|
|
# These are the only properties that can be set
|
|
@property
|
|
def width(self):
|
|
"""The width of the window, in pixels. Read-write.
|
|
|
|
:type: int
|
|
"""
|
|
return self.get_size()[0]
|
|
|
|
@width.setter
|
|
def width(self, new_width):
|
|
self.set_size(new_width, self.height)
|
|
|
|
@property
|
|
def height(self):
|
|
"""The height of the window, in pixels. Read-write.
|
|
|
|
:type: int
|
|
"""
|
|
return self.get_size()[1]
|
|
|
|
@height.setter
|
|
def height(self, new_height):
|
|
self.set_size(self.width, new_height)
|
|
|
|
@property
|
|
def size(self):
|
|
"""The size of the window. Read-Write.
|
|
|
|
:type: tuple
|
|
"""
|
|
return self.get_size()
|
|
|
|
@size.setter
|
|
def size(self, new_size):
|
|
self.set_size(*new_size)
|
|
|
|
@property
|
|
def aspect_ratio(self):
|
|
"""The aspect ratio of the window. Read-Only.
|
|
|
|
:type: float
|
|
"""
|
|
w, h = self.get_size()
|
|
return w / h
|
|
|
|
@property
|
|
def projection(self):
|
|
"""The OpenGL window projection matrix. Read-write.
|
|
|
|
This matrix is used to transform vertices when using any of the built-in
|
|
drawable classes. `view` is done first, then `projection`.
|
|
|
|
The default projection matrix is orthographic (2D),
|
|
but a custom :py:class:`pyglet.math.Mat4` instance
|
|
can be set. Alternatively, you can supply a flat
|
|
tuple of 16 values.
|
|
|
|
(2D), but can be changed to any 4x4 matrix desired.
|
|
See :py:class:`pyglet.math.Mat4`.
|
|
|
|
:type: :py:class:`pyglet.math.Mat4`
|
|
"""
|
|
return self._projection_matrix
|
|
|
|
@projection.setter
|
|
def projection(self, matrix: Mat4):
|
|
|
|
with self.ubo as window_block:
|
|
window_block.projection[:] = matrix
|
|
|
|
self._projection_matrix = matrix
|
|
|
|
@property
|
|
def view(self):
|
|
"""The OpenGL window view matrix. Read-write.
|
|
|
|
This matrix is used to transform vertices when using any of the built-in
|
|
drawable classes. `view` is done first, then `projection`.
|
|
|
|
The default view is an identity matrix, but a custom
|
|
:py:class:`pyglet.math.Mat4` instance can be set.
|
|
Alternatively, you can supply a flat tuple of 16 values.
|
|
|
|
:type: :py:class:`pyglet.math.Mat4`
|
|
"""
|
|
return self._view_matrix
|
|
|
|
@view.setter
|
|
def view(self, matrix: Mat4):
|
|
|
|
with self.ubo as window_block:
|
|
window_block.view[:] = matrix
|
|
|
|
self._view_matrix = matrix
|
|
|
|
@property
|
|
def viewport(self):
|
|
"""The Window viewport
|
|
|
|
The Window viewport, expressed as (x, y, width, height).
|
|
|
|
:rtype: (int, int, int, int)
|
|
:return: The viewport size as a tuple of four ints.
|
|
"""
|
|
return self._viewport
|
|
|
|
@viewport.setter
|
|
def viewport(self, values):
|
|
self._viewport = values
|
|
pr = self.get_pixel_ratio()
|
|
x, y, w, h = values
|
|
pyglet.gl.glViewport(int(x * pr), int(y * pr), int(w * pr), int(h * pr))
|
|
|
|
# If documenting, show the event methods. Otherwise, leave them out
|
|
# as they are not really methods.
|
|
if _is_pyglet_doc_run:
|
|
def on_activate(self):
|
|
"""The window was activated.
|
|
|
|
This event can be triggered by clicking on the title bar, bringing
|
|
it to the foreground; or by some platform-specific method.
|
|
|
|
When a window is "active" it has the keyboard focus.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_close(self):
|
|
"""The user attempted to close the window.
|
|
|
|
This event can be triggered by clicking on the "X" control box in
|
|
the window title bar, or by some other platform-dependent manner.
|
|
|
|
The default handler sets `has_exit` to ``True``. In pyglet 1.1, if
|
|
`pyglet.app.event_loop` is being used, `close` is also called,
|
|
closing the window immediately.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_context_lost(self):
|
|
"""The window's GL context was lost.
|
|
|
|
When the context is lost no more GL methods can be called until it
|
|
is recreated. This is a rare event, triggered perhaps by the user
|
|
switching to an incompatible video mode. When it occurs, an
|
|
application will need to reload all objects (display lists, texture
|
|
objects, shaders) as well as restore the GL state.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_context_state_lost(self):
|
|
"""The state of the window's GL context was lost.
|
|
|
|
pyglet may sometimes need to recreate the window's GL context if
|
|
the window is moved to another video device, or between fullscreen
|
|
or windowed mode. In this case it will try to share the objects
|
|
(display lists, texture objects, shaders) between the old and new
|
|
contexts. If this is possible, only the current state of the GL
|
|
context is lost, and the application should simply restore state.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_deactivate(self):
|
|
"""The window was deactivated.
|
|
|
|
This event can be triggered by clicking on another application
|
|
window. When a window is deactivated it no longer has the
|
|
keyboard focus.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_draw(self):
|
|
"""The window contents should be redrawn.
|
|
|
|
The `EventLoop` will dispatch this event when the `draw`
|
|
method has been called. The window will already have the
|
|
GL context, so there is no need to call `switch_to`. The window's
|
|
`flip` method will be called immediately after this event,
|
|
so your event handler should not.
|
|
|
|
You should make no assumptions about the window contents when
|
|
this event is triggered; a resize or expose event may have
|
|
invalidated the framebuffer since the last time it was drawn.
|
|
|
|
.. versionadded:: 1.1
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_expose(self):
|
|
"""A portion of the window needs to be redrawn.
|
|
|
|
This event is triggered when the window first appears, and any time
|
|
the contents of the window is invalidated due to another window
|
|
obscuring it.
|
|
|
|
There is no way to determine which portion of the window needs
|
|
redrawing. Note that the use of this method is becoming
|
|
increasingly uncommon, as newer window managers composite windows
|
|
automatically and keep a backing store of the window contents.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_file_drop(self, x, y, paths):
|
|
"""File(s) were dropped into the window, will return the position of the cursor and
|
|
a list of paths to the files that were dropped.
|
|
|
|
.. versionadded:: 1.5.1
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_hide(self):
|
|
"""The window was hidden.
|
|
|
|
This event is triggered when a window is minimised
|
|
or hidden by the user.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_key_press(self, symbol, modifiers):
|
|
"""A key on the keyboard was pressed (and held down).
|
|
|
|
Since pyglet 1.1 the default handler dispatches the :py:meth:`~pyglet.window.Window.on_close`
|
|
event if the ``ESC`` key is pressed.
|
|
|
|
:Parameters:
|
|
`symbol` : int
|
|
The key symbol pressed.
|
|
`modifiers` : int
|
|
Bitwise combination of the key modifiers active.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_key_release(self, symbol, modifiers):
|
|
"""A key on the keyboard was released.
|
|
|
|
:Parameters:
|
|
`symbol` : int
|
|
The key symbol pressed.
|
|
`modifiers` : int
|
|
Bitwise combination of the key modifiers active.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_motion(self, x, y, dx, dy):
|
|
"""The mouse was moved with no buttons held down.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
`dx` : int
|
|
Relative X position from the previous mouse position.
|
|
`dy` : int
|
|
Relative Y position from the previous mouse position.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
|
"""The mouse was moved with one or more mouse buttons pressed.
|
|
|
|
This event will continue to be fired even if the mouse leaves
|
|
the window, so long as the drag buttons are continuously held down.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
`dx` : int
|
|
Relative X position from the previous mouse position.
|
|
`dy` : int
|
|
Relative Y position from the previous mouse position.
|
|
`buttons` : int
|
|
Bitwise combination of the mouse buttons currently pressed.
|
|
`modifiers` : int
|
|
Bitwise combination of any keyboard modifiers currently
|
|
active.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_press(self, x, y, button, modifiers):
|
|
"""A mouse button was pressed (and held down).
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
`button` : int
|
|
The mouse button that was pressed.
|
|
`modifiers` : int
|
|
Bitwise combination of any keyboard modifiers currently
|
|
active.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_release(self, x, y, button, modifiers):
|
|
"""A mouse button was released.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
`button` : int
|
|
The mouse button that was released.
|
|
`modifiers` : int
|
|
Bitwise combination of any keyboard modifiers currently
|
|
active.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
|
"""The mouse wheel was scrolled.
|
|
|
|
Note that most mice have only a vertical scroll wheel, so
|
|
`scroll_x` is usually 0. An exception to this is the Apple Mighty
|
|
Mouse, which has a mouse ball in place of the wheel which allows
|
|
both `scroll_x` and `scroll_y` movement.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
`scroll_x` : float
|
|
Amount of movement on the horizontal axis.
|
|
`scroll_y` : float
|
|
Amount of movement on the vertical axis.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_enter(self, x, y):
|
|
"""The mouse was moved into the window.
|
|
|
|
This event will not be triggered if the mouse is currently being
|
|
dragged.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_mouse_leave(self, x, y):
|
|
"""The mouse was moved outside the window.
|
|
|
|
This event will not be triggered if the mouse is currently being
|
|
dragged. Note that the coordinates of the mouse pointer will be
|
|
outside the window rectangle.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance in pixels from the left edge of the window.
|
|
`y` : int
|
|
Distance in pixels from the bottom edge of the window.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_move(self, x, y):
|
|
"""The window was moved.
|
|
|
|
:Parameters:
|
|
`x` : int
|
|
Distance from the left edge of the screen to the left edge
|
|
of the window.
|
|
`y` : int
|
|
Distance from the top edge of the screen to the top edge of
|
|
the window. Note that this is one of few methods in pyglet
|
|
which use a Y-down coordinate system.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_refresh(self, dt):
|
|
"""The window contents should be redrawn.
|
|
|
|
The `EventLoop` will dispatch this event when the `draw`
|
|
method has been called. The window will already have the
|
|
GL context, so there is no need to call `switch_to`. The window's
|
|
`flip` method will be called immediately after this event, so your
|
|
event handler should not.
|
|
|
|
You should make no assumptions about the window contents when
|
|
this event is triggered; a resize or expose event may have
|
|
invalidated the framebuffer since the last time it was drawn.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_resize(self, width, height):
|
|
"""The window was resized.
|
|
|
|
The window will have the GL context when this event is dispatched;
|
|
there is no need to call `switch_to` in this handler.
|
|
|
|
:Parameters:
|
|
`width` : int
|
|
The new width of the window, in pixels.
|
|
`height` : int
|
|
The new height of the window, in pixels.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_show(self):
|
|
"""The window was shown.
|
|
|
|
This event is triggered when a window is restored after being
|
|
minimised, hidden, or after being displayed for the first time.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_text(self, text):
|
|
"""The user input some text.
|
|
|
|
Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before
|
|
:py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key
|
|
is held down (key repeating); or called without key presses if
|
|
another input method was used (e.g., a pen input).
|
|
|
|
You should always use this method for interpreting text, as the
|
|
key symbols often have complex mappings to their unicode
|
|
representation which this event takes care of.
|
|
|
|
:Parameters:
|
|
`text` : unicode
|
|
The text entered by the user.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_text_motion(self, motion):
|
|
"""The user moved the text input cursor.
|
|
|
|
Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before
|
|
:py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key
|
|
is help down (key repeating).
|
|
|
|
You should always use this method for moving the text input cursor
|
|
(caret), as different platforms have different default keyboard
|
|
mappings, and key repeats are handled correctly.
|
|
|
|
The values that `motion` can take are defined in
|
|
:py:mod:`pyglet.window.key`:
|
|
|
|
* MOTION_UP
|
|
* MOTION_RIGHT
|
|
* MOTION_DOWN
|
|
* MOTION_LEFT
|
|
* MOTION_NEXT_WORD
|
|
* MOTION_PREVIOUS_WORD
|
|
* MOTION_BEGINNING_OF_LINE
|
|
* MOTION_END_OF_LINE
|
|
* MOTION_NEXT_PAGE
|
|
* MOTION_PREVIOUS_PAGE
|
|
* MOTION_BEGINNING_OF_FILE
|
|
* MOTION_END_OF_FILE
|
|
* MOTION_BACKSPACE
|
|
* MOTION_DELETE
|
|
|
|
:Parameters:
|
|
`motion` : int
|
|
The direction of motion; see remarks.
|
|
|
|
:event:
|
|
"""
|
|
|
|
def on_text_motion_select(self, motion):
|
|
"""The user moved the text input cursor while extending the
|
|
selection.
|
|
|
|
Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before
|
|
:py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key
|
|
is help down (key repeating).
|
|
|
|
You should always use this method for responding to text selection
|
|
events rather than the raw :py:meth:`~pyglet.window.Window.on_key_press`, as different platforms
|
|
have different default keyboard mappings, and key repeats are
|
|
handled correctly.
|
|
|
|
The values that `motion` can take are defined in :py:mod:`pyglet.window.key`:
|
|
|
|
* MOTION_UP
|
|
* MOTION_RIGHT
|
|
* MOTION_DOWN
|
|
* MOTION_LEFT
|
|
* MOTION_NEXT_WORD
|
|
* MOTION_PREVIOUS_WORD
|
|
* MOTION_BEGINNING_OF_LINE
|
|
* MOTION_END_OF_LINE
|
|
* MOTION_NEXT_PAGE
|
|
* MOTION_PREVIOUS_PAGE
|
|
* MOTION_BEGINNING_OF_FILE
|
|
* MOTION_END_OF_FILE
|
|
|
|
:Parameters:
|
|
`motion` : int
|
|
The direction of selection motion; see remarks.
|
|
|
|
:event:
|
|
"""
|
|
|
|
|
|
BaseWindow.register_event_type('on_key_press')
|
|
BaseWindow.register_event_type('on_key_release')
|
|
BaseWindow.register_event_type('on_text')
|
|
BaseWindow.register_event_type('on_text_motion')
|
|
BaseWindow.register_event_type('on_text_motion_select')
|
|
BaseWindow.register_event_type('on_mouse_motion')
|
|
BaseWindow.register_event_type('on_mouse_drag')
|
|
BaseWindow.register_event_type('on_mouse_press')
|
|
BaseWindow.register_event_type('on_mouse_release')
|
|
BaseWindow.register_event_type('on_mouse_scroll')
|
|
BaseWindow.register_event_type('on_mouse_enter')
|
|
BaseWindow.register_event_type('on_mouse_leave')
|
|
BaseWindow.register_event_type('on_close')
|
|
BaseWindow.register_event_type('on_expose')
|
|
BaseWindow.register_event_type('on_resize')
|
|
BaseWindow.register_event_type('on_move')
|
|
BaseWindow.register_event_type('on_activate')
|
|
BaseWindow.register_event_type('on_deactivate')
|
|
BaseWindow.register_event_type('on_show')
|
|
BaseWindow.register_event_type('on_hide')
|
|
BaseWindow.register_event_type('on_context_lost')
|
|
BaseWindow.register_event_type('on_context_state_lost')
|
|
BaseWindow.register_event_type('on_file_drop')
|
|
BaseWindow.register_event_type('on_draw')
|
|
BaseWindow.register_event_type('on_refresh')
|
|
|
|
|
|
class FPSDisplay:
|
|
"""Display of a window's framerate.
|
|
|
|
This is a convenience class to aid in profiling and debugging. Typical
|
|
usage is to create an `FPSDisplay` for each window, and draw the display
|
|
at the end of the windows' :py:meth:`~pyglet.window.Window.on_draw` event handler::
|
|
|
|
from pyglet.window import Window, FPSDisplay
|
|
|
|
window = Window()
|
|
fps_display = FPSDisplay(window)
|
|
|
|
@window.event
|
|
def on_draw():
|
|
# ... perform ordinary window drawing operations ...
|
|
|
|
fps_display.draw()
|
|
|
|
The style and position of the display can be modified via the :py:func:`~pyglet.text.Label`
|
|
attribute. Different text can be substituted by overriding the
|
|
`set_fps` method. The display can be set to update more or less often
|
|
by setting the `update_period` attribute. Note: setting the `update_period`
|
|
to a value smaller than your Window refresh rate will cause inaccurate readings.
|
|
|
|
:Ivariables:
|
|
`label` : Label
|
|
The text label displaying the framerate.
|
|
|
|
"""
|
|
|
|
#: Time in seconds between updates.
|
|
#:
|
|
#: :type: float
|
|
update_period = 0.25
|
|
|
|
def __init__(self, window, color=(127, 127, 127, 127), samples=240):
|
|
from time import time
|
|
from statistics import mean
|
|
from collections import deque
|
|
from pyglet.text import Label
|
|
self._time = time
|
|
self._mean = mean
|
|
|
|
# Hook into the Window.flip method:
|
|
self._window_flip, window.flip = window.flip, self._hook_flip
|
|
self.label = Label('', x=10, y=10, font_size=24, bold=True, color=color)
|
|
|
|
self._elapsed = 0.0
|
|
self._last_time = time()
|
|
self._delta_times = deque(maxlen=samples)
|
|
|
|
def update(self):
|
|
"""Records a new data point at the current time. This method
|
|
is called automatically when the window buffer is flipped.
|
|
"""
|
|
t = self._time()
|
|
delta = t - self._last_time
|
|
self._elapsed += delta
|
|
self._delta_times.append(delta)
|
|
self._last_time = t
|
|
|
|
if self._elapsed >= self.update_period:
|
|
self._elapsed = 0
|
|
self.label.text = f"{1 / self._mean(self._delta_times):.2f}"
|
|
|
|
def draw(self):
|
|
"""Draw the label."""
|
|
self.label.draw()
|
|
|
|
def _hook_flip(self):
|
|
self.update()
|
|
self._window_flip()
|
|
|
|
|
|
if _is_pyglet_doc_run:
|
|
# We are building documentation
|
|
Window = BaseWindow
|
|
Window.__name__ = 'Window'
|
|
del BaseWindow
|
|
|
|
else:
|
|
# Try to determine which platform to use.
|
|
if pyglet.options['headless']:
|
|
from pyglet.window.headless import HeadlessWindow as Window
|
|
elif pyglet.compat_platform == 'darwin':
|
|
from pyglet.window.cocoa import CocoaWindow as Window
|
|
elif pyglet.compat_platform in ('win32', 'cygwin'):
|
|
from pyglet.window.win32 import Win32Window as Window
|
|
else:
|
|
from pyglet.window.xlib import XlibWindow as Window
|
|
|
|
# Create shadow window. (trickery is for circular import)
|
|
if not _is_pyglet_doc_run:
|
|
pyglet.window = sys.modules[__name__]
|
|
gl._create_shadow_window()
|