Files
Phoenix/wx/lib/plot/utils.py
2016-07-07 09:13:19 -07:00

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.")