pyglet !
This commit is contained in:
parent
76e4b4a1cf
commit
ccaaf3b7fa
@ -11,7 +11,9 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Callable, Union, Tuple, List
|
||||
from typing import Any, Dict, Callable, Union, Tuple, List, get_type_hints, Type
|
||||
|
||||
from Difficult_Rocket.utils.typings import Options
|
||||
|
||||
from libs.MCDR.version import Version
|
||||
|
||||
@ -19,71 +21,6 @@ game_version = Version("0.6.3")
|
||||
__version__ = game_version
|
||||
|
||||
|
||||
class OptionNameNotDefined(Exception):
|
||||
"""向初始化的 option 里添加了一个不存在于选项里的选项"""
|
||||
|
||||
|
||||
class Options:
|
||||
"""
|
||||
Difficult Rocket 的游戏配置的存储基类
|
||||
"""
|
||||
__options: Dict[str, Union[Callable, object]] = {}
|
||||
cached_options: Dict[str, Union[str, Any]] = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for option, value in kwargs.items():
|
||||
if option not in self.option():
|
||||
raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined")
|
||||
setattr(self, option, value)
|
||||
self.flush_option()
|
||||
|
||||
def option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
获取配置类的所有配置
|
||||
:return: 自己的所有配置
|
||||
"""
|
||||
values = {}
|
||||
for ann in self.__annotations__: # 获取类型注释
|
||||
values[ann] = getattr(self, ann, None) if getattr(self, ann, None) else self.__annotations__[ann]
|
||||
|
||||
for option, a_fun in self.__options.items(): # 获取额外内容
|
||||
values[option] = a_fun
|
||||
|
||||
for option, a_fun in values.items(): # 检查是否为 property
|
||||
if a_fun is bool and getattr(self, option, None) is not None:
|
||||
values[option] = False
|
||||
if isinstance(a_fun, property):
|
||||
values[option] = getattr(self, option)
|
||||
return values
|
||||
|
||||
def flush_option(self) -> Dict[str, Any]:
|
||||
"""
|
||||
刷新缓存 options 的内容
|
||||
:return: 刷新过的 options
|
||||
"""
|
||||
self.cached_options = self.option()
|
||||
return self.cached_options
|
||||
|
||||
def option_with_len(self) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]:
|
||||
options = self.flush_option()
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
option_list = []
|
||||
for key, value in options.items():
|
||||
value_t = type(value) if not isinstance(type(value), type(value)) else value
|
||||
# value_t = type(value) if type(value) is not type(type(value)) else value
|
||||
max_len_key = max(max_len_key, len(key))
|
||||
max_len_value = max(max_len_value, len(str(value)))
|
||||
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
||||
option_list.append((key, value, value_t))
|
||||
return [option_list, max_len_key, max_len_value, max_len_value_t]
|
||||
|
||||
@classmethod
|
||||
def add_option(cls, name, value: Union[Callable, object]) -> Dict:
|
||||
cls.__options[name] = value
|
||||
return cls.__options
|
||||
|
||||
|
||||
class _DR_option(Options):
|
||||
"""
|
||||
@ -91,9 +28,9 @@ class _DR_option(Options):
|
||||
"""
|
||||
# runtime options
|
||||
InputBox_use_TextEntry: bool = False
|
||||
use_local_logging: bool = False
|
||||
record_threads: bool = True
|
||||
use_cProfile: bool = False
|
||||
use_local_logging: bool = False
|
||||
|
||||
# tests
|
||||
playing: bool = False
|
||||
@ -139,9 +76,16 @@ class _DR_runtime(Options):
|
||||
|
||||
DR_option = _DR_option()
|
||||
DR_runtime = _DR_runtime()
|
||||
print(DR_option.option())
|
||||
print(_DR_option.option())
|
||||
_DR_option.use_cProfile = True
|
||||
print(_DR_option.option())
|
||||
# print(_DR_option.__dict__)
|
||||
# print(DR_option.__dir__())
|
||||
# print(_DR_option.__dir__)
|
||||
|
||||
if DR_option.playing:
|
||||
from .utils import new_thread
|
||||
from Difficult_Rocket.utils import new_thread
|
||||
|
||||
|
||||
def think_it(something):
|
||||
|
@ -23,13 +23,13 @@ from decimal import Decimal
|
||||
# Difficult_Rocket function
|
||||
from Difficult_Rocket import Options, DR_runtime
|
||||
from Difficult_Rocket.command import line, tree
|
||||
from Difficult_Rocket.utils import new_thread
|
||||
from Difficult_Rocket.utils.translate import tr
|
||||
from Difficult_Rocket.client.guis.widgets import InputBox
|
||||
# from Difficult_Rocket.client.screen import DRScreen
|
||||
# from Difficult_Rocket.client.screen import DRDEBUGScreen
|
||||
from Difficult_Rocket.utils import tools, translate
|
||||
from Difficult_Rocket.utils.new_thread import new_thread
|
||||
from Difficult_Rocket.client.fps.fps_log import FpsLogger
|
||||
from Difficult_Rocket.client.guis.widgets import InputBox
|
||||
from Difficult_Rocket.exception.command import CommandError
|
||||
|
||||
import tomlkit
|
||||
|
@ -18,8 +18,9 @@ from typing import Union
|
||||
from decimal import Decimal
|
||||
|
||||
# from DR
|
||||
from Difficult_Rocket.utils import translate
|
||||
from Difficult_Rocket.command.api import CommandText
|
||||
from Difficult_Rocket.utils import new_thread, translate
|
||||
from Difficult_Rocket.utils.new_thread import new_thread
|
||||
|
||||
# from pyglet
|
||||
import pyglet
|
||||
|
@ -11,6 +11,11 @@ github: @shenjackyuanjie
|
||||
gitee: @shenjackyuanjie
|
||||
"""
|
||||
|
||||
from .new_thread import new_thread
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .new_thread import new_thread
|
||||
from .typings import get_type_hints_
|
||||
|
||||
__all__ = ['new_thread', 'get_type_hints_']
|
||||
|
||||
__all__ = ['new_thread']
|
||||
|
@ -21,7 +21,7 @@ import configparser
|
||||
from typing import Union
|
||||
from xml.dom.minidom import parse
|
||||
|
||||
from libs import toml
|
||||
import tomlkit
|
||||
|
||||
from Difficult_Rocket.exception.unsupport import NoMoreJson5
|
||||
|
||||
@ -50,7 +50,8 @@ def load_file(file_name: str, stack: Union[str, list, dict] = None, raise_error:
|
||||
if stack:
|
||||
get_file = get_file[stack]
|
||||
elif f_type == 'toml':
|
||||
get_file = toml.load(file_name)
|
||||
with open(file_name, mode='r', encoding='utf-8') as file:
|
||||
get_file = tomlkit.load(file)
|
||||
elif f_type == 'json5':
|
||||
raise NoMoreJson5("我说什么也不用json5了!喵的")
|
||||
except Exception as exp:
|
||||
|
84
Difficult_Rocket/utils/typings.py
Normal file
84
Difficult_Rocket/utils/typings.py
Normal file
@ -0,0 +1,84 @@
|
||||
# -------------------------------
|
||||
# Difficult Rocket
|
||||
# Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
|
||||
from typing import get_type_hints, Type, List, Union, Dict, Any, Callable, Tuple
|
||||
|
||||
|
||||
def get_type_hints_(cls: Type):
|
||||
try:
|
||||
return get_type_hints(cls)
|
||||
except ValueError:
|
||||
return get_type_hints(cls, globalns={})
|
||||
|
||||
|
||||
class OptionNameNotDefined(Exception):
|
||||
"""向初始化的 option 里添加了一个不存在于选项里的选项"""
|
||||
|
||||
|
||||
class Options:
|
||||
"""
|
||||
Difficult Rocket 的游戏配置的存储基类
|
||||
"""
|
||||
__options: Dict[str, Union[Callable, object]] = {}
|
||||
cached_options: Dict[str, Union[str, Any]] = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
for option, value in kwargs.items():
|
||||
if option not in self.option():
|
||||
raise OptionNameNotDefined(f"option: {option} with value: {value} is not defined")
|
||||
setattr(self, option, value)
|
||||
self.flush_option()
|
||||
|
||||
@classmethod
|
||||
def option(cls) -> Dict[str, Any]:
|
||||
"""
|
||||
获取配置类的所有配置
|
||||
:return: 自己的所有配置
|
||||
"""
|
||||
values = {}
|
||||
for ann in cls.__annotations__: # 获取类型注释
|
||||
values[ann] = getattr(cls, ann, None)
|
||||
if values[ann] is None:
|
||||
values[ann] = cls.__annotations__[ann]
|
||||
|
||||
for option, a_fun in cls.__options.items(): # 获取额外内容
|
||||
values[option] = a_fun
|
||||
|
||||
for option, a_fun in values.items(): # 检查是否为 property
|
||||
if a_fun is bool and getattr(cls, option, None) is not None:
|
||||
values[option] = False
|
||||
if isinstance(a_fun, property):
|
||||
values[option] = getattr(cls, option)
|
||||
return values
|
||||
|
||||
@classmethod
|
||||
def flush_option(cls) -> Dict[str, Any]:
|
||||
"""
|
||||
刷新缓存 options 的内容
|
||||
:return: 刷新过的 options
|
||||
"""
|
||||
cls.cached_options = cls.option()
|
||||
return cls.cached_options
|
||||
|
||||
@classmethod
|
||||
def option_with_len(cls) -> List[Union[List[Tuple[str, Any, Any]], int, Any]]:
|
||||
options = cls.flush_option()
|
||||
max_len_key = 1
|
||||
max_len_value = 1
|
||||
max_len_value_t = 1
|
||||
option_list = []
|
||||
for key, value in options.items():
|
||||
value_t = type(value) if not isinstance(value, Type) else value
|
||||
max_len_key = max(max_len_key, len(key))
|
||||
max_len_value = max(max_len_value, len(str(value)))
|
||||
max_len_value_t = max(max_len_value_t, len(str(value_t)))
|
||||
option_list.append((key, value, value_t))
|
||||
return [option_list, max_len_key, max_len_value, max_len_value_t]
|
||||
|
||||
@classmethod
|
||||
def add_option(cls, name, value: Union[Callable, object]) -> Dict:
|
||||
cls.__options[name] = value
|
||||
return cls.__options
|
6
compile.ps1
Normal file
6
compile.ps1
Normal file
@ -0,0 +1,6 @@
|
||||
allPython.ps1 -m compileall 'libs\'
|
||||
allPython.ps1 -m compileall 'Difficult_Rocket\'
|
||||
allPython.ps1 -O -m compileall 'libs\'
|
||||
allPython.ps1 -O -m compileall 'Difficult_Rocket\'
|
||||
allPython.ps1 -OO -m compileall 'libs\'
|
||||
allPython.ps1 -OO -m compileall 'Difficult_Rocket\'
|
@ -1,4 +0,0 @@
|
||||
python3.8 -O -m compileall .\libs\
|
||||
python3.8 -O -m compileall .\Difficult_Rocket\
|
||||
python3.10 -O -m compileall .\libs\
|
||||
python3.10 -O -m compileall .\Difficult_Rocket\
|
@ -163,6 +163,9 @@ class EventLoop(event.EventDispatcher):
|
||||
Developers are discouraged from overriding this method, as the
|
||||
implementation is platform-specific.
|
||||
"""
|
||||
if not interval:
|
||||
self.clock.schedule(self._redraw_windows)
|
||||
else:
|
||||
self.clock.schedule_interval(self._redraw_windows, interval)
|
||||
|
||||
self.has_exit = False
|
||||
|
@ -35,26 +35,13 @@
|
||||
|
||||
"""Precise framerate calculation function scheduling.
|
||||
|
||||
Measuring time
|
||||
==============
|
||||
The :py:mod:`~pyglet.clock` module allows you to schedule functions
|
||||
to run periodically, or for one-shot future execution. pyglet's default
|
||||
event loop (:py:func:`~pyglet.app.run`) keeps an internal instance of
|
||||
a :py:class:`~pyglet.clock.Clock`, which is ticked automatically.
|
||||
|
||||
The `tick` and `get_frequency` functions can be used in conjunction to fulfil most
|
||||
games' basic requirements::
|
||||
|
||||
from pyglet import clock
|
||||
while True:
|
||||
dt = clock.tick()
|
||||
# ... update and render ...
|
||||
print(f"FPS is {clock.get_frequency()}")
|
||||
|
||||
The ``dt`` value returned gives the number of seconds (as a float) since the
|
||||
last "tick".
|
||||
|
||||
The `get_frequency` function averages the framerate over a sliding window of
|
||||
approximately 1 second. (You can calculate the instantaneous framerate by
|
||||
taking the reciprocal of ``dt``).
|
||||
|
||||
Always remember to `tick` the clock!
|
||||
..note:: Some internal modules will schedule items on the clock. If you
|
||||
are using a custom event loop, always remember to `tick` the clock!
|
||||
|
||||
Scheduling
|
||||
==========
|
||||
@ -69,14 +56,14 @@ You can schedule a function to be called every time the clock is ticked::
|
||||
The `schedule_interval` method causes a function to be called every "n"
|
||||
seconds::
|
||||
|
||||
clock.schedule_interval(callback, .5) # called twice a second
|
||||
clock.schedule_interval(callback, 0.5) # called twice a second
|
||||
|
||||
The `schedule_once` method causes a function to be called once "n" seconds
|
||||
in the future::
|
||||
|
||||
clock.schedule_once(callback, 5) # called in 5 seconds
|
||||
|
||||
All of the `schedule` methods will pass on any additional args or keyword args
|
||||
All the `schedule` methods will pass on any additional args or keyword args
|
||||
you specify to the callback function::
|
||||
|
||||
def move(dt, velocity, sprite):
|
||||
@ -96,14 +83,14 @@ The clock functions are all relayed to an instance of
|
||||
:py:class:`~pyglet.clock.Clock` which is initialised with the module. You can
|
||||
get this instance to use directly::
|
||||
|
||||
clk = clock.get_default()
|
||||
clk = pyglet.clock.get_default()
|
||||
|
||||
You can also replace the default clock with your own:
|
||||
|
||||
myclk = clock.Clock()
|
||||
clock.set_default(myclk)
|
||||
myclk = pyglet.clock.Clock()
|
||||
pyglet.clock.set_default(myclk)
|
||||
|
||||
Each clock maintains its own set of scheduled functions and FPS
|
||||
Each clock maintains its own set of scheduled functions and frequency
|
||||
measurement. Each clock must be "ticked" separately.
|
||||
|
||||
Multiple and derived clocks potentially allow you to separate "game-time" and
|
||||
@ -113,6 +100,7 @@ of the system clock.
|
||||
|
||||
import time as _time
|
||||
|
||||
from typing import Callable
|
||||
from heapq import heappop as _heappop
|
||||
from heapq import heappush as _heappush
|
||||
from heapq import heappushpop as _heappushpop
|
||||
@ -164,14 +152,11 @@ class Clock:
|
||||
def __init__(self, time_function=_time.perf_counter):
|
||||
"""Initialise a Clock, with optional custom time function.
|
||||
|
||||
:Parameters:
|
||||
`time_function` : function
|
||||
Function to return the elapsed time of the application,
|
||||
in seconds. Defaults to time.time, but can be replaced
|
||||
to allow for easy time dilation effects or game pausing.
|
||||
|
||||
You can provide a custom time function to return the elapsed
|
||||
time of the application, in seconds. Defaults to time.perf_counter,
|
||||
but can be replaced to allow for easy time dilation effects or game
|
||||
pausing.
|
||||
"""
|
||||
super(Clock, self).__init__()
|
||||
self.time = time_function
|
||||
self.next_ts = self.time()
|
||||
self.last_ts = None
|
||||
@ -263,7 +248,7 @@ class Clock:
|
||||
else:
|
||||
item = _heappushpop(interval_items, item)
|
||||
|
||||
# a scheduled function may try and unschedule itself
|
||||
# a scheduled function may try to unschedule itself,
|
||||
# so we need to keep a reference to the current
|
||||
# item no longer on heap to be able to check
|
||||
self._current_interval_item = item
|
||||
@ -288,15 +273,15 @@ class Clock:
|
||||
|
||||
# test the schedule for the next execution
|
||||
if item.next_ts <= now:
|
||||
# the scheduled time of this item has already passed
|
||||
# so it must be rescheduled
|
||||
# the scheduled time of this item has already
|
||||
# passed, so it must be rescheduled
|
||||
if now - item.next_ts < 0.05:
|
||||
# missed execution time by 'reasonable' amount, so
|
||||
# reschedule at normal interval
|
||||
item.next_ts = now + item.interval
|
||||
else:
|
||||
# missed by significant amount, now many events have
|
||||
# likely missed execution. do a soft reschedule to
|
||||
# likely missed execution. do a soft re-schedule to
|
||||
# avoid lumping many events together.
|
||||
# in this case, the next dt will not be accurate
|
||||
item.next_ts = get_soft_next_ts(now, item.interval)
|
||||
@ -400,6 +385,7 @@ class Clock:
|
||||
return last_ts
|
||||
|
||||
def _get_soft_next_ts(self, last_ts, interval):
|
||||
|
||||
def taken(ts, e):
|
||||
"""Check if `ts` has already got an item scheduled nearby."""
|
||||
# TODO this function is slow and called very often.
|
||||
@ -412,9 +398,9 @@ class Clock:
|
||||
|
||||
return False
|
||||
|
||||
# sorted list is required required to produce expected results
|
||||
# sorted list is required to produce expected results
|
||||
# taken() will iterate through the heap, expecting it to be sorted
|
||||
# and will not always catch smallest value, so sort here.
|
||||
# and will not always catch the smallest value, so sort here.
|
||||
# do not remove the sort key...it is faster than relaying comparisons
|
||||
# NOTE: do not rewrite as popping from heap, as that is super slow!
|
||||
self._schedule_interval_items.sort(key=_attrgetter('next_ts'))
|
||||
@ -512,7 +498,7 @@ class Clock:
|
||||
|
||||
This method is similar to `schedule_interval`, except that the
|
||||
clock will move the interval out of phase with other scheduled
|
||||
functions so as to distribute CPU more load evenly over time.
|
||||
functions in order to distribute CPU load more evenly.
|
||||
|
||||
This is useful for functions that need to be called regularly,
|
||||
but not relative to the initial start time. :py:mod:`pyglet.media`
|
||||
@ -572,14 +558,10 @@ class Clock:
|
||||
_default = Clock()
|
||||
|
||||
|
||||
def set_default(default):
|
||||
def set_default(default) -> None:
|
||||
"""Set the default clock to use for all module-level functions.
|
||||
|
||||
By default an instance of :py:class:`~pyglet.clock.Clock` is used.
|
||||
|
||||
:Parameters:
|
||||
`default` : `Clock`
|
||||
The default clock to use.
|
||||
By default, an instance of :py:class:`~pyglet.clock.Clock` is used.
|
||||
"""
|
||||
global _default
|
||||
_default = default
|
||||
@ -590,17 +572,16 @@ def get_default():
|
||||
|
||||
Return the :py:class:`~pyglet.clock.Clock` instance that is used by all
|
||||
module-level clock functions.
|
||||
|
||||
:rtype: `Clock`
|
||||
:return: The default clock.
|
||||
"""
|
||||
return _default
|
||||
|
||||
|
||||
def tick(poll=False):
|
||||
def tick(poll: bool = False) -> float:
|
||||
"""Signify that one frame has passed on the default clock.
|
||||
|
||||
This will call any scheduled functions that have elapsed.
|
||||
This will call any scheduled functions that have elapsed,
|
||||
and return the elapsed seconds since the last tick. The
|
||||
return value will be 0.0 if this is the first tick.
|
||||
|
||||
:Parameters:
|
||||
`poll` : bool
|
||||
@ -610,17 +591,16 @@ def tick(poll=False):
|
||||
only.
|
||||
|
||||
Since pyglet 1.1.
|
||||
|
||||
:rtype: float
|
||||
:return: The number of seconds since the last "tick", or 0 if this was the
|
||||
first frame.
|
||||
"""
|
||||
return _default.tick(poll)
|
||||
|
||||
|
||||
def get_sleep_time(sleep_idle):
|
||||
def get_sleep_time(sleep_idle: bool) -> float:
|
||||
"""Get the time until the next item is scheduled on the default clock.
|
||||
|
||||
Returns the time until the next scheduled event in seconds, or
|
||||
``None`` if there is no event scheduled.
|
||||
|
||||
See `Clock.get_sleep_time` for details.
|
||||
|
||||
:Parameters:
|
||||
@ -628,17 +608,11 @@ def get_sleep_time(sleep_idle):
|
||||
If True, the application intends to sleep through its idle
|
||||
time; otherwise it will continue ticking at the maximum
|
||||
frame rate allowed.
|
||||
|
||||
:rtype: float
|
||||
:return: Time until the next scheduled event in seconds, or ``None``
|
||||
if there is no event scheduled.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return _default.get_sleep_time(sleep_idle)
|
||||
|
||||
|
||||
def get_frequency():
|
||||
def get_frequency() -> float:
|
||||
"""Get the average clock update frequency.
|
||||
|
||||
The result is the sliding average of the last "n" updates,
|
||||
@ -646,65 +620,43 @@ def get_frequency():
|
||||
second. This is the internal clock update rate, **not** the
|
||||
Window redraw rate. Platform events, such as moving the
|
||||
mouse rapidly, will cause the clock to refresh more often.
|
||||
|
||||
:rtype: float
|
||||
:return: The measured updates per second.
|
||||
"""
|
||||
return _default.get_frequency()
|
||||
|
||||
|
||||
def schedule(func, *args, **kwargs):
|
||||
def schedule(func: Callable, *args, **kwargs) -> None:
|
||||
"""Schedule 'func' to be called every frame on the default clock.
|
||||
|
||||
The arguments passed to func are ``dt``, followed by any ``*args`` and
|
||||
``**kwargs`` given here.
|
||||
|
||||
:Parameters:
|
||||
`func` : callable
|
||||
The function to call each frame.
|
||||
"""
|
||||
_default.schedule(func, *args, **kwargs)
|
||||
|
||||
|
||||
def schedule_interval(func, interval, *args, **kwargs):
|
||||
"""Schedule ``func`` on the default clock every interval seconds.
|
||||
def schedule_interval(func: Callable, interval: float, *args, **kwargs) -> None:
|
||||
"""Schedule ``func`` on the default clock every ``interval`` seconds.
|
||||
|
||||
The arguments passed to ``func`` are ``dt`` (time since last function
|
||||
call), followed by any ``*args`` and ``**kwargs`` given here.
|
||||
|
||||
:Parameters:
|
||||
`func` : callable
|
||||
The function to call when the timer lapses.
|
||||
`interval` : float
|
||||
The number of seconds to wait between each call.
|
||||
"""
|
||||
_default.schedule_interval(func, interval, *args, **kwargs)
|
||||
|
||||
|
||||
def schedule_interval_soft(func, interval, *args, **kwargs):
|
||||
def schedule_interval_soft(func: Callable, interval: float, *args, **kwargs) -> None:
|
||||
"""Schedule ``func`` on the default clock every interval seconds.
|
||||
|
||||
The clock will move the interval out of phase with other scheduled
|
||||
functions so as to distribute CPU more load evenly over time.
|
||||
functions in order to distribute CPU load more evenly.
|
||||
|
||||
The arguments passed to ``func`` are ``dt`` (time since last function
|
||||
call), followed by any ``*args`` and ``**kwargs`` given here.
|
||||
|
||||
:see: `Clock.schedule_interval_soft`
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
:Parameters:
|
||||
`func` : callable
|
||||
The function to call when the timer lapses.
|
||||
`interval` : float
|
||||
The number of seconds to wait between each call.
|
||||
|
||||
"""
|
||||
_default.schedule_interval_soft(func, interval, *args, **kwargs)
|
||||
|
||||
|
||||
def schedule_once(func, delay, *args, **kwargs):
|
||||
def schedule_once(func: Callable, delay: float, *args, **kwargs) -> None:
|
||||
"""Schedule ``func`` to be called once after ``delay`` seconds.
|
||||
|
||||
This function uses the default clock. ``delay`` can be a float. The
|
||||
@ -713,23 +665,13 @@ def schedule_once(func, delay, *args, **kwargs):
|
||||
|
||||
If no default clock is set, the func is queued and will be scheduled
|
||||
on the default clock as soon as it is created.
|
||||
|
||||
:Parameters:
|
||||
`func` : callable
|
||||
The function to call when the timer lapses.
|
||||
`delay` : float
|
||||
The number of seconds to wait before the timer lapses.
|
||||
"""
|
||||
_default.schedule_once(func, delay, *args, **kwargs)
|
||||
|
||||
|
||||
def unschedule(func):
|
||||
def unschedule(func: Callable) -> None:
|
||||
"""Remove ``func`` from the default clock's schedule.
|
||||
|
||||
No error is raised if the ``func`` was never scheduled.
|
||||
|
||||
:Parameters:
|
||||
`func` : callable
|
||||
The function to remove from the schedule.
|
||||
"""
|
||||
_default.unschedule(func)
|
||||
|
@ -1550,7 +1550,7 @@ if not wic_decoder:
|
||||
|
||||
class DirectWriteGlyphRenderer(base.GlyphRenderer):
|
||||
antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
|
||||
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
|
||||
draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT if WINDOWS_8_1_OR_GREATER else D2D1_DRAW_TEXT_OPTIONS_NONE
|
||||
measuring_mode = DWRITE_MEASURING_MODE_NATURAL
|
||||
|
||||
def __init__(self, font):
|
||||
|
@ -91,46 +91,13 @@ possible. If the drawing of sprites or text objects need to be interleaved
|
||||
with other drawing that does not use the graphics API, multiple batches will
|
||||
be required.
|
||||
|
||||
Data item parameters
|
||||
====================
|
||||
|
||||
Many of the functions and methods in this module accept any number of ``data``
|
||||
parameters as their final parameters. In the documentation these are notated
|
||||
as ``*data`` in the formal parameter list.
|
||||
|
||||
A data parameter describes a vertex attribute format and an optional sequence
|
||||
to initialise that attribute. Examples of common attribute formats are:
|
||||
|
||||
``"v3f"``
|
||||
Vertex position, specified as three floats.
|
||||
``"c4B"``
|
||||
Vertex color, specified as four unsigned bytes.
|
||||
``"t2f"``
|
||||
Texture coordinate, specified as two floats.
|
||||
|
||||
See `pyglet.graphics.vertexattribute` for the complete syntax of the vertex
|
||||
format string.
|
||||
|
||||
When no initial data is to be given, the data item is just the format string.
|
||||
For example, the following creates a 2 element vertex list with position and
|
||||
color attributes::
|
||||
|
||||
vertex_list = pyglet.graphics.vertex_list(2, 'v2f', 'c4B')
|
||||
|
||||
When initial data is required, wrap the format string and the initial data in
|
||||
a tuple, for example::
|
||||
|
||||
vertex_list = pyglet.graphics.vertex_list(2,
|
||||
('v2f', (0.0, 1.0, 1.0, 0.0)),
|
||||
('c4B', (255, 255, 255, 255) * 2))
|
||||
|
||||
Drawing modes
|
||||
=============
|
||||
|
||||
Methods in this module that accept a ``mode`` parameter will accept any value
|
||||
in the OpenGL drawing mode enumeration: ``GL_POINTS``, ``GL_LINE_STRIP``,
|
||||
``GL_LINE_LOOP``, ``GL_LINES``, ``GL_TRIANGLE_STRIP``, ``GL_TRIANGLE_FAN``,
|
||||
``GL_TRIANGLES``, ``GL_QUAD_STRIP``, ``GL_QUADS``, and ``GL_POLYGON``.
|
||||
``GL_TRIANGLES``, and ``GL_POLYGON``.
|
||||
|
||||
::
|
||||
|
||||
@ -140,8 +107,8 @@ However, because of the way the graphics API renders multiple primitives with
|
||||
shared state, ``GL_POLYGON``, ``GL_LINE_LOOP`` and ``GL_TRIANGLE_FAN`` cannot
|
||||
be used --- the results are undefined.
|
||||
|
||||
When using ``GL_LINE_STRIP``, ``GL_TRIANGLE_STRIP`` or ``GL_QUAD_STRIP`` care
|
||||
must be taken to insert degenerate vertices at the beginning and end of each
|
||||
When using ``GL_LINE_STRIP`` or ``GL_TRIANGLE_STRIP``, care must be taken to
|
||||
insert degenerate vertices at the beginning and end of each
|
||||
vertex list. For example, given the vertex list::
|
||||
|
||||
A, B, C, D
|
||||
@ -163,7 +130,7 @@ import weakref
|
||||
|
||||
import pyglet
|
||||
from pyglet.gl import *
|
||||
from pyglet.graphics import vertexattribute, vertexdomain
|
||||
from pyglet.graphics import shader, vertexdomain
|
||||
from pyglet.graphics.vertexarray import VertexArray
|
||||
from pyglet.graphics.vertexbuffer import BufferObject
|
||||
|
||||
@ -199,7 +166,7 @@ def draw(size, mode, **data):
|
||||
count = program.attributes[name]['count']
|
||||
gl_type = vertexdomain._gl_types[fmt[0]]
|
||||
normalize = 'n' in fmt
|
||||
attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize)
|
||||
attribute = shader.Attribute(name, location, count, gl_type, normalize)
|
||||
assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt
|
||||
|
||||
buffer = BufferObject(size * attribute.stride)
|
||||
@ -249,7 +216,7 @@ def draw_indexed(size, mode, indices, **data):
|
||||
count = program.attributes[name]['count']
|
||||
gl_type = vertexdomain._gl_types[fmt[0]]
|
||||
normalize = 'n' in fmt
|
||||
attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize)
|
||||
attribute = shader.Attribute(name, location, count, gl_type, normalize)
|
||||
assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt
|
||||
|
||||
buffer = BufferObject(size * attribute.stride)
|
||||
|
@ -14,6 +14,17 @@ class ShaderException(BaseException):
|
||||
pass
|
||||
|
||||
|
||||
_c_types = {
|
||||
GL_BYTE: c_byte,
|
||||
GL_UNSIGNED_BYTE: c_ubyte,
|
||||
GL_SHORT: c_short,
|
||||
GL_UNSIGNED_SHORT: c_ushort,
|
||||
GL_INT: c_int,
|
||||
GL_UNSIGNED_INT: c_uint,
|
||||
GL_FLOAT: c_float,
|
||||
GL_DOUBLE: c_double,
|
||||
}
|
||||
|
||||
# TODO: test other shader types, and update if necessary.
|
||||
_shader_types = {
|
||||
'vertex': GL_VERTEX_SHADER,
|
||||
@ -91,6 +102,102 @@ _attribute_types = {
|
||||
}
|
||||
|
||||
|
||||
class Attribute:
|
||||
"""Abstract accessor for an attribute in a mapped buffer."""
|
||||
|
||||
def __init__(self, name, location, count, gl_type, normalize):
|
||||
"""Create the attribute accessor.
|
||||
|
||||
:Parameters:
|
||||
`name` : str
|
||||
Name of the vertex attribute.
|
||||
`location` : int
|
||||
Location (index) of the vertex attribute.
|
||||
`count` : int
|
||||
Number of components in the attribute.
|
||||
`gl_type` : int
|
||||
OpenGL type enumerant; for example, ``GL_FLOAT``
|
||||
`normalize`: bool
|
||||
True if OpenGL should normalize the values
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.location = location
|
||||
self.count = count
|
||||
|
||||
self.gl_type = gl_type
|
||||
self.c_type = _c_types[gl_type]
|
||||
self.normalize = normalize
|
||||
|
||||
self.align = sizeof(self.c_type)
|
||||
self.size = count * self.align
|
||||
self.stride = self.size
|
||||
|
||||
def enable(self):
|
||||
"""Enable the attribute."""
|
||||
glEnableVertexAttribArray(self.location)
|
||||
|
||||
def set_pointer(self, ptr):
|
||||
"""Setup this attribute to point to the currently bound buffer at
|
||||
the given offset.
|
||||
|
||||
``offset`` should be based on the currently bound buffer's ``ptr``
|
||||
member.
|
||||
|
||||
:Parameters:
|
||||
`offset` : int
|
||||
Pointer offset to the currently bound buffer for this
|
||||
attribute.
|
||||
|
||||
"""
|
||||
glVertexAttribPointer(self.location, self.count, self.gl_type, self.normalize, self.stride, ptr)
|
||||
|
||||
def get_region(self, buffer, start, count):
|
||||
"""Map a buffer region using this attribute as an accessor.
|
||||
|
||||
The returned region consists of a contiguous array of component
|
||||
data elements. For example, if this attribute uses 3 floats per
|
||||
vertex, and the `count` parameter is 4, the number of floats mapped
|
||||
will be ``3 * 4 = 12``.
|
||||
|
||||
:Parameters:
|
||||
`buffer` : `AbstractMappable`
|
||||
The buffer to map.
|
||||
`start` : int
|
||||
Offset of the first vertex to map.
|
||||
`count` : int
|
||||
Number of vertices to map
|
||||
|
||||
:rtype: `AbstractBufferRegion`
|
||||
"""
|
||||
byte_start = self.stride * start
|
||||
byte_size = self.stride * count
|
||||
array_count = self.count * count
|
||||
ptr_type = POINTER(self.c_type * array_count)
|
||||
return buffer.get_region(byte_start, byte_size, ptr_type)
|
||||
|
||||
def set_region(self, buffer, start, count, data):
|
||||
"""Set the data over a region of the buffer.
|
||||
|
||||
:Parameters:
|
||||
`buffer` : AbstractMappable`
|
||||
The buffer to modify.
|
||||
`start` : int
|
||||
Offset of the first vertex to set.
|
||||
`count` : int
|
||||
Number of vertices to set.
|
||||
`data` : A sequence of data components.
|
||||
"""
|
||||
byte_start = self.stride * start
|
||||
byte_size = self.stride * count
|
||||
array_count = self.count * count
|
||||
data = (self.c_type * array_count)(*data)
|
||||
buffer.set_data_region(data, byte_start, byte_size)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Attribute(name='{self.name}', location={self.location}, count={self.count})"
|
||||
|
||||
|
||||
class _Uniform:
|
||||
__slots__ = 'program', 'name', 'type', 'location', 'length', 'count', 'get', 'set'
|
||||
|
||||
|
@ -1,148 +0,0 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# pyglet
|
||||
# Copyright (c) 2006-2008 Alex Holkner
|
||||
# Copyright (c) 2008-2022 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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
import ctypes
|
||||
|
||||
from pyglet.gl import *
|
||||
|
||||
|
||||
_c_types = {
|
||||
GL_BYTE: ctypes.c_byte,
|
||||
GL_UNSIGNED_BYTE: ctypes.c_ubyte,
|
||||
GL_SHORT: ctypes.c_short,
|
||||
GL_UNSIGNED_SHORT: ctypes.c_ushort,
|
||||
GL_INT: ctypes.c_int,
|
||||
GL_UNSIGNED_INT: ctypes.c_uint,
|
||||
GL_FLOAT: ctypes.c_float,
|
||||
GL_DOUBLE: ctypes.c_double,
|
||||
}
|
||||
|
||||
|
||||
class VertexAttribute:
|
||||
"""Abstract accessor for an attribute in a mapped buffer."""
|
||||
|
||||
def __init__(self, name, location, count, gl_type, normalize):
|
||||
"""Create the attribute accessor.
|
||||
|
||||
:Parameters:
|
||||
`name` : str
|
||||
Name of the vertex attribute.
|
||||
`location` : int
|
||||
Location (index) of the vertex attribute.
|
||||
`count` : int
|
||||
Number of components in the attribute.
|
||||
`gl_type` : int
|
||||
OpenGL type enumerant; for example, ``GL_FLOAT``
|
||||
`normalize`: bool
|
||||
True if OpenGL should normalize the values
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.location = location
|
||||
self.count = count
|
||||
|
||||
self.gl_type = gl_type
|
||||
self.c_type = _c_types[gl_type]
|
||||
self.normalize = normalize
|
||||
|
||||
self.align = ctypes.sizeof(self.c_type)
|
||||
self.size = count * self.align
|
||||
self.stride = self.size
|
||||
|
||||
def enable(self):
|
||||
"""Enable the attribute."""
|
||||
glEnableVertexAttribArray(self.location)
|
||||
|
||||
def set_pointer(self, pointer):
|
||||
"""Setup this attribute to point to the currently bound buffer at
|
||||
the given offset.
|
||||
|
||||
``offset`` should be based on the currently bound buffer's ``ptr``
|
||||
member.
|
||||
|
||||
:Parameters:
|
||||
`offset` : int
|
||||
Pointer offset to the currently bound buffer for this
|
||||
attribute.
|
||||
|
||||
"""
|
||||
glVertexAttribPointer(self.location, self.count, self.gl_type, self.normalize, self.stride, pointer)
|
||||
|
||||
def get_region(self, buffer, start, count):
|
||||
"""Map a buffer region using this attribute as an accessor.
|
||||
|
||||
The returned region consists of a contiguous array of component
|
||||
data elements. For example, if this attribute uses 3 floats per
|
||||
vertex, and the `count` parameter is 4, the number of floats mapped
|
||||
will be ``3 * 4 = 12``.
|
||||
|
||||
:Parameters:
|
||||
`buffer` : `AbstractMappable`
|
||||
The buffer to map.
|
||||
`start` : int
|
||||
Offset of the first vertex to map.
|
||||
`count` : int
|
||||
Number of vertices to map
|
||||
|
||||
:rtype: `AbstractBufferRegion`
|
||||
"""
|
||||
byte_start = self.stride * start
|
||||
byte_size = self.stride * count
|
||||
array_count = self.count * count
|
||||
ptr_type = ctypes.POINTER(self.c_type * array_count)
|
||||
return buffer.get_region(byte_start, byte_size, ptr_type)
|
||||
|
||||
def set_region(self, buffer, start, count, data):
|
||||
"""Set the data over a region of the buffer.
|
||||
|
||||
:Parameters:
|
||||
`buffer` : AbstractMappable`
|
||||
The buffer to modify.
|
||||
`start` : int
|
||||
Offset of the first vertex to set.
|
||||
`count` : int
|
||||
Number of vertices to set.
|
||||
`data` : sequence
|
||||
Sequence of data components.
|
||||
|
||||
"""
|
||||
byte_start = self.stride * start
|
||||
byte_size = self.stride * count
|
||||
array_count = self.count * count
|
||||
data = (self.c_type * array_count)(*data)
|
||||
buffer.set_data_region(data, byte_start, byte_size)
|
||||
|
||||
def __repr__(self):
|
||||
return f"VertexAttribute(name='{self.name}', location={self.location}, count={self.count})"
|
@ -61,7 +61,7 @@ import ctypes
|
||||
import pyglet
|
||||
|
||||
from pyglet.gl import *
|
||||
from pyglet.graphics import allocation, vertexattribute, vertexarray
|
||||
from pyglet.graphics import allocation, shader, vertexarray
|
||||
from pyglet.graphics.vertexbuffer import BufferObject, MappableBufferObject
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ class VertexDomain:
|
||||
count = meta['count']
|
||||
gl_type = _gl_types[meta['format'][0]]
|
||||
normalize = 'n' in meta['format']
|
||||
attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize)
|
||||
attribute = shader.Attribute(name, location, count, gl_type, normalize)
|
||||
self.attributes.append(attribute)
|
||||
|
||||
# Create buffer:
|
||||
@ -370,7 +370,7 @@ class IndexedVertexDomain(VertexDomain):
|
||||
self.index_allocator = allocation.Allocator(self._initial_index_count)
|
||||
|
||||
self.index_gl_type = index_gl_type
|
||||
self.index_c_type = vertexattribute._c_types[index_gl_type]
|
||||
self.index_c_type = shader._c_types[index_gl_type]
|
||||
self.index_element_size = ctypes.sizeof(self.index_c_type)
|
||||
self.index_buffer = BufferObject(self.index_allocator.capacity * self.index_element_size)
|
||||
|
||||
|
@ -818,6 +818,31 @@ class Mat4(tuple):
|
||||
0, 0, q, -1,
|
||||
0, 0, qn, 0))
|
||||
|
||||
@classmethod
|
||||
def from_rotation(cls, angle: float, vector: Vec3) -> Mat4:
|
||||
"""Create a rotation matrix from an angle and Vec3.
|
||||
|
||||
:Parameters:
|
||||
`angle` : A `float` :
|
||||
The angle as a float.
|
||||
`vector` : A `Vec3`, or 3 component tuple of float or int :
|
||||
Vec3 or tuple with x, y and z translation values
|
||||
"""
|
||||
return cls().rotate(angle, vector)
|
||||
|
||||
@classmethod
|
||||
def from_scale(cls: type[Mat4T], vector: Vec3) -> Mat4T:
|
||||
"""Create a scale matrix from a Vec3.
|
||||
|
||||
:Parameters:
|
||||
`vector` : A `Vec3`, or 3 component tuple of float or int
|
||||
Vec3 or tuple with x, y and z scale values
|
||||
"""
|
||||
return cls((vector[0], 0.0, 0.0, 0.0,
|
||||
0.0, vector[1], 0.0, 0.0,
|
||||
0.0, 0.0, vector[2], 0.0,
|
||||
0.0, 0.0, 0.0, 1.0))
|
||||
|
||||
@classmethod
|
||||
def from_translation(cls: type[Mat4T], vector: Vec3) -> Mat4T:
|
||||
"""Create a translation matrix from a Vec3.
|
||||
@ -831,18 +856,6 @@ class Mat4(tuple):
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
vector[0], vector[1], vector[2], 1.0))
|
||||
|
||||
@classmethod
|
||||
def from_rotation(cls, angle: float, vector: Vec3) -> Mat4:
|
||||
"""Create a rotation matrix from an angle and Vec3.
|
||||
|
||||
:Parameters:
|
||||
`angle` : A `float` :
|
||||
The angle as a float.
|
||||
`vector` : A `Vec3`, or 3 component tuple of float or int :
|
||||
Vec3 or tuple with x, y and z translation values
|
||||
"""
|
||||
return cls().rotate(angle, vector)
|
||||
|
||||
@classmethod
|
||||
def look_at_direction(cls: type[Mat4T], direction: Vec3, up: Vec3) -> Mat4T:
|
||||
vec_z = direction.normalize()
|
||||
@ -869,18 +882,6 @@ class Mat4(tuple):
|
||||
"""Get a specific column as a tuple."""
|
||||
return self[index::4]
|
||||
|
||||
def scale(self, vector: Vec3) -> Mat4:
|
||||
"""Get a scale Matrix on x, y, or z axis."""
|
||||
temp = list(self)
|
||||
temp[0] *= vector[0]
|
||||
temp[5] *= vector[1]
|
||||
temp[10] *= vector[2]
|
||||
return Mat4(temp)
|
||||
|
||||
def translate(self, vector: Vec3) -> Mat4:
|
||||
"""Get a translation Matrix along x, y, and z axis."""
|
||||
return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
|
||||
|
||||
def rotate(self, angle: float, vector: Vec3) -> Mat4:
|
||||
"""Get a rotation Matrix on x, y, or z axis."""
|
||||
if not all(abs(n) <= 1 for n in vector):
|
||||
@ -908,6 +909,18 @@ class Mat4(tuple):
|
||||
|
||||
return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1))
|
||||
|
||||
def scale(self, vector: Vec3) -> Mat4:
|
||||
"""Get a scale Matrix on x, y, or z axis."""
|
||||
temp = list(self)
|
||||
temp[0] *= vector[0]
|
||||
temp[5] *= vector[1]
|
||||
temp[10] *= vector[2]
|
||||
return Mat4(temp)
|
||||
|
||||
def translate(self, vector: Vec3) -> Mat4:
|
||||
"""Get a translation Matrix along x, y, and z axis."""
|
||||
return self @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, *vector, 1))
|
||||
|
||||
def transpose(self) -> Mat4:
|
||||
"""Get a transpose of this Matrix."""
|
||||
return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4])
|
||||
|
@ -7,8 +7,8 @@
|
||||
# Copyright © 2021-2022 by shenjackyuanjie 3695888@qq.com
|
||||
# All rights reserved
|
||||
# -------------------------------
|
||||
import re
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import atexit
|
||||
import inspect
|
||||
@ -16,12 +16,13 @@ import threading
|
||||
|
||||
from queue import Queue
|
||||
from time import strftime
|
||||
from logging import NOTSET, DEBUG, INFO, WARNING, ERROR, FATAL
|
||||
from logging import NOTSET, DEBUG
|
||||
from types import FrameType
|
||||
from typing import NoReturn, Optional, Type, Union, Dict, Iterable, Any, List
|
||||
from typing import Optional, Type, Union, Dict, Iterable, Any, List
|
||||
|
||||
os.system('')
|
||||
# print(os.path.abspath(os.curdir))
|
||||
# TODO 这个文件就是个大TODO
|
||||
"""
|
||||
如果想要直接使用 logger 来 logging
|
||||
直接调用 logger.debug() 即可
|
||||
@ -161,8 +162,7 @@ logger_configs = {
|
||||
LoggingLevel.INFO_t: {'info': '\033[0m'},
|
||||
LoggingLevel.WARNING_t: {'info': '\033[33m'},
|
||||
LoggingLevel.ERROR_t: {'info': '\033[31m'},
|
||||
LoggingLevel.FATAL_t: {
|
||||
'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
|
||||
LoggingLevel.FATAL_t: {'info': '\033[38;2;255;255;0;48;2;120;10;10m', 'logger': '\033[38;2;245;189;230m'}
|
||||
},
|
||||
'fancy_main_color': {
|
||||
# 'file_time': '\033[38;2;201;222;56m',
|
||||
@ -456,7 +456,8 @@ class CachedFileHandler(StreamHandlerTemplate):
|
||||
with self.time_limit_lock:
|
||||
with open(file=self.file_conf.file_name, mode=self.file_conf.file_mode,
|
||||
encoding=self.file_conf.file_encoding) as log_file:
|
||||
...
|
||||
while not self.string_queue.empty():
|
||||
log_file.write(self.string_queue.get())
|
||||
|
||||
def write(self, message: str, flush: Optional[bool]) -> bool:
|
||||
if not flush:
|
||||
|
Loading…
Reference in New Issue
Block a user