mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-17 17:27:06 +01:00
189 lines
5.6 KiB
Python
189 lines
5.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# pylint: disable=E1101, C0330, C0103
|
|
# E1101: Module X has no Y member
|
|
# C0330: Wrong continued indentation
|
|
# C0103: Invalid attribute/variable/method name
|
|
#
|
|
"""
|
|
utils.py
|
|
=========
|
|
|
|
This is a collection of utilities used by the wx.lib.plot package.
|
|
|
|
"""
|
|
import functools
|
|
from warnings import warn as _warn
|
|
|
|
# TODO: New name: RevertStyle? SavedStyle? Something else?
|
|
class TempStyle(object):
|
|
"""
|
|
Combination Decorator and Context Manager to revert pen or brush changes
|
|
after a method call or block finish.
|
|
|
|
:param which: The item to save and revert after execution. Can be
|
|
one of ``{'both', 'pen', 'brush'}``.
|
|
:type which: str
|
|
:param dc: The DC to get brush/pen info from.
|
|
:type dc: :class:`wx.DC`
|
|
|
|
::
|
|
|
|
# Using as a method decorator:
|
|
@TempStyle() # same as @TempStyle('both')
|
|
def func(self, dc, a, b, c): # dc must be 1st arg (beside self)
|
|
# edit pen and brush here
|
|
|
|
# Or as a context manager:
|
|
with TempStyle('both', dc):
|
|
# do stuff
|
|
|
|
.. Note::
|
|
|
|
As of 2016-06-15, this can only be used as a decorator for **class
|
|
methods**, not standard functions. There is a plan to try and remove
|
|
this restriction, but I don't know when that will happen...
|
|
|
|
|
|
.. epigraph::
|
|
|
|
*Combination Decorator and Context Manager! Also makes Julienne fries!
|
|
Will not break! Will not... It broke!*
|
|
|
|
-- The Genie
|
|
"""
|
|
_valid_types = {'both', 'pen', 'brush'}
|
|
_err_str = (
|
|
"No DC provided and unable to determine DC from context for function "
|
|
"`{func_name}`. When `{cls_name}` is used as a decorator, the "
|
|
"decorated function must have a wx.DC as a keyword arg 'dc=' or "
|
|
"as the first arg."
|
|
)
|
|
|
|
def __init__(self, which='both', dc=None):
|
|
if which not in self._valid_types:
|
|
raise ValueError(
|
|
"`which` must be one of {}".format(self._valid_types)
|
|
)
|
|
self.which = which
|
|
self.dc = dc
|
|
self.prevPen = None
|
|
self.prevBrush = None
|
|
|
|
def __call__(self, func):
|
|
|
|
@functools.wraps(func)
|
|
def wrapper(instance, dc, *args, **kwargs):
|
|
# fake the 'with' block. This solves:
|
|
# 1. plots only being shown on 2nd menu selection in demo
|
|
# 2. self.dc compalaining about not having a super called when
|
|
# trying to get or set the pen/brush values in __enter__ and
|
|
# __exit__:
|
|
# RuntimeError: super-class __init__() of type
|
|
# BufferedDC was never called
|
|
self._save_items(dc)
|
|
func(instance, dc, *args, **kwargs)
|
|
self._revert_items(dc)
|
|
|
|
#import copy # copy solves issue #1 above, but
|
|
#self.dc = copy.copy(dc) # possibly causes issue #2.
|
|
|
|
#with self:
|
|
# print('in with')
|
|
# func(instance, dc, *args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
def __enter__(self):
|
|
self._save_items(self.dc)
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
self._revert_items(self.dc)
|
|
return False # True means exceptions *are* suppressed.
|
|
|
|
def _save_items(self, dc):
|
|
if self.which == 'both':
|
|
self._save_pen(dc)
|
|
self._save_brush(dc)
|
|
elif self.which == 'pen':
|
|
self._save_pen(dc)
|
|
elif self.which == 'brush':
|
|
self._save_brush(dc)
|
|
else:
|
|
err_str = ("How did you even get here?? This class forces "
|
|
"correct values for `which` at instancing..."
|
|
)
|
|
raise ValueError(err_str)
|
|
|
|
def _revert_items(self, dc):
|
|
if self.which == 'both':
|
|
self._revert_pen(dc)
|
|
self._revert_brush(dc)
|
|
elif self.which == 'pen':
|
|
self._revert_pen(dc)
|
|
elif self.which == 'brush':
|
|
self._revert_brush(dc)
|
|
else:
|
|
err_str = ("How did you even get here?? This class forces "
|
|
"correct values for `which` at instancing...")
|
|
raise ValueError(err_str)
|
|
|
|
def _save_pen(self, dc):
|
|
self.prevPen = dc.GetPen()
|
|
|
|
def _save_brush(self, dc):
|
|
self.prevBrush = dc.GetBrush()
|
|
|
|
def _revert_pen(self, dc):
|
|
dc.SetPen(self.prevPen)
|
|
|
|
def _revert_brush(self, dc):
|
|
dc.SetBrush(self.prevBrush)
|
|
|
|
|
|
class PendingDeprecation(object):
|
|
"""
|
|
Decorator which warns the developer about methods that are
|
|
pending deprecation.
|
|
|
|
:param new_func: The new class, method, or function that should be used.
|
|
:type new_func: str
|
|
|
|
::
|
|
|
|
@PendingDeprecation("new_func")
|
|
def old_func():
|
|
pass
|
|
|
|
# prints the warning:
|
|
# `old_func` is pending deprecation. Please use `new_func` instead.
|
|
|
|
"""
|
|
_warn_txt = "`{}` is pending deprecation. Please use `{}` instead."
|
|
|
|
def __init__(self, new_func):
|
|
self.new_func = new_func
|
|
|
|
def __call__(self, func):
|
|
"""Support for functions"""
|
|
self.func = func
|
|
# self._update_docs()
|
|
|
|
@functools.wraps(self.func)
|
|
def wrapper(*args, **kwargs):
|
|
_warn(self._warn_txt.format(self.func.__name__, self.new_func),
|
|
PendingDeprecationWarning)
|
|
return self.func(*args, **kwargs)
|
|
return wrapper
|
|
|
|
def _update_docs(self):
|
|
""" Set the docs to that of the decorated function. """
|
|
self.__name__ = self.func.__name__
|
|
self.__doc__ = self.func.__doc__
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise RuntimeError("This module is not intended to be run by itself.")
|