Source code for enable.coordinate_box
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
# Enthought library imports
from traits.api import HasTraits, Enum, Instance, Property, Tuple
# Local, relative imports
from .enable_traits import bounds_trait, coordinate_trait
try:
import kiwisolver
except ImportError:
ENABLE_CONSTRAINTS = False
else:
ENABLE_CONSTRAINTS = True
from .layout.constraints_namespace import ConstraintsNamespace
from .layout.ab_constrainable import ABConstrainable
from .layout.utils import (
add_symbolic_constraints,
STRENGTHS,
get_from_constraints_namespace,
)
ConstraintPolicyEnum = Enum("ignore", *STRENGTHS)
del kiwisolver
[docs]class CoordinateBox(HasTraits):
"""
Represents a box in screen space, and provides convenience properties to
access bounds and coordinates in a variety of ways.
Primary attributes (not properties):
position : [x, y]
bounds : [width, height]
Secondary attributes (properties):
x, y : coordinates of the lower-left pixel of the box
x2, y2 : coordinates of the upper-right pixel of the box
width : the number of horizontal pixels in the box; equal to x2-x+1
height : the number of vertical pixels in the box; equal to y2-y+1
Note that setting x and y will modify the position, but setting any of the
other secondary attributes will modify the bounds of the box.
"""
bounds = bounds_trait
# The position relative to the container. If container is None, then
# position will be set to (0,0).
position = coordinate_trait
x = Property
y = Property
x2 = Property
y2 = Property
width = Property
height = Property
# ------------------------------------------------------------------------
# Constraints-based layout
# ------------------------------------------------------------------------
if ENABLE_CONSTRAINTS:
# A read-only symbolic object that represents the left boundary of
# the component
left = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the right boundary
# of the component
right = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the bottom boundary
# of the component
bottom = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the top boundary of
# the component
top = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the width of the
# component
layout_width = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the height of the
# component
layout_height = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the vertical center
# of the component
v_center = Property(fget=get_from_constraints_namespace)
# A read-only symbolic object that represents the horizontal
# center of the component
h_center = Property(fget=get_from_constraints_namespace)
# A size hint for the layout
layout_size_hint = Tuple(0.0, 0.0)
# How strongly a layout box hugs it's width hint.
hug_width = ConstraintPolicyEnum("weak")
# How strongly a layout box hugs it's height hint.
hug_height = ConstraintPolicyEnum("weak")
# How strongly a layout box resists clipping its contents.
resist_width = ConstraintPolicyEnum("strong")
# How strongly a layout box resists clipping its contents.
resist_height = ConstraintPolicyEnum("strong")
# A namespace containing the constraints for this CoordinateBox
_constraints_vars = Instance(ConstraintsNamespace)
# The list of hard constraints which must be applied to the object.
_hard_constraints = Property
# The list of size constraints to apply to the object.
_size_constraints = Property
# ------------------------------------------------------------------------
# Public methods
# ------------------------------------------------------------------------
[docs] def is_in(self, x, y):
"Returns if the point x,y is in the box"
p = self.position
b = self.bounds
dx = x - p[0]
dy = y - p[1]
return (dx >= 0) and (dx < b[0]) and (dy >= 0) and (dy < b[1])
[docs] def as_coordinates(self):
"Returns a 4-tuple (x, y, x2, y2)"
p = self.position
b = self.bounds
return (p[0], p[1], p[0] + b[0] - 1, p[1] + b[1] - 1)
# ------------------------------------------------------------------------
# Property setters and getters
# ------------------------------------------------------------------------
def _get_x(self):
return self.position[0]
def _set_x(self, val):
self.position[0] = val
def _get_y(self):
return self.position[1]
def _set_y(self, val):
self.position[1] = val
def _get_width(self):
return self.bounds[0]
def _set_width(self, val):
if isinstance(val, str):
try:
val = float(val)
except ValueError:
pass
old_value = self.bounds[0]
self.bounds[0] = val
self.trait_property_changed("width", old_value, val)
def _get_height(self):
return self.bounds[1]
def _set_height(self, val):
if isinstance(val, str):
try:
val = float(val)
except ValueError:
pass
old_value = self.bounds[1]
self.bounds[1] = val
self.trait_property_changed("height", old_value, val)
def _get_x2(self):
if self.bounds[0] == 0:
return self.position[0]
return self.position[0] + self.bounds[0] - 1
def _set_x2(self, val):
self.position[0] = val - self.bounds[0] + 1
def _old_set_x2(self, val):
new_width = val - self.position[0] + 1
if new_width < 0.0:
raise RuntimeError("Attempted to set negative component width.")
else:
self.bounds[0] = new_width
def _get_y2(self):
if self.bounds[1] == 0:
return self.position[1]
return self.position[1] + self.bounds[1] - 1
def _set_y2(self, val):
self.position[1] = val - self.bounds[1] + 1
def _old_set_y2(self, val):
new_height = val - self.position[1] + 1
if new_height < 0.0:
raise RuntimeError("Attempted to set negative component height.")
else:
self.bounds[1] = new_height
if ENABLE_CONSTRAINTS:
def __constraints_vars_default(self):
obj_name = self.id if hasattr(self, "id") else ""
cns_names = ConstraintsNamespace(type(self).__name__, obj_name)
add_symbolic_constraints(cns_names)
return cns_names
def _get__hard_constraints(self):
""" Generate the constraints which must always be applied.
"""
left = self.left
bottom = self.bottom
width = self.layout_width
height = self.layout_height
cns = [left >= 0, bottom >= 0, width >= 0, height >= 0]
return cns
def _get__size_constraints(self):
""" Creates the list of size hint constraints for this box.
"""
cns = []
push = cns.append
width_hint, height_hint = self.layout_size_hint
width = self.layout_width
height = self.layout_height
hug_width, hug_height = self.hug_width, self.hug_height
resist_width, resist_height = self.resist_width, self.resist_height
if width_hint >= 0:
if hug_width != "ignore":
cn = (width == width_hint) | hug_width
push(cn)
if resist_width != "ignore":
cn = (width >= width_hint) | resist_width
push(cn)
if height_hint >= 0:
if hug_height != "ignore":
cn = (height == height_hint) | hug_height
push(cn)
if resist_height != "ignore":
cn = (height >= height_hint) | resist_height
push(cn)
return cns
if ENABLE_CONSTRAINTS:
# Register with ABConstrainable so that layout helpers will recognize
# CoordinateBox instances.
ABConstrainable.register(CoordinateBox)