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,
|
||||
batch=self.label_batch, text='') # 实例化
|
||||
self.input_box.push_handlers(self)
|
||||
self.input_box.push_handler('on_commit', self.on_input)
|
||||
self.push_handlers(self.input_box)
|
||||
self.input_box.set_handler('on_commit', self.on_input)
|
||||
self.set_handlers(self.input_box)
|
||||
self.input_box.enabled = True
|
||||
# 设置刷新率
|
||||
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.
|
||||
|
||||
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
|
||||
correspond with the type of event handlers you can attach. Event types are
|
||||
identified by their name, for example, ''on_resize''.
|
||||
|
||||
If you are creating a new class which implements
|
||||
: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')
|
||||
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
|
||||
`EventDispatcher.register_event_type` for each event type.
|
||||
|
||||
Attaching event handlers
|
||||
========================
|
||||
|
||||
An event handler is simply a function or method, that is called when system or
|
||||
program event happens. There are several ways to add a handler for an event.
|
||||
An event handler is simply a function or method. You can attach an event
|
||||
handler by setting the appropriate function on the instance::
|
||||
|
||||
When the dispatcher object is available as a global variable, it is convenient
|
||||
to use the `event` decorator:
|
||||
def on_resize(width, height):
|
||||
# ...
|
||||
dispatcher.on_resize = on_resize
|
||||
|
||||
@window.event
|
||||
There is also a convenience decorator that reduces typing::
|
||||
|
||||
@dispatcher.event
|
||||
def on_resize(width, height):
|
||||
# ...
|
||||
|
||||
Here `window` is a variable containing an instance of `pyglet.window.Window`,
|
||||
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:
|
||||
You may prefer to subclass and override the event handlers instead::
|
||||
|
||||
@window.event('on_resize')
|
||||
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):
|
||||
class MyDispatcher(DispatcherClass):
|
||||
def on_resize(self, width, height):
|
||||
# ...
|
||||
|
||||
If both a parent class and the child class have a handler for the same event,
|
||||
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:
|
||||
Event handler stack
|
||||
===================
|
||||
|
||||
class ParentDispatcher(pyglet.event.EventDispatcher):
|
||||
def on_resize(self, w, h);
|
||||
# ...
|
||||
When attaching an event handler to a dispatcher using the above methods, it
|
||||
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):
|
||||
def on_resize(self, w, h):
|
||||
super().on_resize(w, h)
|
||||
# ...
|
||||
There are two main use cases for "pushing" event handlers:
|
||||
|
||||
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
|
||||
the order opposite to the order of their registration. So, the handler
|
||||
registered last will be the first to be invoke when the event is fired.
|
||||
Use `EventDispatcher.push_handlers` to create a new level in the stack and
|
||||
attach handlers to it. You can push several handlers at once::
|
||||
|
||||
An event handler can return the value `pyglet.event.EVENT_HANDLED` to prevent
|
||||
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).
|
||||
dispatcher.push_handlers(on_resize, on_key_press)
|
||||
|
||||
Stopping the event propagation is useful to prevent a single user action from
|
||||
being handled by two unrelated systems. For instance, in game using WASD keys
|
||||
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.
|
||||
If your function handlers have different names to the events they handle, use
|
||||
keyword arguments::
|
||||
|
||||
The order of execution of event handlers can be changed by assigning them
|
||||
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.
|
||||
dispatcher.push_handlers(on_resize=my_resize, on_key_press=my_key_press)
|
||||
|
||||
Priority can be assigned by passing the `priority` named parameter to
|
||||
`push_handlers` method:
|
||||
After an event handler has processed an event, it is passed on to the
|
||||
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
|
||||
functions and methods:
|
||||
Note that any handlers pushed onto the stack have precedence over the
|
||||
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)
|
||||
def on_resize(w, h):
|
||||
# ...
|
||||
dispatcher.push_handlers(on_resize=foo)
|
||||
dispatcher.on_resize = bar
|
||||
|
||||
class Listener(object):
|
||||
@pyglet.event.priority(-1)
|
||||
def on_resize(self, w, h):
|
||||
# ...
|
||||
listener = Listener()
|
||||
Dispatching events
|
||||
==================
|
||||
|
||||
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
|
||||
the handler is an object method, the event dispatcher keeps only a weak
|
||||
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
|
||||
silently removed from the list of handlers.
|
||||
This implies that your application runs with a main loop that continuously
|
||||
updates the application state and checks for new events::
|
||||
|
||||
while True:
|
||||
dispatcher.dispatch_events()
|
||||
# ... additional per-frame processing
|
||||
|
||||
Not all event dispatchers require the call to ``dispatch_events``; check with
|
||||
the particular class documentation.
|
||||
|
||||
.. note::
|
||||
|
||||
This means the following example will not work, because the pushed object
|
||||
will fall out of scope and be collected::
|
||||
In order to prevent issues with garbage collection, the
|
||||
: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())
|
||||
|
||||
@ -202,50 +115,12 @@ silently removed from the list of handlers.
|
||||
my_handler_instance = MyHandlerClass()
|
||||
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
|
||||
|
||||
from functools import partial as _partial
|
||||
from weakref import WeakMethod as _WeakMethod
|
||||
import inspect
|
||||
|
||||
from functools import partial
|
||||
from weakref import WeakMethod
|
||||
|
||||
EVENT_HANDLED = True
|
||||
EVENT_UNHANDLED = None
|
||||
@ -257,36 +132,17 @@ class EventException(Exception):
|
||||
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:
|
||||
"""Generic event dispatcher interface.
|
||||
|
||||
See the module docstring for usage.
|
||||
"""
|
||||
# This field will contain the queues of event handlers for every supported
|
||||
# event type. It is lazily initialized when the first event handler is added
|
||||
# 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
|
||||
# Placeholder empty stack; real stack is created only if needed
|
||||
_event_stack = ()
|
||||
|
||||
@classmethod
|
||||
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
|
||||
handler names as they are attached, and to search attached objects for
|
||||
@ -295,216 +151,192 @@ class EventDispatcher:
|
||||
:Parameters:
|
||||
`name` : str
|
||||
Name of the event to register.
|
||||
|
||||
"""
|
||||
if not hasattr(cls, 'event_types'):
|
||||
cls.event_types = []
|
||||
cls.event_types.append(name)
|
||||
return name
|
||||
|
||||
def _get_names_from_handler(self, handler):
|
||||
"""Yields event names handled by a handler function, method or object.
|
||||
def push_handlers(self, *args, **kwargs):
|
||||
"""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__'):
|
||||
# Take the name of a function or a method.
|
||||
yield handler.__name__
|
||||
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
|
||||
# Create event stack if necessary
|
||||
if type(self._event_stack) is tuple:
|
||||
self._event_stack = []
|
||||
|
||||
def _finalize_weak_method(self, name, weak_method):
|
||||
"""Called to remove dead WeakMethods from handlers."""
|
||||
handlers = self._handlers[name]
|
||||
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
|
||||
# Place dict full of new handlers at beginning of stack
|
||||
self._event_stack.insert(0, {})
|
||||
self.set_handlers(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _remove_handler_from_queue(handlers_queue, handler):
|
||||
"""Remove all instances of a handler from a queue for a single event.
|
||||
|
||||
If `handler` is an object, then all the methods bound to this object
|
||||
will be removed from the queue.
|
||||
def _get_handlers(self, args, kwargs):
|
||||
"""Implement handler matching on arguments for set_handlers and
|
||||
remove_handlers.
|
||||
"""
|
||||
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_queue):
|
||||
_, 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 = []
|
||||
for obj in args:
|
||||
if inspect.isroutine(obj):
|
||||
# Single magically named function
|
||||
name = obj.__name__
|
||||
if name not in self.event_types:
|
||||
raise EventException('Unknown event "{}"'.format(name))
|
||||
if not callable(handler):
|
||||
# If handler is not callable, search for in it for a method with
|
||||
# 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
|
||||
raise EventException(f'Unknown event "{name}"')
|
||||
if inspect.ismethod(obj):
|
||||
yield name, WeakMethod(obj, partial(self._remove_handler, name))
|
||||
else:
|
||||
raise EventException(
|
||||
'"{}" is not callable and doesn\'t have '
|
||||
'a method "{}"'.format(repr(handler), name))
|
||||
|
||||
# Determine priority
|
||||
if priority is None:
|
||||
priority = getattr(handler, '__priority', 0)
|
||||
# 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)
|
||||
yield name, obj
|
||||
else:
|
||||
# Single instance with magically named methods
|
||||
for name in dir(obj):
|
||||
if name in self.event_types:
|
||||
meth = getattr(obj, name)
|
||||
yield name, WeakMethod(meth, partial(self._remove_handler, name))
|
||||
|
||||
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):
|
||||
"""Removes a single event handler.
|
||||
def set_handlers(self, *args, **kwargs):
|
||||
"""Attach one or more event handlers to the top level of the handler
|
||||
stack.
|
||||
|
||||
Can be called in one of the following ways:
|
||||
|
||||
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.
|
||||
See :py:meth:`~pyglet.event.EventDispatcher.push_handlers` for the accepted argument types.
|
||||
"""
|
||||
if handler is None:
|
||||
# Called with one positional argument (example #1)
|
||||
assert name is None
|
||||
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
|
||||
# Create event stack if necessary
|
||||
if type(self._event_stack) is tuple:
|
||||
self._event_stack = [{}]
|
||||
|
||||
if name is not None:
|
||||
if name in self._handlers:
|
||||
self._remove_handler_from_queue(self._handlers[name], handler)
|
||||
else:
|
||||
for handlers_queue in self._handlers.values():
|
||||
self._remove_handler_from_queue(handlers_queue, handler)
|
||||
for name, handler in self._get_handlers(args, kwargs):
|
||||
self.set_handler(name, handler)
|
||||
|
||||
def set_handler(self, name, handler):
|
||||
"""Attach a single event 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):
|
||||
"""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
|
||||
accepted argument types. Handlers, passed as positional arguments
|
||||
are removed from all events, regardless of their names.
|
||||
accepted argument types. All handlers are removed from the first stack
|
||||
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
|
||||
the registered handlers.
|
||||
If the stack frame is empty after removing the handlers, it is
|
||||
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:
|
||||
self.remove_handler(None, handler)
|
||||
handlers = list(self._get_handlers(args, kwargs))
|
||||
|
||||
for name, handler in kwargs.items():
|
||||
self.remove_handler(name, handler)
|
||||
# Find the first stack frame containing any of the handlers
|
||||
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):
|
||||
"""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
|
||||
:py:class:`~pyglet.event.EventDispatcher` implementors; applications
|
||||
should call the ``dispatch_events`` method.
|
||||
:py:class:`~pyglet.event.EventDispatcher` implementors; applications should call
|
||||
the ``dispatch_events`` method.
|
||||
|
||||
Since pyglet 1.2, the method returns `EVENT_HANDLED` if an event
|
||||
handler returned `EVENT_HANDLED` or `EVENT_UNHANDLED` if all events
|
||||
@ -525,33 +357,49 @@ class EventDispatcher:
|
||||
is always ``None``.
|
||||
|
||||
"""
|
||||
if not hasattr(self.__class__, 'event_types'):
|
||||
self.__class__.event_types = []
|
||||
if event_type not in self.event_types:
|
||||
raise EventException(
|
||||
'Attempted to dispatch an event of unknown event type "{}". '
|
||||
'Event types have to be registered by calling '
|
||||
'DispatcherClass.register_event_type({})'.format(
|
||||
event_type, repr(event_type)))
|
||||
assert hasattr(self, 'event_types'), (
|
||||
"No events registered on this EventDispatcher. "
|
||||
"You need to register events with the class method "
|
||||
"EventDispatcher.register_event_type('event_name')."
|
||||
)
|
||||
assert event_type in self.event_types, \
|
||||
f"{event_type} not found in {self}.event_types == {self.event_types}"
|
||||
|
||||
if self._handlers is None:
|
||||
# Initialize the handlers with the object itself.
|
||||
self._handlers = {}
|
||||
self.push_handlers(self)
|
||||
invoked = False
|
||||
|
||||
handlers_queue = self._handlers.get(event_type, ())
|
||||
for _, handler in handlers_queue:
|
||||
if isinstance(handler, _WeakMethod):
|
||||
# Search handler stack for matching event handlers
|
||||
for frame in list(self._event_stack):
|
||||
handler = frame.get(event_type, None)
|
||||
if not handler:
|
||||
continue
|
||||
if isinstance(handler, WeakMethod):
|
||||
handler = handler()
|
||||
assert handler is not None
|
||||
try:
|
||||
invoked = True
|
||||
if handler(*args):
|
||||
return EVENT_HANDLED
|
||||
except TypeError as 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 False
|
||||
|
||||
@staticmethod
|
||||
def _raise_dispatch_exception(event_type, args, handler, exception):
|
||||
# A common problem in applications is having the wrong number of
|
||||
@ -566,7 +414,7 @@ class EventDispatcher:
|
||||
n_args = len(args)
|
||||
|
||||
# Inspect the handler
|
||||
argspecs = _inspect.getfullargspec(handler)
|
||||
argspecs = inspect.getfullargspec(handler)
|
||||
handler_args = argspecs.args
|
||||
handler_varargs = argspecs.varargs
|
||||
handler_defaults = argspecs.defaults
|
||||
@ -574,7 +422,7 @@ class EventDispatcher:
|
||||
n_handler_args = len(handler_args)
|
||||
|
||||
# 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
|
||||
|
||||
# Allow *args varargs to overspecify arguments
|
||||
@ -586,15 +434,14 @@ class EventDispatcher:
|
||||
n_handler_args = n_args
|
||||
|
||||
if n_handler_args != n_args:
|
||||
if _inspect.isfunction(handler) or _inspect.ismethod(handler):
|
||||
descr = "'%s' at %s:%d" % (handler.__name__,
|
||||
handler.__code__.co_filename,
|
||||
handler.__code__.co_firstlineno)
|
||||
if inspect.isfunction(handler) or inspect.ismethod(handler):
|
||||
descr = f"'{handler.__name__}' at {handler.__code__.co_filename}:{handler.__code__.co_firstlineno}"
|
||||
else:
|
||||
descr = repr(handler)
|
||||
|
||||
raise TypeError(f"The '{event_type}' event was dispatched with {len(args)} arguments, "
|
||||
f"but your handler {descr} accepts only {len(handler_args)} arguments.")
|
||||
raise TypeError(f"The '{event_type}' event was dispatched with {len(args)} arguments,\n"
|
||||
f"but your handler {descr} accepts only {n_handler_args} arguments.")
|
||||
|
||||
else:
|
||||
raise exception
|
||||
|
||||
@ -618,20 +465,21 @@ class EventDispatcher:
|
||||
"""
|
||||
if len(args) == 0: # @window.event()
|
||||
def decorator(func):
|
||||
name = func.__name__
|
||||
self.push_handler(name, func)
|
||||
func_name = func.__name__
|
||||
self.set_handler(func_name, func)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
elif _inspect.isroutine(args[0]): # @window.event
|
||||
return decorator
|
||||
elif inspect.isroutine(args[0]): # @window.event
|
||||
func = args[0]
|
||||
name = func.__name__
|
||||
self.push_handler(name, func)
|
||||
self.set_handler(name, func)
|
||||
return args[0]
|
||||
|
||||
elif isinstance(args[0], str): # @window.event('on_resize')
|
||||
name = args[0]
|
||||
|
||||
def decorator(func):
|
||||
self.push_handler(name, func)
|
||||
self.set_handler(name, func)
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
@ -1426,12 +1426,6 @@ class Texture(AbstractImage):
|
||||
order = self.tex_coords_order
|
||||
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):
|
||||
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
|
||||
|
||||
|
||||
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 vec4 size;
|
||||
in vec4 color;
|
||||
in vec4 texture_uv;
|
||||
in float rotation;
|
||||
|
||||
out vec4 geo_size;
|
||||
out vec4 geo_color;
|
||||
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;
|
||||
out vec4 vertex_colors;
|
||||
out vec3 texture_coords;
|
||||
|
||||
uniform WindowBlock
|
||||
{
|
||||
@ -111,102 +95,52 @@ geometry_source = """#version 150
|
||||
mat4 view;
|
||||
} 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
|
||||
// shader we need to define the inputs from it as arrays.
|
||||
// In our instance we just take single values (points)
|
||||
in vec4 geo_size[];
|
||||
in vec4 geo_color[];
|
||||
in vec4 geo_tex_coords[];
|
||||
in float geo_rotation[];
|
||||
void main()
|
||||
{
|
||||
m_scale[0][0] = scale.x;
|
||||
m_scale[1][1] = scale.y;
|
||||
m_translate[3][0] = translate.x;
|
||||
m_translate[3][1] = translate.y;
|
||||
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;
|
||||
out vec4 frag_color;
|
||||
gl_Position = window.projection * window.view * m_translate * m_rotation * m_scale * vec4(position, 1.0);
|
||||
|
||||
void main() {
|
||||
|
||||
// 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();
|
||||
vertex_colors = colors;
|
||||
texture_coords = tex_coords;
|
||||
}
|
||||
"""
|
||||
|
||||
fragment_source = """#version 150
|
||||
in vec2 uv;
|
||||
in vec4 frag_color;
|
||||
out vec4 final_color;
|
||||
fragment_source = """#version 150 core
|
||||
in vec4 vertex_colors;
|
||||
in vec3 texture_coords;
|
||||
out vec4 final_colors;
|
||||
|
||||
uniform sampler2D sprite_texture;
|
||||
|
||||
void main() {
|
||||
final_color = texture(sprite_texture, uv) * frag_color;
|
||||
void main()
|
||||
{
|
||||
final_colors = texture(sprite_texture, texture_coords.xy) * vertex_colors;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
fragment_array_source = """#version 150 core
|
||||
in vec2 uv;
|
||||
in vec4 frag_color;
|
||||
|
||||
in vec4 vertex_colors;
|
||||
in vec3 texture_coords;
|
||||
out vec4 final_colors;
|
||||
|
||||
uniform sampler2DArray sprite_texture;
|
||||
|
||||
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:
|
||||
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||
except AttributeError:
|
||||
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
||||
frag_shader = graphics.shader.Shader(fragment_source, 'fragment')
|
||||
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
||||
_default_vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||
_default_frag_shader = graphics.shader.Shader(fragment_source, 'fragment')
|
||||
default_shader_program = graphics.shader.ShaderProgram(_default_vert_shader, _default_frag_shader)
|
||||
pyglet.gl.current_context.pyglet_sprite_default_shader = default_shader_program
|
||||
return pyglet.gl.current_context.pyglet_sprite_default_shader
|
||||
|
||||
@ -227,10 +160,9 @@ def get_default_array_shader():
|
||||
try:
|
||||
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||
except AttributeError:
|
||||
vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||
geom_shader = graphics.shader.Shader(geometry_source, 'geometry')
|
||||
frag_shader = graphics.shader.Shader(fragment_array_source, 'fragment')
|
||||
default_shader_program = graphics.shader.ShaderProgram(vert_shader, geom_shader, frag_shader)
|
||||
_default_vert_shader = graphics.shader.Shader(vertex_source, 'vertex')
|
||||
_default_array_frag_shader = graphics.shader.Shader(fragment_array_source, 'fragment')
|
||||
default_shader_program = graphics.shader.ShaderProgram(_default_vert_shader, _default_array_frag_shader)
|
||||
pyglet.gl.current_context.pyglet_sprite_default_array_shader = default_shader_program
|
||||
return pyglet.gl.current_context.pyglet_sprite_default_array_shader
|
||||
|
||||
@ -312,7 +244,8 @@ class Sprite(event.EventDispatcher):
|
||||
_frame_index = 0
|
||||
_paused = False
|
||||
_rotation = 0
|
||||
_rgba = [255, 255, 255, 255]
|
||||
_opacity = 255
|
||||
_rgb = (255, 255, 255)
|
||||
_scale = 1.0
|
||||
_scale_x = 1.0
|
||||
_scale_y = 1.0
|
||||
@ -326,8 +259,7 @@ class Sprite(event.EventDispatcher):
|
||||
blend_dest=GL_ONE_MINUS_SRC_ALPHA,
|
||||
batch=None,
|
||||
group=None,
|
||||
subpixel=False,
|
||||
program=None):
|
||||
subpixel=False):
|
||||
"""Create a sprite.
|
||||
|
||||
:Parameters:
|
||||
@ -352,11 +284,6 @@ class Sprite(event.EventDispatcher):
|
||||
`subpixel` : bool
|
||||
Allow floating-point coordinates for the sprite. By default,
|
||||
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._y = y
|
||||
@ -372,46 +299,26 @@ class Sprite(event.EventDispatcher):
|
||||
else:
|
||||
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._user_group = group
|
||||
self._group = self.group_class(self._texture, blend_src, blend_dest, self.program, group)
|
||||
self._subpixel = subpixel
|
||||
|
||||
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
|
||||
def program(self):
|
||||
return self._program
|
||||
if isinstance(self._img, image.TextureArrayRegion):
|
||||
program = get_default_array_shader()
|
||||
else:
|
||||
program = get_default_shader()
|
||||
|
||||
@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._user_group)
|
||||
self._batch.migrate(self._vertex_list, GL_POINTS, self._group, self._batch)
|
||||
self._program = program
|
||||
return program
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
if self._vertex_list is not None:
|
||||
self._vertex_list.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
def delete(self):
|
||||
"""Force immediate removal of the sprite from video memory.
|
||||
@ -425,6 +332,8 @@ class Sprite(event.EventDispatcher):
|
||||
self._vertex_list.delete()
|
||||
self._vertex_list = None
|
||||
self._texture = None
|
||||
|
||||
# Easy way to break circular reference, speeds up GC
|
||||
self._group = None
|
||||
|
||||
def _animate(self, dt):
|
||||
@ -464,7 +373,7 @@ class Sprite(event.EventDispatcher):
|
||||
return
|
||||
|
||||
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
|
||||
else:
|
||||
self._vertex_list.delete()
|
||||
@ -491,7 +400,7 @@ class Sprite(event.EventDispatcher):
|
||||
self._group.blend_dest,
|
||||
self._group.program,
|
||||
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
|
||||
def image(self):
|
||||
@ -519,6 +428,7 @@ class Sprite(event.EventDispatcher):
|
||||
clock.schedule_once(self._animate, self._next_dt)
|
||||
else:
|
||||
self._set_texture(img.get_texture())
|
||||
self._update_position()
|
||||
|
||||
def _set_texture(self, texture):
|
||||
if texture.id is not self._texture.id:
|
||||
@ -531,9 +441,35 @@ class Sprite(event.EventDispatcher):
|
||||
self._texture = texture
|
||||
self._create_vertex_list()
|
||||
else:
|
||||
self._vertex_list.texture_uv[:] = texture.uv
|
||||
self._vertex_list.tex_coords[:] = texture.tex_coords
|
||||
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
|
||||
def position(self):
|
||||
"""The (x, y, z) coordinates of the sprite, as a tuple.
|
||||
@ -551,7 +487,7 @@ class Sprite(event.EventDispatcher):
|
||||
@position.setter
|
||||
def position(self, position):
|
||||
self._x, self._y, self._z = position
|
||||
self._vertex_list.position[:] = position
|
||||
self._vertex_list.translate[:] = position * 4
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
@ -564,7 +500,7 @@ class Sprite(event.EventDispatcher):
|
||||
@x.setter
|
||||
def x(self, x):
|
||||
self._x = x
|
||||
self._vertex_list.position[:] = x, self._y, self._z
|
||||
self._vertex_list.translate[:] = (x, self._y, self._z) * 4
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
@ -577,7 +513,7 @@ class Sprite(event.EventDispatcher):
|
||||
@y.setter
|
||||
def y(self, y):
|
||||
self._y = y
|
||||
self._vertex_list.position[:] = self._x, y, self._z
|
||||
self._vertex_list.translate[:] = (self._x, y, self._z) * 4
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
@ -590,7 +526,7 @@ class Sprite(event.EventDispatcher):
|
||||
@z.setter
|
||||
def z(self, z):
|
||||
self._z = z
|
||||
self._vertex_list.position[:] = self._x, self._y, z
|
||||
self._vertex_list.translate[:] = (self._x, self._y, z) * 4
|
||||
|
||||
@property
|
||||
def rotation(self):
|
||||
@ -606,7 +542,7 @@ class Sprite(event.EventDispatcher):
|
||||
@rotation.setter
|
||||
def rotation(self, rotation):
|
||||
self._rotation = rotation
|
||||
self._vertex_list.rotation[0] = self._rotation
|
||||
self._vertex_list.rotation[:] = (self._rotation,) * 4
|
||||
|
||||
@property
|
||||
def scale(self):
|
||||
@ -622,7 +558,7 @@ class Sprite(event.EventDispatcher):
|
||||
@scale.setter
|
||||
def scale(self, 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
|
||||
def scale_x(self):
|
||||
@ -638,7 +574,7 @@ class Sprite(event.EventDispatcher):
|
||||
@scale_x.setter
|
||||
def scale_x(self, 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
|
||||
def scale_y(self):
|
||||
@ -654,7 +590,7 @@ class Sprite(event.EventDispatcher):
|
||||
@scale_y.setter
|
||||
def scale_y(self, 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):
|
||||
"""Simultaneously change the position, rotation or scale.
|
||||
@ -693,11 +629,11 @@ class Sprite(event.EventDispatcher):
|
||||
translations_outdated = True
|
||||
|
||||
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:
|
||||
self._rotation = rotation
|
||||
self._vertex_list.rotation[:] = rotation
|
||||
self._vertex_list.rotation[:] = (rotation,) * 4
|
||||
|
||||
scales_outdated = False
|
||||
|
||||
@ -713,7 +649,7 @@ class Sprite(event.EventDispatcher):
|
||||
scales_outdated = True
|
||||
|
||||
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
|
||||
def width(self):
|
||||
@ -755,12 +691,12 @@ class Sprite(event.EventDispatcher):
|
||||
|
||||
:type: int
|
||||
"""
|
||||
return self._rgba[3]
|
||||
return self._opacity
|
||||
|
||||
@opacity.setter
|
||||
def opacity(self, opacity):
|
||||
self._rgba[3] = opacity
|
||||
self._vertex_list.color[:] = self._rgba
|
||||
self._opacity = opacity
|
||||
self._vertex_list.colors[:] = (*self._rgb, int(self._opacity)) * 4
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
@ -774,12 +710,12 @@ class Sprite(event.EventDispatcher):
|
||||
|
||||
:type: (int, int, int)
|
||||
"""
|
||||
return self._rgba[:3]
|
||||
return self._rgb
|
||||
|
||||
@color.setter
|
||||
def color(self, rgb):
|
||||
self._rgba[:3] = list(map(int, rgb))
|
||||
self._vertex_list.color[:] = self._rgba
|
||||
self._rgb = list(map(int, rgb))
|
||||
self._vertex_list.colors[:] = (*self._rgb, int(self._opacity)) * 4
|
||||
|
||||
@property
|
||||
def visible(self):
|
||||
@ -792,7 +728,7 @@ class Sprite(event.EventDispatcher):
|
||||
@visible.setter
|
||||
def visible(self, visible):
|
||||
self._visible = visible
|
||||
self._vertex_list.texture_uv[:] = (0, 0, 0, 0) if not visible else self._texture.uv
|
||||
self._update_position()
|
||||
|
||||
@property
|
||||
def paused(self):
|
||||
@ -846,16 +782,9 @@ class Sprite(event.EventDispatcher):
|
||||
efficiently.
|
||||
"""
|
||||
self._group.set_state_recursive()
|
||||
self._vertex_list.draw(GL_POINTS)
|
||||
self._vertex_list.draw(GL_TRIANGLES)
|
||||
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:
|
||||
def on_animation_end(self):
|
||||
"""The sprite animation reached the final frame.
|
||||
@ -869,3 +798,46 @@ class Sprite(event.EventDispatcher):
|
||||
|
||||
|
||||
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 {
|
||||
use pyo3::class::basic::CompareOp;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PySlice;
|
||||
|
||||
use crate::math::matrix::{Matrix3, Matrix4};
|
||||
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);
|
||||
}
|
||||
|
||||
// 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]
|
||||
|
Loading…
Reference in New Issue
Block a user