use master of pyglet to fix scale issue
This commit is contained in:
parent
495ece2273
commit
4daec5acc2
@ -179,8 +179,8 @@ class ClientWindow(Window):
|
|||||||
self.input_box = InputBox(x=50, y=30, width=300,
|
self.input_box = InputBox(x=50, y=30, width=300,
|
||||||
batch=self.label_batch, text='') # 实例化
|
batch=self.label_batch, text='') # 实例化
|
||||||
self.input_box.push_handlers(self)
|
self.input_box.push_handlers(self)
|
||||||
self.input_box.push_handler('on_commit', self.on_input)
|
self.input_box.set_handler('on_commit', self.on_input)
|
||||||
self.push_handlers(self.input_box)
|
self.set_handlers(self.input_box)
|
||||||
self.input_box.enabled = True
|
self.input_box.enabled = True
|
||||||
# 设置刷新率
|
# 设置刷新率
|
||||||
pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
pyglet.clock.schedule_interval(self.draw_update, float(self.SPF))
|
||||||
|
@ -1,38 +1,3 @@
|
|||||||
# ----------------------------------------------------------------------------
|
|
||||||
# pyglet
|
|
||||||
# Copyright (c) 2006-2008 Alex Holkner
|
|
||||||
# Copyright (c) 2008-2020 pyglet contributors
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions
|
|
||||||
# are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer in
|
|
||||||
# the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
# * Neither the name of pyglet nor the names of its
|
|
||||||
# contributors may be used to endorse or promote products
|
|
||||||
# derived from this software without specific prior written
|
|
||||||
# permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
||||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
||||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
||||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
# POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
"""Event dispatch framework.
|
"""Event dispatch framework.
|
||||||
|
|
||||||
All objects that produce events in pyglet implement :py:class:`~pyglet.event.EventDispatcher`,
|
All objects that produce events in pyglet implement :py:class:`~pyglet.event.EventDispatcher`,
|
||||||
@ -44,155 +9,103 @@ Event types
|
|||||||
|
|
||||||
For each event dispatcher there is a set of events that it dispatches; these
|
For each event dispatcher there is a set of events that it dispatches; these
|
||||||
correspond with the type of event handlers you can attach. Event types are
|
correspond with the type of event handlers you can attach. Event types are
|
||||||
identified by their name, for example, ''on_resize''.
|
identified by their name, for example, ''on_resize''. If you are creating a
|
||||||
|
new class which implements :py:class:`~pyglet.event.EventDispatcher`, you must call
|
||||||
If you are creating a new class which implements
|
`EventDispatcher.register_event_type` for each event type.
|
||||||
:py:class:`~pyglet.event.EventDispatcher`, or want to add new events
|
|
||||||
to an existing dispatcher, you must call `EventDispatcher.register_event_type`
|
|
||||||
for each event type:
|
|
||||||
|
|
||||||
class MyDispatcher(pyglet.event.EventDispatcher):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
MyDispatcher.register_event_type('on_resize')
|
|
||||||
|
|
||||||
Attaching event handlers
|
Attaching event handlers
|
||||||
========================
|
========================
|
||||||
|
|
||||||
An event handler is simply a function or method, that is called when system or
|
An event handler is simply a function or method. You can attach an event
|
||||||
program event happens. There are several ways to add a handler for an event.
|
handler by setting the appropriate function on the instance::
|
||||||
|
|
||||||
When the dispatcher object is available as a global variable, it is convenient
|
def on_resize(width, height):
|
||||||
to use the `event` decorator:
|
# ...
|
||||||
|
dispatcher.on_resize = on_resize
|
||||||
|
|
||||||
@window.event
|
There is also a convenience decorator that reduces typing::
|
||||||
|
|
||||||
|
@dispatcher.event
|
||||||
def on_resize(width, height):
|
def on_resize(width, height):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
Here `window` is a variable containing an instance of `pyglet.window.Window`,
|
You may prefer to subclass and override the event handlers instead::
|
||||||
which inherits from `EventDispatcher` class. This decorator assumes that
|
|
||||||
the function is named after the event. To use the decorator with a function with
|
|
||||||
another name, pass the name of the event as the argument for the decorator:
|
|
||||||
|
|
||||||
@window.event('on_resize')
|
class MyDispatcher(DispatcherClass):
|
||||||
def my_resize_handler(width, height);
|
|
||||||
# ...
|
|
||||||
|
|
||||||
The most universal way to add an event handler is to call the `push_handlers`
|
|
||||||
method on the dispatcher object:
|
|
||||||
|
|
||||||
window.push_handlers(on_resize)
|
|
||||||
window.push_handlers(on_resize=my_handler)
|
|
||||||
window.push_handlers(on_resize=obj.my_handler)
|
|
||||||
window.push_handlers(obj)
|
|
||||||
|
|
||||||
This methods accepts both positional and keyword parameters. In case of keyword
|
|
||||||
arguments, the name of the event matches the name of the argument. Otherwise,
|
|
||||||
the name of the passed function or method is used as the event name.
|
|
||||||
|
|
||||||
If an object is passed as a positional argument, all its methods that match
|
|
||||||
the names of registered events are added as handlers. For example:
|
|
||||||
|
|
||||||
class MyDispatcher(pyglet.event.EventDispatcher):
|
|
||||||
# ...
|
|
||||||
MyDispatcher.register_event_type('on_resize')
|
|
||||||
MyDispatcher.register_event_type('on_keypress')
|
|
||||||
|
|
||||||
class Listener(object):
|
|
||||||
def on_resize(self, w, h):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def on_keypress(self, key):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
def other_method(self):
|
|
||||||
# ...
|
|
||||||
|
|
||||||
dispatcher = MyDispatcher()
|
|
||||||
listener = Listener()
|
|
||||||
dispatcher.push_handlers(listener)
|
|
||||||
|
|
||||||
In this example both `listener.on_resize` and `listener.on_keypress` are
|
|
||||||
registered as handlers for respective events, but `listener.other_method` is
|
|
||||||
not affected, because it doesn't correspond to a registered event type.
|
|
||||||
|
|
||||||
Finally, yet another option is to subclass the dispatcher and override the event
|
|
||||||
handler methods::
|
|
||||||
|
|
||||||
class MyDispatcher(pyglet.event.EventDispatcher):
|
|
||||||
def on_resize(self, width, height):
|
def on_resize(self, width, height):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
If both a parent class and the child class have a handler for the same event,
|
Event handler stack
|
||||||
only the child's version of the method is invoked. If both event handlers are
|
===================
|
||||||
needed, the child's handler must explicitly call the parent's handler:
|
|
||||||
|
|
||||||
class ParentDispatcher(pyglet.event.EventDispatcher):
|
When attaching an event handler to a dispatcher using the above methods, it
|
||||||
def on_resize(self, w, h);
|
replaces any existing handler (causing the original handler to no longer be
|
||||||
# ...
|
called). Each dispatcher maintains a stack of event handlers, allowing you to
|
||||||
|
insert an event handler "above" the existing one rather than replacing it.
|
||||||
|
|
||||||
class ChildDispatcher(ParentDispatcher):
|
There are two main use cases for "pushing" event handlers:
|
||||||
def on_resize(self, w, h):
|
|
||||||
super().on_resize(w, h)
|
|
||||||
# ...
|
|
||||||
|
|
||||||
Multiple handlers for an event
|
* Temporarily intercepting the events coming from the dispatcher by pushing a
|
||||||
==============================
|
custom set of handlers onto the dispatcher, then later "popping" them all
|
||||||
|
off at once.
|
||||||
|
* Creating "chains" of event handlers, where the event propagates from the
|
||||||
|
top-most (most recently added) handler to the bottom, until a handler
|
||||||
|
takes care of it.
|
||||||
|
|
||||||
A single event can be handled by multiple handlers. The handlers are invoked in
|
Use `EventDispatcher.push_handlers` to create a new level in the stack and
|
||||||
the order opposite to the order of their registration. So, the handler
|
attach handlers to it. You can push several handlers at once::
|
||||||
registered last will be the first to be invoke when the event is fired.
|
|
||||||
|
|
||||||
An event handler can return the value `pyglet.event.EVENT_HANDLED` to prevent
|
dispatcher.push_handlers(on_resize, on_key_press)
|
||||||
running the subsequent handlers. Alternatively if the handle returns
|
|
||||||
`pyglet.event.EVENT_UNHANDLED` or doesn't return an explicit value, the next
|
|
||||||
event handler will be called (if there is one).
|
|
||||||
|
|
||||||
Stopping the event propagation is useful to prevent a single user action from
|
If your function handlers have different names to the events they handle, use
|
||||||
being handled by two unrelated systems. For instance, in game using WASD keys
|
keyword arguments::
|
||||||
for movement, should suppress movement when a chat window is opened: the
|
|
||||||
"keypress" event should be handled by the chat or by the character
|
|
||||||
movement system, but not both.
|
|
||||||
|
|
||||||
The order of execution of event handlers can be changed by assigning them
|
dispatcher.push_handlers(on_resize=my_resize, on_key_press=my_key_press)
|
||||||
priority. Default priority for all handlers is 0. If handler needs to be run
|
|
||||||
before other handlers even though it was added early, it can be assigned
|
|
||||||
priority 1. Conversely, a handler added late can be assigned priority -1 to be
|
|
||||||
run late.
|
|
||||||
|
|
||||||
Priority can be assigned by passing the `priority` named parameter to
|
After an event handler has processed an event, it is passed on to the
|
||||||
`push_handlers` method:
|
next-lowest event handler, unless the handler returns `EVENT_HANDLED`, which
|
||||||
|
prevents further propagation.
|
||||||
|
|
||||||
window.push_handlers(on_resize, priority=-1)
|
To remove all handlers on the top stack level, use
|
||||||
|
`EventDispatcher.pop_handlers`.
|
||||||
|
|
||||||
It can also be specified by using the `@priority` decorators on handler
|
Note that any handlers pushed onto the stack have precedence over the
|
||||||
functions and methods:
|
handlers set directly on the instance (for example, using the methods
|
||||||
|
described in the previous section), regardless of when they were set.
|
||||||
|
For example, handler ``foo`` is called before handler ``bar`` in the following
|
||||||
|
example::
|
||||||
|
|
||||||
@pyglet.event.priority(1)
|
dispatcher.push_handlers(on_resize=foo)
|
||||||
def on_resize(w, h):
|
dispatcher.on_resize = bar
|
||||||
# ...
|
|
||||||
|
|
||||||
class Listener(object):
|
Dispatching events
|
||||||
@pyglet.event.priority(-1)
|
==================
|
||||||
def on_resize(self, w, h):
|
|
||||||
# ...
|
|
||||||
listener = Listener()
|
|
||||||
|
|
||||||
dispatcher.push_handlers(on_resize, listener)
|
pyglet uses a single-threaded model for all application code. Event
|
||||||
|
handlers are only ever invoked as a result of calling
|
||||||
|
EventDispatcher.dispatch_events`.
|
||||||
|
|
||||||
Removing event handlers
|
It is up to the specific event dispatcher to queue relevant events until they
|
||||||
=======================
|
can be dispatched, at which point the handlers are called in the order the
|
||||||
|
events were originally generated.
|
||||||
|
|
||||||
In most cases it is not necessary to remove event handlers manually. When
|
This implies that your application runs with a main loop that continuously
|
||||||
the handler is an object method, the event dispatcher keeps only a weak
|
updates the application state and checks for new events::
|
||||||
reference to it. It means, that the dispatcher will not prevent the object from
|
|
||||||
being deleted when it goes out of scope. In that case the handler will be
|
while True:
|
||||||
silently removed from the list of handlers.
|
dispatcher.dispatch_events()
|
||||||
|
# ... additional per-frame processing
|
||||||
|
|
||||||
|
Not all event dispatchers require the call to ``dispatch_events``; check with
|
||||||
|
the particular class documentation.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
This means the following example will not work, because the pushed object
|
In order to prevent issues with garbage collection, the
|
||||||
will fall out of scope and be collected::
|
:py:class:`~pyglet.event.EventDispatcher` class only holds weak
|
||||||
|
references to pushed event handlers. That means the following example
|
||||||
|
will not work, because the pushed object will fall out of scope and be
|
||||||
|
collected::
|
||||||
|
|
||||||
dispatcher.push_handlers(MyHandlerClass())
|
dispatcher.push_handlers(MyHandlerClass())
|
||||||
|
|
||||||
@ -202,50 +115,12 @@ silently removed from the list of handlers.
|
|||||||
my_handler_instance = MyHandlerClass()
|
my_handler_instance = MyHandlerClass()
|
||||||
dispatcher.push_handlers(my_handler_instance)
|
dispatcher.push_handlers(my_handler_instance)
|
||||||
|
|
||||||
When explicit removal of handlers is required, the method `remove_handlers`
|
|
||||||
can be used. Its arguments are the same as the arguments of `push_handlers`:
|
|
||||||
|
|
||||||
dispatcher.remove_handlers(on_resize)
|
|
||||||
dispatcher.remove_handlers(on_resize=my_handler)
|
|
||||||
dispatcher.remove_handlers(on_resize=obj.my_handler)
|
|
||||||
dispatcher.remove_handlers(obj)
|
|
||||||
|
|
||||||
When an object is passed as a positional parameter to `remove_handlers`, all its
|
|
||||||
methods are removed from the handlers, regardless of their names.
|
|
||||||
|
|
||||||
Dispatching events
|
|
||||||
==================
|
|
||||||
|
|
||||||
pyglet uses a single-threaded model for all application code. Normally event
|
|
||||||
handlers are invoked while running an event loop by calling
|
|
||||||
|
|
||||||
pyglet.app.run()
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
event_loop = pyglet.app.EventLoop()
|
|
||||||
event_loop.run()
|
|
||||||
|
|
||||||
Application code can invoke events directly by calling the method
|
|
||||||
`dispatch_event` of `EventDispatcher`:
|
|
||||||
|
|
||||||
dispatcher.dispatch_event('on_resize', 640, 480)
|
|
||||||
|
|
||||||
The first argument of this method is the event name, that has to be previously
|
|
||||||
registered using `register_event_type` class method. The rest of the arguments
|
|
||||||
are pass to event handlers.
|
|
||||||
|
|
||||||
The handlers of an event fired by calling `dispatch_event` are called directly
|
|
||||||
from this method. If any of the handlers returns `EVENT_HANDLED`, then
|
|
||||||
`dispatch_event` also returns `EVENT_HANDLED` otherwise (or if there weren't
|
|
||||||
any handlers for a given event) it returns `EVENT_UNHANDLED`.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import inspect as _inspect
|
import inspect
|
||||||
|
|
||||||
from functools import partial as _partial
|
|
||||||
from weakref import WeakMethod as _WeakMethod
|
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
from weakref import WeakMethod
|
||||||
|
|
||||||
EVENT_HANDLED = True
|
EVENT_HANDLED = True
|
||||||
EVENT_UNHANDLED = None
|
EVENT_UNHANDLED = None
|
||||||
@ -257,36 +132,17 @@ class EventException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def priority(prio=0):
|
|
||||||
"""A decorator to set priority on handler functions and handlers.
|
|
||||||
|
|
||||||
Default priority is 0. Handlers with higher priority are invoked first.
|
|
||||||
Recommended priority values are 1 and -1. In most cases more than 3 priority
|
|
||||||
classes are not required.
|
|
||||||
"""
|
|
||||||
def wrap(func):
|
|
||||||
func.__priority = prio
|
|
||||||
return func
|
|
||||||
return wrap
|
|
||||||
|
|
||||||
|
|
||||||
class EventDispatcher:
|
class EventDispatcher:
|
||||||
"""Generic event dispatcher interface.
|
"""Generic event dispatcher interface.
|
||||||
|
|
||||||
See the module docstring for usage.
|
See the module docstring for usage.
|
||||||
"""
|
"""
|
||||||
# This field will contain the queues of event handlers for every supported
|
# Placeholder empty stack; real stack is created only if needed
|
||||||
# event type. It is lazily initialized when the first event handler is added
|
_event_stack = ()
|
||||||
# to the class. After that it contains a dictionary of lists, in which
|
|
||||||
# handlers are sorted according to their priority:
|
|
||||||
# {'on_event': [(priority1, handler1),
|
|
||||||
# (priority2, handler2)]}
|
|
||||||
# Handlers are invoked until any one of them returns EVENT_HANDLED
|
|
||||||
_handlers = None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_event_type(cls, name):
|
def register_event_type(cls, name):
|
||||||
"""Registers an event type with the dispatcher.
|
"""Register an event type with the dispatcher.
|
||||||
|
|
||||||
Registering event types allows the dispatcher to validate event
|
Registering event types allows the dispatcher to validate event
|
||||||
handler names as they are attached, and to search attached objects for
|
handler names as they are attached, and to search attached objects for
|
||||||
@ -295,216 +151,192 @@ class EventDispatcher:
|
|||||||
:Parameters:
|
:Parameters:
|
||||||
`name` : str
|
`name` : str
|
||||||
Name of the event to register.
|
Name of the event to register.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not hasattr(cls, 'event_types'):
|
if not hasattr(cls, 'event_types'):
|
||||||
cls.event_types = []
|
cls.event_types = []
|
||||||
cls.event_types.append(name)
|
cls.event_types.append(name)
|
||||||
|
return name
|
||||||
|
|
||||||
def _get_names_from_handler(self, handler):
|
def push_handlers(self, *args, **kwargs):
|
||||||
"""Yields event names handled by a handler function, method or object.
|
"""Push a level onto the top of the handler stack, then attach zero or
|
||||||
|
more event handlers.
|
||||||
|
|
||||||
|
If keyword arguments are given, they name the event type to attach.
|
||||||
|
Otherwise, a callable's `__name__` attribute will be used. Any other
|
||||||
|
object may also be specified, in which case it will be searched for
|
||||||
|
callables with event names.
|
||||||
"""
|
"""
|
||||||
if callable(handler) and hasattr(handler, '__name__'):
|
# Create event stack if necessary
|
||||||
# Take the name of a function or a method.
|
if type(self._event_stack) is tuple:
|
||||||
yield handler.__name__
|
self._event_stack = []
|
||||||
else:
|
|
||||||
# Iterate through all the methods of an object and yield those that
|
|
||||||
# match registered events.
|
|
||||||
for name in dir(handler):
|
|
||||||
if name in self.event_types and callable(getattr(handler, name)):
|
|
||||||
yield name
|
|
||||||
|
|
||||||
def _finalize_weak_method(self, name, weak_method):
|
# Place dict full of new handlers at beginning of stack
|
||||||
"""Called to remove dead WeakMethods from handlers."""
|
self._event_stack.insert(0, {})
|
||||||
handlers = self._handlers[name]
|
self.set_handlers(*args, **kwargs)
|
||||||
i = 0
|
|
||||||
# This is not the most efficient way of removing several elements from
|
|
||||||
# an array, but in almost all cases only one element has to be removed.
|
|
||||||
while i < len(handlers):
|
|
||||||
if handlers[i][1] is weak_method:
|
|
||||||
del handlers[i]
|
|
||||||
else:
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
@staticmethod
|
def _get_handlers(self, args, kwargs):
|
||||||
def _remove_handler_from_queue(handlers_queue, handler):
|
"""Implement handler matching on arguments for set_handlers and
|
||||||
"""Remove all instances of a handler from a queue for a single event.
|
remove_handlers.
|
||||||
|
|
||||||
If `handler` is an object, then all the methods bound to this object
|
|
||||||
will be removed from the queue.
|
|
||||||
"""
|
"""
|
||||||
i = 0
|
for obj in args:
|
||||||
# This is not the most efficient way of removing several elements from
|
if inspect.isroutine(obj):
|
||||||
# an array, but in almost all cases only one element has to be removed.
|
# Single magically named function
|
||||||
while i < len(handlers_queue):
|
name = obj.__name__
|
||||||
_, registered_handler = handlers_queue[i]
|
|
||||||
if isinstance(registered_handler, _WeakMethod):
|
|
||||||
# Wrapped in _WeakMethod in `push_handler`.
|
|
||||||
registered_handler = registered_handler()
|
|
||||||
if registered_handler is handler or getattr(registered_handler, '__self__', None) is handler:
|
|
||||||
del handlers_queue[i]
|
|
||||||
else:
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
def push_handler(self, name, handler, priority=None):
|
|
||||||
"""Adds a single event handler.
|
|
||||||
|
|
||||||
If the `handler` parameter is callable, it will be registered directly.
|
|
||||||
Otherwise, it's expected to be an object having a method with a name
|
|
||||||
matching the name of the event.
|
|
||||||
|
|
||||||
If the `priority` parameter is not None, it is used as a priotity.
|
|
||||||
Otherwise, the value specified by the @priority decorator is used. If
|
|
||||||
neither is specified the default value of 0 is used.
|
|
||||||
"""
|
|
||||||
if not hasattr(self.__class__, 'event_types'):
|
|
||||||
self.__class__.event_types = []
|
|
||||||
if name not in self.event_types:
|
if name not in self.event_types:
|
||||||
raise EventException('Unknown event "{}"'.format(name))
|
raise EventException(f'Unknown event "{name}"')
|
||||||
if not callable(handler):
|
if inspect.ismethod(obj):
|
||||||
# If handler is not callable, search for in it for a method with
|
yield name, WeakMethod(obj, partial(self._remove_handler, name))
|
||||||
# a name matching the name of the event.
|
|
||||||
if hasattr(handler, name):
|
|
||||||
method = getattr(handler, name)
|
|
||||||
if not callable(method):
|
|
||||||
raise EventException(
|
|
||||||
'Field {} on "{}" is not callable'.format(
|
|
||||||
name, repr(handler)))
|
|
||||||
handler = method
|
|
||||||
else:
|
else:
|
||||||
raise EventException(
|
yield name, obj
|
||||||
'"{}" is not callable and doesn\'t have '
|
else:
|
||||||
'a method "{}"'.format(repr(handler), name))
|
# Single instance with magically named methods
|
||||||
|
for name in dir(obj):
|
||||||
# Determine priority
|
if name in self.event_types:
|
||||||
if priority is None:
|
meth = getattr(obj, name)
|
||||||
priority = getattr(handler, '__priority', 0)
|
yield name, WeakMethod(meth, partial(self._remove_handler, name))
|
||||||
# A hack for the case when handler is a MagicMock.
|
|
||||||
if type(priority) not in (int, float):
|
|
||||||
priority = int(priority)
|
|
||||||
|
|
||||||
# Wrap methods in weak references.
|
|
||||||
if _inspect.ismethod(handler):
|
|
||||||
handler = _WeakMethod(handler, _partial(self._finalize_weak_method, name))
|
|
||||||
|
|
||||||
# Create handler queues if necessary.
|
|
||||||
if self._handlers is None:
|
|
||||||
self._handlers = {}
|
|
||||||
self.push_handlers(self)
|
|
||||||
if name not in self._handlers:
|
|
||||||
self._handlers[name] = []
|
|
||||||
|
|
||||||
handlers = self._handlers[name]
|
|
||||||
|
|
||||||
# Finding the place to insert the new handler. All the previous handlers
|
|
||||||
# have to have strictly higher priority.
|
|
||||||
#
|
|
||||||
# A binary search would theoretically be faster, but a) there's
|
|
||||||
# usually just a handful of handlers, b) we are going to shift
|
|
||||||
# the elements in the list anyway, which will take O(n), c) we are
|
|
||||||
# doing this only during handler registration, and we are more
|
|
||||||
# conserned in the efficiency of dispatching event.
|
|
||||||
i = 0
|
|
||||||
while i < len(handlers) and handlers[i][0] > priority:
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
handlers.insert(i, (priority, handler))
|
|
||||||
|
|
||||||
def push_handlers(self, *args, priority=None, **kwargs):
|
|
||||||
"""Adds new handlers to registered events.
|
|
||||||
|
|
||||||
Multiple positional and keyword arguments can be provided.
|
|
||||||
|
|
||||||
For a keyword argument, the name of the event is taken from the name
|
|
||||||
of the argument. If the argument is callable, it is used
|
|
||||||
as a handler directly. If the argument is an object, it is searched for
|
|
||||||
a method with the name matching the name of the event/argument.
|
|
||||||
|
|
||||||
When a callable named object (usually a function or a method) is passed
|
|
||||||
as a positional argument, its name is used as the event name. When
|
|
||||||
an object is passed as a positional argument, it is scanned for methods
|
|
||||||
with names that match the names of registered events. These methods are
|
|
||||||
added as handlers for the respective events.
|
|
||||||
|
|
||||||
An optional argument priority can be used to override the priority for
|
|
||||||
all the added handlers. Default priority is 0, and handlers with higher
|
|
||||||
priority will be invoked first. The priority specified in the call will
|
|
||||||
take precedence of priority, specified in @priority decorator.
|
|
||||||
|
|
||||||
EventException is raised if the event name is not registered.
|
|
||||||
"""
|
|
||||||
if not hasattr(self.__class__, 'event_types'):
|
|
||||||
self.__class__.event_types = []
|
|
||||||
|
|
||||||
for handler in args:
|
|
||||||
for name in self._get_names_from_handler(handler):
|
|
||||||
self.push_handler(name, handler, priority=priority)
|
|
||||||
|
|
||||||
for name, handler in kwargs.items():
|
for name, handler in kwargs.items():
|
||||||
self.push_handler(name, handler, priority)
|
# Function for handling given event (no magic)
|
||||||
|
if name not in self.event_types:
|
||||||
|
raise EventException(f'Unknown event "{name}"')
|
||||||
|
if inspect.ismethod(handler):
|
||||||
|
yield name, WeakMethod(handler, partial(self._remove_handler, name))
|
||||||
|
else:
|
||||||
|
yield name, handler
|
||||||
|
|
||||||
def remove_handler(self, name_or_handler=None, handler=None, name=None):
|
def set_handlers(self, *args, **kwargs):
|
||||||
"""Removes a single event handler.
|
"""Attach one or more event handlers to the top level of the handler
|
||||||
|
stack.
|
||||||
|
|
||||||
Can be called in one of the following ways:
|
See :py:meth:`~pyglet.event.EventDispatcher.push_handlers` for the accepted argument types.
|
||||||
|
|
||||||
dispatcher.remove_handler(my_handler)
|
|
||||||
dispatcher.remove_handler(handler=my_handler)
|
|
||||||
dispatcher.remove_handler("event_name", my_handler)
|
|
||||||
dispatcher.remove_handler(name="event_name", handler=my_handler)
|
|
||||||
|
|
||||||
If the event name is specified, only the queue of handlers for that
|
|
||||||
event is scanned, and the handler is removed from it. Otherwise all
|
|
||||||
handler queues are scanned and the handler is removed from all of them.
|
|
||||||
|
|
||||||
If the handler is an object, then all the registered handlers that are
|
|
||||||
bound to this object are removed. Unlike `push_handler`, the method
|
|
||||||
names in the class are not taken into account.
|
|
||||||
|
|
||||||
No error is raised if the event handler is not set.
|
|
||||||
"""
|
"""
|
||||||
if handler is None:
|
# Create event stack if necessary
|
||||||
# Called with one positional argument (example #1)
|
if type(self._event_stack) is tuple:
|
||||||
assert name is None
|
self._event_stack = [{}]
|
||||||
assert name_or_handler is not None
|
|
||||||
handler = name_or_handler
|
|
||||||
elif name is not None:
|
|
||||||
# Called with keyword arguments for handler and name (example #4)
|
|
||||||
assert name_or_handler is None
|
|
||||||
else:
|
|
||||||
# Called with two positional arguments, or only with handler as
|
|
||||||
# a keyword argument (examples #2, #3)
|
|
||||||
name = name_or_handler
|
|
||||||
|
|
||||||
if name is not None:
|
for name, handler in self._get_handlers(args, kwargs):
|
||||||
if name in self._handlers:
|
self.set_handler(name, handler)
|
||||||
self._remove_handler_from_queue(self._handlers[name], handler)
|
|
||||||
else:
|
def set_handler(self, name, handler):
|
||||||
for handlers_queue in self._handlers.values():
|
"""Attach a single event handler.
|
||||||
self._remove_handler_from_queue(handlers_queue, handler)
|
|
||||||
|
:Parameters:
|
||||||
|
`name` : str
|
||||||
|
Name of the event type to attach to.
|
||||||
|
`handler` : callable
|
||||||
|
Event handler to attach.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Create event stack if necessary
|
||||||
|
if type(self._event_stack) is tuple:
|
||||||
|
self._event_stack = [{}]
|
||||||
|
|
||||||
|
self._event_stack[0][name] = handler
|
||||||
|
|
||||||
|
def pop_handlers(self):
|
||||||
|
"""Pop the top level of event handlers off the stack.
|
||||||
|
"""
|
||||||
|
assert self._event_stack and 'No handlers pushed'
|
||||||
|
|
||||||
|
del self._event_stack[0]
|
||||||
|
|
||||||
def remove_handlers(self, *args, **kwargs):
|
def remove_handlers(self, *args, **kwargs):
|
||||||
"""Removes event handlers from the event handlers queue.
|
"""Remove event handlers from the event stack.
|
||||||
|
|
||||||
See :py:meth:`~pyglet.event.EventDispatcher.push_handlers` for the
|
See :py:meth:`~pyglet.event.EventDispatcher.push_handlers` for the
|
||||||
accepted argument types. Handlers, passed as positional arguments
|
accepted argument types. All handlers are removed from the first stack
|
||||||
are removed from all events, regardless of their names.
|
frame that contains any of the given handlers. No error is raised if
|
||||||
|
any handler does not appear in that frame, or if no stack frame
|
||||||
|
contains any of the given handlers.
|
||||||
|
|
||||||
No error is raised if any handler does not appear among
|
If the stack frame is empty after removing the handlers, it is
|
||||||
the registered handlers.
|
removed from the stack. Note that this interferes with the expected
|
||||||
|
symmetry of :py:meth:`~pyglet.event.EventDispatcher.push_handlers` and
|
||||||
|
:py:meth:`~pyglet.event.EventDispatcher.pop_handlers`.
|
||||||
"""
|
"""
|
||||||
for handler in args:
|
handlers = list(self._get_handlers(args, kwargs))
|
||||||
self.remove_handler(None, handler)
|
|
||||||
|
|
||||||
for name, handler in kwargs.items():
|
# Find the first stack frame containing any of the handlers
|
||||||
self.remove_handler(name, handler)
|
def find_frame():
|
||||||
|
for frame in self._event_stack:
|
||||||
|
for name, handler in handlers:
|
||||||
|
try:
|
||||||
|
if frame[name] == handler:
|
||||||
|
return frame
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
frame = find_frame()
|
||||||
|
|
||||||
|
# No frame matched; no error.
|
||||||
|
if not frame:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Remove each handler from the frame.
|
||||||
|
for name, handler in handlers:
|
||||||
|
try:
|
||||||
|
if frame[name] == handler:
|
||||||
|
del frame[name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Remove the frame if it's empty.
|
||||||
|
if not frame:
|
||||||
|
self._event_stack.remove(frame)
|
||||||
|
|
||||||
|
def remove_handler(self, name, handler):
|
||||||
|
"""Remove a single event handler.
|
||||||
|
|
||||||
|
The given event handler is removed from the first handler stack frame
|
||||||
|
it appears in. The handler must be the exact same callable as passed
|
||||||
|
to `set_handler`, `set_handlers` or
|
||||||
|
:py:meth:`~pyglet.event.EventDispatcher.push_handlers`; and the name
|
||||||
|
must match the event type it is bound to.
|
||||||
|
|
||||||
|
No error is raised if the event handler is not set.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`name` : str
|
||||||
|
Name of the event type to remove.
|
||||||
|
`handler` : callable
|
||||||
|
Event handler to remove.
|
||||||
|
"""
|
||||||
|
for frame in self._event_stack:
|
||||||
|
try:
|
||||||
|
if frame[name] == handler:
|
||||||
|
del frame[name]
|
||||||
|
break
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _remove_handler(self, name, handler):
|
||||||
|
"""Used internally to remove all handler instances for the given event name.
|
||||||
|
|
||||||
|
This is normally called from a dead ``WeakMethod`` to remove itself from the
|
||||||
|
event stack.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Iterate over a copy as we might mutate the list
|
||||||
|
for frame in list(self._event_stack):
|
||||||
|
|
||||||
|
if name in frame:
|
||||||
|
try:
|
||||||
|
if frame[name] == handler:
|
||||||
|
del frame[name]
|
||||||
|
if not frame:
|
||||||
|
self._event_stack.remove(frame)
|
||||||
|
except TypeError:
|
||||||
|
# weakref is already dead
|
||||||
|
pass
|
||||||
|
|
||||||
def dispatch_event(self, event_type, *args):
|
def dispatch_event(self, event_type, *args):
|
||||||
"""Dispatch a single event to the attached handlers.
|
"""Dispatch a single event to the attached handlers.
|
||||||
|
|
||||||
The event is propagated to all handlers from the top of the stack
|
The event is propagated to all handlers from from the top of the stack
|
||||||
until one returns `EVENT_HANDLED`. This method should be used only by
|
until one returns `EVENT_HANDLED`. This method should be used only by
|
||||||
:py:class:`~pyglet.event.EventDispatcher` implementors; applications
|
:py:class:`~pyglet.event.EventDispatcher` implementors; applications should call
|
||||||
should call the ``dispatch_events`` method.
|
the ``dispatch_events`` method.
|
||||||
|
|
||||||
Since pyglet 1.2, the method returns `EVENT_HANDLED` if an event
|
Since pyglet 1.2, the method returns `EVENT_HANDLED` if an event
|
||||||
handler returned `EVENT_HANDLED` or `EVENT_UNHANDLED` if all events
|
handler returned `EVENT_HANDLED` or `EVENT_UNHANDLED` if all events
|
||||||
@ -525,33 +357,49 @@ class EventDispatcher:
|
|||||||
is always ``None``.
|
is always ``None``.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not hasattr(self.__class__, 'event_types'):
|
assert hasattr(self, 'event_types'), (
|
||||||
self.__class__.event_types = []
|
"No events registered on this EventDispatcher. "
|
||||||
if event_type not in self.event_types:
|
"You need to register events with the class method "
|
||||||
raise EventException(
|
"EventDispatcher.register_event_type('event_name')."
|
||||||
'Attempted to dispatch an event of unknown event type "{}". '
|
)
|
||||||
'Event types have to be registered by calling '
|
assert event_type in self.event_types, \
|
||||||
'DispatcherClass.register_event_type({})'.format(
|
f"{event_type} not found in {self}.event_types == {self.event_types}"
|
||||||
event_type, repr(event_type)))
|
|
||||||
|
|
||||||
if self._handlers is None:
|
invoked = False
|
||||||
# Initialize the handlers with the object itself.
|
|
||||||
self._handlers = {}
|
|
||||||
self.push_handlers(self)
|
|
||||||
|
|
||||||
handlers_queue = self._handlers.get(event_type, ())
|
# Search handler stack for matching event handlers
|
||||||
for _, handler in handlers_queue:
|
for frame in list(self._event_stack):
|
||||||
if isinstance(handler, _WeakMethod):
|
handler = frame.get(event_type, None)
|
||||||
|
if not handler:
|
||||||
|
continue
|
||||||
|
if isinstance(handler, WeakMethod):
|
||||||
handler = handler()
|
handler = handler()
|
||||||
assert handler is not None
|
assert handler is not None
|
||||||
try:
|
try:
|
||||||
|
invoked = True
|
||||||
if handler(*args):
|
if handler(*args):
|
||||||
return EVENT_HANDLED
|
return EVENT_HANDLED
|
||||||
except TypeError as exception:
|
except TypeError as exception:
|
||||||
self._raise_dispatch_exception(event_type, args, handler, exception)
|
self._raise_dispatch_exception(event_type, args, handler, exception)
|
||||||
|
|
||||||
|
# Check instance for an event handler
|
||||||
|
try:
|
||||||
|
if getattr(self, event_type)(*args):
|
||||||
|
return EVENT_HANDLED
|
||||||
|
except AttributeError as e:
|
||||||
|
event_op = getattr(self, event_type, None)
|
||||||
|
if callable(event_op):
|
||||||
|
raise e
|
||||||
|
except TypeError as exception:
|
||||||
|
self._raise_dispatch_exception(event_type, args, getattr(self, event_type), exception)
|
||||||
|
else:
|
||||||
|
invoked = True
|
||||||
|
|
||||||
|
if invoked:
|
||||||
return EVENT_UNHANDLED
|
return EVENT_UNHANDLED
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _raise_dispatch_exception(event_type, args, handler, exception):
|
def _raise_dispatch_exception(event_type, args, handler, exception):
|
||||||
# A common problem in applications is having the wrong number of
|
# A common problem in applications is having the wrong number of
|
||||||
@ -566,7 +414,7 @@ class EventDispatcher:
|
|||||||
n_args = len(args)
|
n_args = len(args)
|
||||||
|
|
||||||
# Inspect the handler
|
# Inspect the handler
|
||||||
argspecs = _inspect.getfullargspec(handler)
|
argspecs = inspect.getfullargspec(handler)
|
||||||
handler_args = argspecs.args
|
handler_args = argspecs.args
|
||||||
handler_varargs = argspecs.varargs
|
handler_varargs = argspecs.varargs
|
||||||
handler_defaults = argspecs.defaults
|
handler_defaults = argspecs.defaults
|
||||||
@ -574,7 +422,7 @@ class EventDispatcher:
|
|||||||
n_handler_args = len(handler_args)
|
n_handler_args = len(handler_args)
|
||||||
|
|
||||||
# Remove "self" arg from handler if it's a bound method
|
# Remove "self" arg from handler if it's a bound method
|
||||||
if _inspect.ismethod(handler) and handler.__self__:
|
if inspect.ismethod(handler) and handler.__self__:
|
||||||
n_handler_args -= 1
|
n_handler_args -= 1
|
||||||
|
|
||||||
# Allow *args varargs to overspecify arguments
|
# Allow *args varargs to overspecify arguments
|
||||||
@ -586,15 +434,14 @@ class EventDispatcher:
|
|||||||
n_handler_args = n_args
|
n_handler_args = n_args
|
||||||
|
|
||||||
if n_handler_args != n_args:
|
if n_handler_args != n_args:
|
||||||
if _inspect.isfunction(handler) or _inspect.ismethod(handler):
|
if inspect.isfunction(handler) or inspect.ismethod(handler):
|
||||||
descr = "'%s' at %s:%d" % (handler.__name__,
|
descr = f"'{handler.__name__}' at {handler.__code__.co_filename}:{handler.__code__.co_firstlineno}"
|
||||||
handler.__code__.co_filename,
|
|
||||||
handler.__code__.co_firstlineno)
|
|
||||||
else:
|
else:
|
||||||
descr = repr(handler)
|
descr = repr(handler)
|
||||||
|
|
||||||
raise TypeError(f"The '{event_type}' event was dispatched with {len(args)} arguments, "
|
raise TypeError(f"The '{event_type}' event was dispatched with {len(args)} arguments,\n"
|
||||||
f"but your handler {descr} accepts only {len(handler_args)} arguments.")
|
f"but your handler {descr} accepts only {n_handler_args} arguments.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise exception
|
raise exception
|
||||||
|
|
||||||
@ -618,20 +465,21 @@ class EventDispatcher:
|
|||||||
"""
|
"""
|
||||||
if len(args) == 0: # @window.event()
|
if len(args) == 0: # @window.event()
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
name = func.__name__
|
func_name = func.__name__
|
||||||
self.push_handler(name, func)
|
self.set_handler(func_name, func)
|
||||||
return func
|
return func
|
||||||
return decorator
|
|
||||||
|
|
||||||
elif _inspect.isroutine(args[0]): # @window.event
|
return decorator
|
||||||
|
elif inspect.isroutine(args[0]): # @window.event
|
||||||
func = args[0]
|
func = args[0]
|
||||||
name = func.__name__
|
name = func.__name__
|
||||||
self.push_handler(name, func)
|
self.set_handler(name, func)
|
||||||
return args[0]
|
return args[0]
|
||||||
|
|
||||||
elif isinstance(args[0], str): # @window.event('on_resize')
|
elif isinstance(args[0], str): # @window.event('on_resize')
|
||||||
name = args[0]
|
name = args[0]
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
self.push_handler(name, func)
|
self.set_handler(name, func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
@ -1426,12 +1426,6 @@ class Texture(AbstractImage):
|
|||||||
order = self.tex_coords_order
|
order = self.tex_coords_order
|
||||||
self.tex_coords_order = (order[bl], order[br], order[tr], order[tl])
|
self.tex_coords_order = (order[bl], order[br], order[tr], order[tl])
|
||||||
|
|
||||||
@property
|
|
||||||
def uv(self):
|
|
||||||
"""Tuple containing the left, bottom, right, top 2D texture coordinates."""
|
|
||||||
tex_coords = self.tex_coords
|
|
||||||
return tex_coords[0], tex_coords[1], tex_coords[3], tex_coords[7]
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{}(id={}, size={}x{})".format(self.__class__.__name__, self.id, self.width, self.height)
|
return "{}(id={}, size={}x{})".format(self.__class__.__name__, self.id, self.width, self.height)
|
||||||
|
|
||||||
|
@ -78,32 +78,16 @@ from pyglet import image
|
|||||||
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
|
||||||
|
|
||||||
|
|
||||||
vertex_source = """#version 150
|
vertex_source = """#version 150 core
|
||||||
|
in vec3 translate;
|
||||||
|
in vec4 colors;
|
||||||
|
in vec3 tex_coords;
|
||||||
|
in vec2 scale;
|
||||||
in vec3 position;
|
in vec3 position;
|
||||||
in vec4 size;
|
|
||||||
in vec4 color;
|
|
||||||
in vec4 texture_uv;
|
|
||||||
in float rotation;
|
in float rotation;
|
||||||
|
|
||||||
out vec4 geo_size;
|
out vec4 vertex_colors;
|
||||||
out vec4 geo_color;
|
out vec3 texture_coords;
|
||||||
out vec4 geo_tex_coords;
|
|
||||||
out float geo_rotation;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
gl_Position = vec4(position, 1);
|
|
||||||
geo_size = size;
|
|
||||||
geo_color = color;
|
|
||||||
geo_tex_coords = texture_uv;
|
|
||||||
geo_rotation = rotation;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
geometry_source = """#version 150
|
|
||||||
// We are taking single points form the vertex shader
|
|
||||||
// and emitting 4 new vertices creating a quad/sprites
|
|
||||||
layout (points) in;
|
|
||||||
layout (triangle_strip, max_vertices = 4) out;
|
|
||||||
|
|
||||||
uniform WindowBlock
|
uniform WindowBlock
|
||||||
{
|
{
|
||||||
@ -111,102 +95,52 @@ geometry_source = """#version 150
|
|||||||
mat4 view;
|
mat4 view;
|
||||||
} window;
|
} window;
|
||||||
|
|
||||||
|
mat4 m_scale = mat4(1.0);
|
||||||
|
mat4 m_rotation = mat4(1.0);
|
||||||
|
mat4 m_translate = mat4(1.0);
|
||||||
|
|
||||||
// Since geometry shader can take multiple values from a vertex
|
void main()
|
||||||
// shader we need to define the inputs from it as arrays.
|
{
|
||||||
// In our instance we just take single values (points)
|
m_scale[0][0] = scale.x;
|
||||||
in vec4 geo_size[];
|
m_scale[1][1] = scale.y;
|
||||||
in vec4 geo_color[];
|
m_translate[3][0] = translate.x;
|
||||||
in vec4 geo_tex_coords[];
|
m_translate[3][1] = translate.y;
|
||||||
in float geo_rotation[];
|
m_translate[3][2] = translate.z;
|
||||||
|
m_rotation[0][0] = cos(-radians(rotation));
|
||||||
|
m_rotation[0][1] = sin(-radians(rotation));
|
||||||
|
m_rotation[1][0] = -sin(-radians(rotation));
|
||||||
|
m_rotation[1][1] = cos(-radians(rotation));
|
||||||
|
|
||||||
out vec2 uv;
|
gl_Position = window.projection * window.view * m_translate * m_rotation * m_scale * vec4(position, 1.0);
|
||||||
out vec4 frag_color;
|
|
||||||
|
|
||||||
void main() {
|
vertex_colors = colors;
|
||||||
|
texture_coords = tex_coords;
|
||||||
// We grab the position value from the vertex shader
|
|
||||||
vec2 center = gl_in[0].gl_Position.xy;
|
|
||||||
|
|
||||||
// Calculate the half size of the sprites for easier calculations
|
|
||||||
vec2 hsize = geo_size[0].xy / 2.0;
|
|
||||||
|
|
||||||
// Convert the rotation to radians
|
|
||||||
float angle = radians(-geo_rotation[0]);
|
|
||||||
|
|
||||||
// Create a scale vector
|
|
||||||
vec2 scale = vec2(geo_size[0][2], geo_size[0][3]);
|
|
||||||
|
|
||||||
// Create a 2d rotation matrix
|
|
||||||
mat2 rot = mat2(cos(angle), sin(angle),
|
|
||||||
-sin(angle), cos(angle));
|
|
||||||
|
|
||||||
// Calculate the left, bottom, right, top:
|
|
||||||
float tl = geo_tex_coords[0].s;
|
|
||||||
float tb = geo_tex_coords[0].t;
|
|
||||||
float tr = geo_tex_coords[0].s + geo_tex_coords[0].p;
|
|
||||||
float tt = geo_tex_coords[0].t + geo_tex_coords[0].q;
|
|
||||||
|
|
||||||
// Emit a triangle strip creating a quad (4 vertices).
|
|
||||||
// Here we need to make sure the rotation is applied before we position the sprite.
|
|
||||||
// We just use hardcoded texture coordinates here. If an atlas is used we
|
|
||||||
// can pass an additional vec4 for specific texture coordinates.
|
|
||||||
// Each EmitVertex() emits values down the shader pipeline just like a single
|
|
||||||
// run of a vertex shader, but in geomtry shaders we can do it multiple times!
|
|
||||||
|
|
||||||
// Upper left
|
|
||||||
gl_Position = window.projection * window.view * vec4(rot * vec2(-hsize.x, hsize.y) * scale + center, 0.0, 1.0);
|
|
||||||
uv = vec2(tl, tt);
|
|
||||||
frag_color = geo_color[0];
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
// lower left
|
|
||||||
gl_Position = window.projection * window.view * vec4(rot * vec2(-hsize.x, -hsize.y) * scale + center, 0.0, 1.0);
|
|
||||||
uv = vec2(tl, tb);
|
|
||||||
frag_color = geo_color[0];
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
// upper right
|
|
||||||
gl_Position = window.projection * window.view * vec4(rot * vec2(hsize.x, hsize.y) * scale + center, 0.0, 1.0);
|
|
||||||
uv = vec2(tr, tt);
|
|
||||||
frag_color = geo_color[0];
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
// lower right
|
|
||||||
gl_Position = window.projection * window.view * vec4(rot * vec2(hsize.x, -hsize.y) * scale + center, 0.0, 1.0);
|
|
||||||
uv = vec2(tr, tb);
|
|
||||||
frag_color = geo_color[0];
|
|
||||||
EmitVertex();
|
|
||||||
|
|
||||||
// We are done with this triangle strip now
|
|
||||||
EndPrimitive();
|
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fragment_source = """#version 150
|
fragment_source = """#version 150 core
|
||||||
in vec2 uv;
|
in vec4 vertex_colors;
|
||||||
in vec4 frag_color;
|
in vec3 texture_coords;
|
||||||
out vec4 final_color;
|
out vec4 final_colors;
|
||||||
|
|
||||||
uniform sampler2D sprite_texture;
|
uniform sampler2D sprite_texture;
|
||||||
|
|
||||||
void main() {
|
void main()
|
||||||
final_color = texture(sprite_texture, uv) * frag_color;
|
{
|
||||||
|
final_colors = texture(sprite_texture, texture_coords.xy) * vertex_colors;
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fragment_array_source = """#version 150 core
|
fragment_array_source = """#version 150 core
|
||||||
in vec2 uv;
|
in vec4 vertex_colors;
|
||||||
in vec4 frag_color;
|
in vec3 texture_coords;
|
||||||
|
|
||||||
out vec4 final_colors;
|
out vec4 final_colors;
|
||||||
|
|
||||||
uniform sampler2DArray sprite_texture;
|
uniform sampler2DArray sprite_texture;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
final_colors = texture(sprite_texture, uv) * frag_color;
|
final_colors = texture(sprite_texture, texture_coords) * vertex_colors;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -215,10 +149,9 @@ def get_default_shader():
|
|||||||
try:
|
try:
|
||||||
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
_default_vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||||
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
_default_frag_shader = graphics.shader.Shader(fragment_source, 'fragment')
|
||||||
frag_shader = graphics.shader.Shader(fragment_source, 'fragment')
|
default_shader_program = graphics.shader.ShaderProgram(_default_vert_shader, _default_frag_shader)
|
||||||
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
|
||||||
pyglet.gl.current_context.pyglet_sprite_default_shader = default_shader_program
|
pyglet.gl.current_context.pyglet_sprite_default_shader = default_shader_program
|
||||||
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||||
|
|
||||||
@ -227,10 +160,9 @@ def get_default_array_shader():
|
|||||||
try:
|
try:
|
||||||
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
_default_vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||||
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
_default_array_frag_shader = graphics.shader.Shader(fragment_array_source, 'fragment')
|
||||||
frag_shader = graphics.shader.Shader(fragment_array_source, 'fragment')
|
default_shader_program = graphics.shader.ShaderProgram(_default_vert_shader, _default_array_frag_shader)
|
||||||
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
|
||||||
pyglet.gl.current_context.pyglet_sprite_default_array_shader = default_shader_program
|
pyglet.gl.current_context.pyglet_sprite_default_array_shader = default_shader_program
|
||||||
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||||
|
|
||||||
@ -312,7 +244,8 @@ class Sprite(event.EventDispatcher):
|
|||||||
_frame_index = 0
|
_frame_index = 0
|
||||||
_paused = False
|
_paused = False
|
||||||
_rotation = 0
|
_rotation = 0
|
||||||
_rgba = [255, 255, 255, 255]
|
_opacity = 255
|
||||||
|
_rgb = (255, 255, 255)
|
||||||
_scale = 1.0
|
_scale = 1.0
|
||||||
_scale_x = 1.0
|
_scale_x = 1.0
|
||||||
_scale_y = 1.0
|
_scale_y = 1.0
|
||||||
@ -326,8 +259,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
blend_dest=GL_ONE_MINUS_SRC_ALPHA,
|
blend_dest=GL_ONE_MINUS_SRC_ALPHA,
|
||||||
batch=None,
|
batch=None,
|
||||||
group=None,
|
group=None,
|
||||||
subpixel=False,
|
subpixel=False):
|
||||||
program=None):
|
|
||||||
"""Create a sprite.
|
"""Create a sprite.
|
||||||
|
|
||||||
:Parameters:
|
:Parameters:
|
||||||
@ -352,11 +284,6 @@ class Sprite(event.EventDispatcher):
|
|||||||
`subpixel` : bool
|
`subpixel` : bool
|
||||||
Allow floating-point coordinates for the sprite. By default,
|
Allow floating-point coordinates for the sprite. By default,
|
||||||
coordinates are restricted to integer values.
|
coordinates are restricted to integer values.
|
||||||
`program` : `~pyglet.graphics.shader.ShaderProgram`
|
|
||||||
A custom shader to use. This shader program must contain the
|
|
||||||
exact same attribute names and types as the default shader.
|
|
||||||
The class methods and properties depend on this, and will
|
|
||||||
crash otherwise.
|
|
||||||
"""
|
"""
|
||||||
self._x = x
|
self._x = x
|
||||||
self._y = y
|
self._y = y
|
||||||
@ -372,46 +299,26 @@ class Sprite(event.EventDispatcher):
|
|||||||
else:
|
else:
|
||||||
self._texture = img.get_texture()
|
self._texture = img.get_texture()
|
||||||
|
|
||||||
if not program:
|
|
||||||
if isinstance(img, image.TextureArrayRegion):
|
|
||||||
self._program = get_default_array_shader()
|
|
||||||
else:
|
|
||||||
self._program = get_default_shader()
|
|
||||||
else:
|
|
||||||
self._program = program
|
|
||||||
|
|
||||||
self._batch = batch or graphics.get_default_batch()
|
self._batch = batch or graphics.get_default_batch()
|
||||||
self._user_group = group
|
|
||||||
self._group = self.group_class(self._texture, blend_src, blend_dest, self.program, group)
|
self._group = self.group_class(self._texture, blend_src, blend_dest, self.program, group)
|
||||||
self._subpixel = subpixel
|
self._subpixel = subpixel
|
||||||
|
|
||||||
self._create_vertex_list()
|
self._create_vertex_list()
|
||||||
|
|
||||||
def _create_vertex_list(self):
|
|
||||||
texture = self._texture
|
|
||||||
self._vertex_list = self.program.vertex_list(
|
|
||||||
1, GL_POINTS, self._batch, self._group,
|
|
||||||
position=('f', (self._x, self._y, self._z)),
|
|
||||||
size=('f', (texture.width, texture.height, 1, 1)),
|
|
||||||
color=('Bn', self._rgba),
|
|
||||||
texture_uv=('f', texture.uv),
|
|
||||||
rotation=('f', (self._rotation,)))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def program(self):
|
def program(self):
|
||||||
return self._program
|
if isinstance(self._img, image.TextureArrayRegion):
|
||||||
|
program = get_default_array_shader()
|
||||||
|
else:
|
||||||
|
program = get_default_shader()
|
||||||
|
|
||||||
@program.setter
|
return program
|
||||||
def program(self, program):
|
|
||||||
if self._program == program:
|
def __del__(self):
|
||||||
return
|
try:
|
||||||
self._group = self.group_class(self._texture,
|
if self._vertex_list is not None:
|
||||||
self._group.blend_src,
|
self._vertex_list.delete()
|
||||||
self._group.blend_dest,
|
except:
|
||||||
program,
|
pass
|
||||||
self._user_group)
|
|
||||||
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, self._batch)
|
|
||||||
self._program = program
|
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""Force immediate removal of the sprite from video memory.
|
"""Force immediate removal of the sprite from video memory.
|
||||||
@ -425,6 +332,8 @@ class Sprite(event.EventDispatcher):
|
|||||||
self._vertex_list.delete()
|
self._vertex_list.delete()
|
||||||
self._vertex_list = None
|
self._vertex_list = None
|
||||||
self._texture = None
|
self._texture = None
|
||||||
|
|
||||||
|
# Easy way to break circular reference, speeds up GC
|
||||||
self._group = None
|
self._group = None
|
||||||
|
|
||||||
def _animate(self, dt):
|
def _animate(self, dt):
|
||||||
@ -464,7 +373,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if batch is not None and self._batch is not None:
|
if batch is not None and self._batch is not None:
|
||||||
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, batch)
|
self._batch.migrate(self._vertex_list, GL_TRIANGLES, self._group, batch)
|
||||||
self._batch = batch
|
self._batch = batch
|
||||||
else:
|
else:
|
||||||
self._vertex_list.delete()
|
self._vertex_list.delete()
|
||||||
@ -491,7 +400,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
self._group.blend_dest,
|
self._group.blend_dest,
|
||||||
self._group.program,
|
self._group.program,
|
||||||
group)
|
group)
|
||||||
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, self._batch)
|
self._batch.migrate(self._vertex_list, GL_TRIANGLES, self._group, self._batch)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image(self):
|
def image(self):
|
||||||
@ -519,6 +428,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
clock.schedule_once(self._animate, self._next_dt)
|
clock.schedule_once(self._animate, self._next_dt)
|
||||||
else:
|
else:
|
||||||
self._set_texture(img.get_texture())
|
self._set_texture(img.get_texture())
|
||||||
|
self._update_position()
|
||||||
|
|
||||||
def _set_texture(self, texture):
|
def _set_texture(self, texture):
|
||||||
if texture.id is not self._texture.id:
|
if texture.id is not self._texture.id:
|
||||||
@ -531,9 +441,35 @@ class Sprite(event.EventDispatcher):
|
|||||||
self._texture = texture
|
self._texture = texture
|
||||||
self._create_vertex_list()
|
self._create_vertex_list()
|
||||||
else:
|
else:
|
||||||
self._vertex_list.texture_uv[:] = texture.uv
|
self._vertex_list.tex_coords[:] = texture.tex_coords
|
||||||
self._texture = texture
|
self._texture = texture
|
||||||
|
|
||||||
|
def _create_vertex_list(self):
|
||||||
|
self._vertex_list = self.program.vertex_list_indexed(
|
||||||
|
4, GL_TRIANGLES, [0, 1, 2, 0, 2, 3], self._batch, self._group,
|
||||||
|
colors=('Bn', (*self._rgb, int(self._opacity)) * 4),
|
||||||
|
translate=('f', (self._x, self._y, self._z) * 4),
|
||||||
|
scale=('f', (self._scale*self._scale_x, self._scale*self._scale_y) * 4),
|
||||||
|
rotation=('f', (self._rotation,) * 4),
|
||||||
|
tex_coords=('f', self._texture.tex_coords))
|
||||||
|
self._update_position()
|
||||||
|
|
||||||
|
def _update_position(self):
|
||||||
|
if not self._visible:
|
||||||
|
self._vertex_list.position[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||||
|
else:
|
||||||
|
img = self._texture
|
||||||
|
x1 = -img.anchor_x
|
||||||
|
y1 = -img.anchor_y
|
||||||
|
x2 = x1 + img.width
|
||||||
|
y2 = y1 + img.height
|
||||||
|
vertices = (x1, y1, 0, x2, y1, 0, x2, y2, 0, x1, y2, 0)
|
||||||
|
|
||||||
|
if not self._subpixel:
|
||||||
|
self._vertex_list.position[:] = tuple(map(int, vertices))
|
||||||
|
else:
|
||||||
|
self._vertex_list.position[:] = vertices
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def position(self):
|
def position(self):
|
||||||
"""The (x, y, z) coordinates of the sprite, as a tuple.
|
"""The (x, y, z) coordinates of the sprite, as a tuple.
|
||||||
@ -551,7 +487,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@position.setter
|
@position.setter
|
||||||
def position(self, position):
|
def position(self, position):
|
||||||
self._x, self._y, self._z = position
|
self._x, self._y, self._z = position
|
||||||
self._vertex_list.position[:] = position
|
self._vertex_list.translate[:] = position * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x(self):
|
def x(self):
|
||||||
@ -564,7 +500,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@x.setter
|
@x.setter
|
||||||
def x(self, x):
|
def x(self, x):
|
||||||
self._x = x
|
self._x = x
|
||||||
self._vertex_list.position[:] = x, self._y, self._z
|
self._vertex_list.translate[:] = (x, self._y, self._z) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self):
|
def y(self):
|
||||||
@ -577,7 +513,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@y.setter
|
@y.setter
|
||||||
def y(self, y):
|
def y(self, y):
|
||||||
self._y = y
|
self._y = y
|
||||||
self._vertex_list.position[:] = self._x, y, self._z
|
self._vertex_list.translate[:] = (self._x, y, self._z) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def z(self):
|
def z(self):
|
||||||
@ -590,7 +526,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@z.setter
|
@z.setter
|
||||||
def z(self, z):
|
def z(self, z):
|
||||||
self._z = z
|
self._z = z
|
||||||
self._vertex_list.position[:] = self._x, self._y, z
|
self._vertex_list.translate[:] = (self._x, self._y, z) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rotation(self):
|
def rotation(self):
|
||||||
@ -606,7 +542,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@rotation.setter
|
@rotation.setter
|
||||||
def rotation(self, rotation):
|
def rotation(self, rotation):
|
||||||
self._rotation = rotation
|
self._rotation = rotation
|
||||||
self._vertex_list.rotation[0] = self._rotation
|
self._vertex_list.rotation[:] = (self._rotation,) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scale(self):
|
def scale(self):
|
||||||
@ -622,7 +558,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@scale.setter
|
@scale.setter
|
||||||
def scale(self, scale):
|
def scale(self, scale):
|
||||||
self._scale = scale
|
self._scale = scale
|
||||||
self._vertex_list.scale[:] = scale * self._scale_x, scale * self._scale_y
|
self._vertex_list.scale[:] = (scale * self._scale_x, scale * self._scale_y) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scale_x(self):
|
def scale_x(self):
|
||||||
@ -638,7 +574,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@scale_x.setter
|
@scale_x.setter
|
||||||
def scale_x(self, scale_x):
|
def scale_x(self, scale_x):
|
||||||
self._scale_x = scale_x
|
self._scale_x = scale_x
|
||||||
self._vertex_list.scale[:] = self._scale * scale_x, self._scale * self._scale_y
|
self._vertex_list.scale[:] = (self._scale * scale_x, self._scale * self._scale_y) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def scale_y(self):
|
def scale_y(self):
|
||||||
@ -654,7 +590,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@scale_y.setter
|
@scale_y.setter
|
||||||
def scale_y(self, scale_y):
|
def scale_y(self, scale_y):
|
||||||
self._scale_y = scale_y
|
self._scale_y = scale_y
|
||||||
self._vertex_list.scale[:] = self._scale * self._scale_x, self._scale * scale_y
|
self._vertex_list.scale[:] = (self._scale * self._scale_x, self._scale * scale_y) * 4
|
||||||
|
|
||||||
def update(self, x=None, y=None, z=None, rotation=None, scale=None, scale_x=None, scale_y=None):
|
def update(self, x=None, y=None, z=None, rotation=None, scale=None, scale_x=None, scale_y=None):
|
||||||
"""Simultaneously change the position, rotation or scale.
|
"""Simultaneously change the position, rotation or scale.
|
||||||
@ -693,11 +629,11 @@ class Sprite(event.EventDispatcher):
|
|||||||
translations_outdated = True
|
translations_outdated = True
|
||||||
|
|
||||||
if translations_outdated:
|
if translations_outdated:
|
||||||
self._vertex_list.position[:] = (self._x, self._y, self._z)
|
self._vertex_list.translate[:] = (self._x, self._y, self._z) * 4
|
||||||
|
|
||||||
if rotation is not None and rotation != self._rotation:
|
if rotation is not None and rotation != self._rotation:
|
||||||
self._rotation = rotation
|
self._rotation = rotation
|
||||||
self._vertex_list.rotation[:] = rotation
|
self._vertex_list.rotation[:] = (rotation,) * 4
|
||||||
|
|
||||||
scales_outdated = False
|
scales_outdated = False
|
||||||
|
|
||||||
@ -713,7 +649,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
scales_outdated = True
|
scales_outdated = True
|
||||||
|
|
||||||
if scales_outdated:
|
if scales_outdated:
|
||||||
self._vertex_list.scale[:] = self._scale * self._scale_x, self._scale * self._scale_y
|
self._vertex_list.scale[:] = (self._scale * self._scale_x, self._scale * self._scale_y) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self):
|
def width(self):
|
||||||
@ -755,12 +691,12 @@ class Sprite(event.EventDispatcher):
|
|||||||
|
|
||||||
:type: int
|
:type: int
|
||||||
"""
|
"""
|
||||||
return self._rgba[3]
|
return self._opacity
|
||||||
|
|
||||||
@opacity.setter
|
@opacity.setter
|
||||||
def opacity(self, opacity):
|
def opacity(self, opacity):
|
||||||
self._rgba[3] = opacity
|
self._opacity = opacity
|
||||||
self._vertex_list.color[:] = self._rgba
|
self._vertex_list.colors[:] = (*self._rgb, int(self._opacity)) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self):
|
def color(self):
|
||||||
@ -774,12 +710,12 @@ class Sprite(event.EventDispatcher):
|
|||||||
|
|
||||||
:type: (int, int, int)
|
:type: (int, int, int)
|
||||||
"""
|
"""
|
||||||
return self._rgba[:3]
|
return self._rgb
|
||||||
|
|
||||||
@color.setter
|
@color.setter
|
||||||
def color(self, rgb):
|
def color(self, rgb):
|
||||||
self._rgba[:3] = list(map(int, rgb))
|
self._rgb = list(map(int, rgb))
|
||||||
self._vertex_list.color[:] = self._rgba
|
self._vertex_list.colors[:] = (*self._rgb, int(self._opacity)) * 4
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def visible(self):
|
def visible(self):
|
||||||
@ -792,7 +728,7 @@ class Sprite(event.EventDispatcher):
|
|||||||
@visible.setter
|
@visible.setter
|
||||||
def visible(self, visible):
|
def visible(self, visible):
|
||||||
self._visible = visible
|
self._visible = visible
|
||||||
self._vertex_list.texture_uv[:] = (0, 0, 0, 0) if not visible else self._texture.uv
|
self._update_position()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def paused(self):
|
def paused(self):
|
||||||
@ -846,16 +782,9 @@ class Sprite(event.EventDispatcher):
|
|||||||
efficiently.
|
efficiently.
|
||||||
"""
|
"""
|
||||||
self._group.set_state_recursive()
|
self._group.set_state_recursive()
|
||||||
self._vertex_list.draw(GL_POINTS)
|
self._vertex_list.draw(GL_TRIANGLES)
|
||||||
self._group.unset_state_recursive()
|
self._group.unset_state_recursive()
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
try:
|
|
||||||
if self._vertex_list is not None:
|
|
||||||
self._vertex_list.delete()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if _is_pyglet_doc_run:
|
if _is_pyglet_doc_run:
|
||||||
def on_animation_end(self):
|
def on_animation_end(self):
|
||||||
"""The sprite animation reached the final frame.
|
"""The sprite animation reached the final frame.
|
||||||
@ -869,3 +798,46 @@ class Sprite(event.EventDispatcher):
|
|||||||
|
|
||||||
|
|
||||||
Sprite.register_event_type('on_animation_end')
|
Sprite.register_event_type('on_animation_end')
|
||||||
|
|
||||||
|
|
||||||
|
class AdvancedSprite(pyglet.sprite.Sprite):
|
||||||
|
"""Is a sprite that lets you change the shader program during initialization and after
|
||||||
|
For advanced users who understand shaders."""
|
||||||
|
def __init__(self,
|
||||||
|
img, x=0, y=0, z=0,
|
||||||
|
blend_src=GL_SRC_ALPHA,
|
||||||
|
blend_dest=GL_ONE_MINUS_SRC_ALPHA,
|
||||||
|
batch=None,
|
||||||
|
group=None,
|
||||||
|
subpixel=False,
|
||||||
|
program=None):
|
||||||
|
|
||||||
|
self._program = program
|
||||||
|
|
||||||
|
if not program:
|
||||||
|
if isinstance(img, image.TextureArrayRegion):
|
||||||
|
self._program = get_default_array_shader()
|
||||||
|
else:
|
||||||
|
self._program = get_default_shader()
|
||||||
|
|
||||||
|
super().__init__(img, x, y, z, blend_src, blend_dest, batch, group, subpixel)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def program(self):
|
||||||
|
return self._program
|
||||||
|
|
||||||
|
@program.setter
|
||||||
|
def program(self, program):
|
||||||
|
if self._program == program:
|
||||||
|
return
|
||||||
|
self._group = self.group_class(self._texture,
|
||||||
|
self._group.blend_src,
|
||||||
|
self._group.blend_dest,
|
||||||
|
program,
|
||||||
|
self._group)
|
||||||
|
self._batch.migrate(self._vertex_list, GL_TRIANGLES, self._group, self._batch)
|
||||||
|
self._program = program
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
pub mod python_class {
|
pub mod python_class {
|
||||||
use pyo3::class::basic::CompareOp;
|
use pyo3::class::basic::CompareOp;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::types::PySlice;
|
||||||
|
|
||||||
use crate::math::matrix::{Matrix3, Matrix4};
|
use crate::math::matrix::{Matrix3, Matrix4};
|
||||||
use crate::math::vector::{Vector2, Vector3, Vector4, VectorTrait};
|
use crate::math::vector::{Vector2, Vector3, Vector4, VectorTrait};
|
||||||
@ -131,6 +132,13 @@ pub mod python_class {
|
|||||||
return format!("Vector2_rs({}, {})", self.data.x, self.data.y);
|
return format!("Vector2_rs({}, {})", self.data.x, self.data.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn __getitem__(&self, item: &PyAny) -> PyResult<&PyAny> {
|
||||||
|
// if item.is_instance_of::<PySlice>().unwrap() {
|
||||||
|
// let item = item.extract::<PySlice>().unwrap();
|
||||||
|
// let indices = item.indices().unwrap();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// getter and setter
|
// getter and setter
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
|
Loading…
Reference in New Issue
Block a user