Skip to content
...@@ -20,6 +20,36 @@ ...@@ -20,6 +20,36 @@
"""Module to implement vector. """Module to implement vector.
Example of usage::
v = Vector2D(10, 20)
v = Vector2D((10, 20))
v = Vector2D([10, 20])
v = Vector2D(iterable)
v = Vector2D(v)
v.x
v.y
# array interface
v[0], v[1]
iter(v)
-v
v + v
v += v
v - v
v -= v
v * 2
2 * v
v *= 2
v / 2
v /= 2
""" """
#################################################################################################### ####################################################################################################
...@@ -51,17 +81,6 @@ class Vector2DBase(Primitive, Primitive2DMixin): ...@@ -51,17 +81,6 @@ class Vector2DBase(Primitive, Primitive2DMixin):
def __init__(self, *args): def __init__(self, *args):
"""
Example of usage::
Vector(1, 3)
Vector((1, 3))
Vector([1, 3])
Vector(iterable)
Vector(vector)
"""
array = self._check_arguments(args) array = self._check_arguments(args)
# call __getitem__ once # call __getitem__ once
...@@ -117,7 +136,7 @@ class Vector2DBase(Primitive, Primitive2DMixin): ...@@ -117,7 +136,7 @@ class Vector2DBase(Primitive, Primitive2DMixin):
############################################## ##############################################
def copy(self): def clone(self):
""" Return a copy of self """ """ Return a copy of self """
return self.__class__(self._v) return self.__class__(self._v)
...@@ -339,6 +358,20 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -339,6 +358,20 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
@property
def permute(self):
"""Return a new vector where x and y are permuted.
"""
xp = self._v[1]
yp = self._v[0]
return self.__class__((xp, yp))
##############################################
@property @property
def parity(self): def parity(self):
...@@ -389,12 +422,12 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -389,12 +422,12 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
def is_parallel(self, other, cross=False): def is_parallel(self, other, return_cross=False):
"""Self is parallel with other""" """Self is parallel with other"""
cross = self.cross(other) cross = self.cross(other)
test = round(cross, 7) == 0 test = round(cross, 7) == 0
if cross: if return_cross:
return test, cross return test, cross
else: else:
return test return test
...@@ -437,10 +470,7 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -437,10 +470,7 @@ class Vector2DFloatBase(Vector2DBase):
############################################## ##############################################
def orientation_with(self, direction): def angle_with(self, direction):
# Fixme: check all cases
# -> angle_with
"""Return the angle of self on direction""" """Return the angle of self on direction"""
...@@ -449,6 +479,8 @@ class Vector2DFloatBase(Vector2DBase): ...@@ -449,6 +479,8 @@ class Vector2DFloatBase(Vector2DBase):
return angle_sign * math.degrees(angle) return angle_sign * math.degrees(angle)
orientation_with = angle_with
#################################################################################################### ####################################################################################################
class Vector2D(Vector2DFloatBase): class Vector2D(Vector2DFloatBase):
...@@ -478,13 +510,13 @@ class Vector2D(Vector2DFloatBase): ...@@ -478,13 +510,13 @@ class Vector2D(Vector2DFloatBase):
############################################## ##############################################
@staticmethod @staticmethod
def from_ellipse(x_radius, y_radius, angle): def from_ellipse(radius_x, radius_y, angle):
"""Create the vector (x_radius*cos(angle), y_radius*sin(angle)). *angle* is in degree.""" """Create the vector (radius_x*cos(angle), radius_y*sin(angle)). *angle* is in degree."""
angle = math.radians(angle) angle = math.radians(angle)
x = x_radius * cos(angle) x = radius_x * cos(angle)
y = y_radius * sin(angle) y = radius_y * sin(angle)
return Vector2D(x, y) # Fixme: classmethod return Vector2D(x, y) # Fixme: classmethod
...@@ -503,6 +535,12 @@ class Vector2D(Vector2DFloatBase): ...@@ -503,6 +535,12 @@ class Vector2D(Vector2DFloatBase):
############################################## ##############################################
def __rmul__(self, scale):
"""Return a new vector equal to the self scaled by scale"""
return self.__mul__(scale)
##############################################
def __imul__(self, scale): def __imul__(self, scale):
"""Scale self by scale""" """Scale self by scale"""
self._v *= scale self._v *= scale
...@@ -523,6 +561,22 @@ class Vector2D(Vector2DFloatBase): ...@@ -523,6 +561,22 @@ class Vector2D(Vector2DFloatBase):
############################################## ##############################################
def scale(self, scale_x, scale_y):
"""Scale self by scale"""
obj = self.clone()
obj._v *= np.array((scale_x, scale_y))
return obj
##############################################
def divide(self, scale_x, scale_y):
"""Scale self by 1/scale"""
obj = self.clone()
obj._v /= np.array((scale_x, scale_y))
return obj
##############################################
def normalise(self): def normalise(self):
"""Normalise the vector""" """Normalise the vector"""
self._v /= self.magnitude self._v /= self.magnitude
...@@ -564,6 +618,12 @@ class NormalisedVector2D(Vector2DFloatBase): ...@@ -564,6 +618,12 @@ class NormalisedVector2D(Vector2DFloatBase):
""" Return a new vector equal to the self scaled by scale """ """ Return a new vector equal to the self scaled by scale """
return self.__class__(scale * self._v) # Fixme: Vector2D ? return self.__class__(scale * self._v) # Fixme: Vector2D ?
##############################################
def __rmul__(self, scale):
""" Return a new vector equal to the self scaled by scale """
return self.__mul__(scale)
#################################################################################################### ####################################################################################################
class HomogeneousVector2D(Vector2D): class HomogeneousVector2D(Vector2D):
......
...@@ -23,12 +23,13 @@ ...@@ -23,12 +23,13 @@
""" """
# Fixme: get_geometry / as argument # Fixme: get_geometry / as argument
# position versus point
#################################################################################################### ####################################################################################################
import logging import logging
from Patro.GeometryEngine.Bezier import CubicBezier2D from Patro.GeometryEngine.Bezier import CubicBezier2D, QuadraticBezier2D
from Patro.GeometryEngine.Conic import Circle2D, Ellipse2D, AngularDomain from Patro.GeometryEngine.Conic import Circle2D, Ellipse2D, AngularDomain
from Patro.GeometryEngine.Polyline import Polyline2D from Patro.GeometryEngine.Polyline import Polyline2D
from Patro.GeometryEngine.Rectangle import Rectangle2D from Patro.GeometryEngine.Rectangle import Rectangle2D
...@@ -38,6 +39,7 @@ from .GraphicItemMixin import ( ...@@ -38,6 +39,7 @@ from .GraphicItemMixin import (
PathStyleItemMixin, PathStyleItemMixin,
PositionMixin, PositionMixin,
TwoPositionMixin, TwoPositionMixin,
ThreePositionMixin,
FourPositionMixin, FourPositionMixin,
NPositionMixin, NPositionMixin,
StartStopAngleMixin, StartStopAngleMixin,
...@@ -151,7 +153,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin): ...@@ -151,7 +153,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
############################################## ##############################################
def __init__(self, scene, position, def __init__(self, scene, position,
x_radius, y_radius, radius_x, radius_y,
angle, angle,
path_style, user_data, path_style, user_data,
start_angle=0, start_angle=0,
...@@ -162,27 +164,27 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin): ...@@ -162,27 +164,27 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
PositionMixin.__init__(self, position) PositionMixin.__init__(self, position)
StartStopAngleMixin.__init__(self, start_angle, stop_angle) StartStopAngleMixin.__init__(self, start_angle, stop_angle)
self._x_radius = x_radius self._radius_x = radius_x
self._y_radius = y_radius self._radius_y = radius_y
self._angle = angle self._angle = angle
############################################## ##############################################
@property @property
def x_radius(self): def radius_x(self):
return self._x_radius return self._radius_x
# @x_radius.setter # @radius_x.setter
# def x_radius(self, value): # def radius_x(self, value):
# self._x_radius = value # self._radius_x = value
@property @property
def y_radius(self): def radius_y(self):
return self._y_radius return self._radius_y
# @y_radius.setter # @radius_y.setter
# def y_radius(self, value): # def radius_y(self, value):
# self._y_radius = value # self._radius_y = value
@property @property
def angle(self): def angle(self):
...@@ -192,7 +194,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin): ...@@ -192,7 +194,7 @@ class EllipseItem(PositionMixin, StartStopAngleMixin, PathStyleItemMixin):
def get_geometry(self): def get_geometry(self):
position = self.casted_position position = self.casted_position
return Ellipse2D(position, self._x_radius, self._y_radius, self._angle) return Ellipse2D(position, self._radius_x, self._radius_y, self._angle)
#################################################################################################### ####################################################################################################
...@@ -311,3 +313,48 @@ class CubicBezierItem(FourPositionMixin, PathStyleItemMixin): ...@@ -311,3 +313,48 @@ class CubicBezierItem(FourPositionMixin, PathStyleItemMixin):
def get_geometry(self): def get_geometry(self):
positions = self.casted_positions positions = self.casted_positions
return CubicBezier2D(*positions) return CubicBezier2D(*positions)
####################################################################################################
class QuadraticBezierItem(ThreePositionMixin, PathStyleItemMixin):
##############################################
def __init__(self,
scene,
position1, position2, position3,
path_style,
user_data,
):
# Fixme: curve vs path
PathStyleItemMixin.__init__(self, scene, path_style, user_data)
ThreePositionMixin.__init__(self, position1, position2, position3)
# super(CubicBezierItem, self).__init__(path_style)
# self._curve = curve
##############################################
# @property
# def curve(self):
# return self._curve
# @curve.setter
# def curve(self, value):
# self._curve = value
##############################################
def get_geometry(self):
positions = self.casted_positions
return QuadraticBezier2D(*positions)
##############################################
@property
def cubic_positions(self):
if not(hasattr(self, '_cubic_points')):
cubic = self.geometry.to_cubic()
self._cubic_points = list(cubic.points)
return self._cubic_points
...@@ -240,14 +240,13 @@ class TwoPositionMixin: ...@@ -240,14 +240,13 @@ class TwoPositionMixin:
#################################################################################################### ####################################################################################################
class FourPositionMixin(TwoPositionMixin): class ThreePositionMixin(TwoPositionMixin):
############################################## ##############################################
def __init__(self, position1, position2, position3, position4): def __init__(self, position1, position2, position3):
TwoPositionMixin.__init__(self, position1, position2) TwoPositionMixin.__init__(self, position1, position2)
self._position3 = position3 self._position3 = position3
self._position4 = position4
############################################## ##############################################
...@@ -255,6 +254,22 @@ class FourPositionMixin(TwoPositionMixin): ...@@ -255,6 +254,22 @@ class FourPositionMixin(TwoPositionMixin):
def position3(self): def position3(self):
return self._position3 return self._position3
@property
def positions(self):
return (self._position1, self._position2, self._position3)
####################################################################################################
class FourPositionMixin(ThreePositionMixin):
##############################################
def __init__(self, position1, position2, position3, position4):
ThreePositionMixin.__init__(self, position1, position2, position3)
self._position4 = position4
##############################################
@property @property
def position4(self): def position4(self):
return self._position4 return self._position4
......
...@@ -113,7 +113,7 @@ class GraphicPathStyle: ...@@ -113,7 +113,7 @@ class GraphicPathStyle:
def line_width_as_float(self): def line_width_as_float(self):
line_width = self._line_width line_width = self._line_width
# Fixme: use scale ? # Fixme: use scale ?
if isinstance(line_width, float): if isinstance(line_width, (int, float)):
return line_width return line_width
else: else:
line_width = line_width.replace('pt', '') line_width = line_width.replace('pt', '')
......
...@@ -73,9 +73,12 @@ class GraphicSceneScope: ...@@ -73,9 +73,12 @@ class GraphicSceneScope:
'rectangle': GraphicItem.RectangleItem, 'rectangle': GraphicItem.RectangleItem,
'segment': GraphicItem.SegmentItem, 'segment': GraphicItem.SegmentItem,
'polyline': GraphicItem.PolylineItem, 'polyline': GraphicItem.PolylineItem,
'quadratic_bezier': GraphicItem.QuadraticBezierItem,
'text': GraphicItem.TextItem, 'text': GraphicItem.TextItem,
} }
_logger = _module_logger.getChild('GraphicSceneScope')
############################################## ##############################################
def __init__(self, transformation=None): def __init__(self, transformation=None):
...@@ -101,6 +104,15 @@ class GraphicSceneScope: ...@@ -101,6 +104,15 @@ class GraphicSceneScope:
############################################## ##############################################
def __len__(self):
return self.number_of_items
@property
def number_of_items(self):
return len(self._items)
##############################################
def __iter__(self): def __iter__(self):
# must be an ordered item list # must be an ordered item list
return iter(self._items.values()) return iter(self._items.values())
...@@ -257,7 +269,8 @@ class GraphicSceneScope: ...@@ -257,7 +269,8 @@ class GraphicSceneScope:
try: # Fixme try: # Fixme
distance = item.distance_to_point(position) distance = item.distance_to_point(position)
# print('distance_to_point {:6.2f} {}'.format(distance, item)) # print('distance_to_point {:6.2f} {}'.format(distance, item))
if distance <= radius: # Fixme: distance is None
if distance is not None and distance <= radius:
items.append((distance, item)) items.append((distance, item))
except NotImplementedError: except NotImplementedError:
pass pass
...@@ -283,25 +296,22 @@ class GraphicSceneScope: ...@@ -283,25 +296,22 @@ class GraphicSceneScope:
# Bezier # Bezier
if isinstance(item, Bezier.QuadraticBezier2D): if isinstance(item, Bezier.QuadraticBezier2D):
# ctor = self._scene.quadratic_bezier ctor = self.quadratic_bezier
# raise NotImplementedError
ctor = self._scene.cubic_bezier
points = list(item.to_cubic().points)
elif isinstance(item, Bezier.CubicBezierItem): elif isinstance(item, Bezier.CubicBezier2D):
ctor = self._scene.cubic_bezier ctor = self.cubic_bezier
# Conic # Conic
elif isinstance(item, Conic.Circle2D): elif isinstance(item, Conic.Circle2D):
ctor = self._scene.circle ctor = self.circle
args = [item.radius] args = [item.radius]
if item.domain: if item.domain:
kwargs['start_angle'] = item.domain.start kwargs['start_angle'] = item.domain.start
kwargs['stop_angle'] = item.domain.stop kwargs['stop_angle'] = item.domain.stop
elif isinstance(item, Conic.Ellipse2D): elif isinstance(item, Conic.Ellipse2D):
ctor = self._scene.ellipse ctor = self.ellipse
args = [item.x_radius, item.y_radius, item.angle] args = [item.radius_x, item.radius_y, item.angle]
# Line # Line
elif isinstance(item, Line.Line2D): elif isinstance(item, Line.Line2D):
...@@ -310,38 +320,39 @@ class GraphicSceneScope: ...@@ -310,38 +320,39 @@ class GraphicSceneScope:
# Path # Path
elif isinstance(item, Path.Path2D): elif isinstance(item, Path.Path2D):
raise NotImplementedError self.add_path(item, path_style)
# Polygon # Polygon
elif isinstance(item, Path.Polygon2D): elif isinstance(item, Polygon.Polygon2D):
# Fixme: to path # Fixme: to path
raise NotImplementedError raise NotImplementedError
# Polyline # Polyline
elif isinstance(item, Polyline.Polyline2D): elif isinstance(item, Polyline.Polyline2D):
ctor = self._scene.polyline ctor = self.polyline
# fixme: to path # fixme: to path
# Rectangle # Rectangle
elif isinstance(item, Rectangle.Rectangle2D): elif isinstance(item, Rectangle.Rectangle2D):
ctor = self._scene.rectangle ctor = self.rectangle
# Fixme: to path # Fixme: to path
# Segment # Segment
if isinstance(item, Segment.Segment2D): elif isinstance(item, Segment.Segment2D):
ctor = self._scene.segment ctor = self.segment
# Spline # Spline
elif isinstance(item, Spline.BSpline2D): elif isinstance(item, Spline.BSpline2D):
return self._add_spline(item, path_style) return self.add_spline(item, path_style)
# Triangle # Triangle
if isinstance(item, Triangle.Triangle2D): elif isinstance(item, Triangle.Triangle2D):
# Fixme: to path # Fixme: to path
raise NotImplementedError raise NotImplementedError
# Not implemented # Not implemented
else: else:
self._logger.warning('Not implemented item {}'.format(item))
raise NotImplementedError raise NotImplementedError
if ctor is not None: if ctor is not None:
...@@ -351,14 +362,101 @@ class GraphicSceneScope: ...@@ -351,14 +362,101 @@ class GraphicSceneScope:
############################################## ##############################################
def add_spline(self, item, path_style): # def add_quadratic_bezier(self, curve, *args, **kwargs):
# # Fixme:
# cubic = curve.to_cubic()
# return self.cubic_bezier(*cubic.points, *args, **kwargs)
##############################################
def add_spline(self, spline, path_style):
return [ return [
self._scene.cubic_bezier(*bezier.points, path_style, user_data=item) self.cubic_bezier(*bezier.points, path_style, user_data=spline)
for bezier in item.to_bezier() for bezier in spline.to_bezier()
] ]
############################################## ##############################################
def add_path(self, path, path_style):
items = []
def add_bulge(segment):
arc = segment.bulge_geometry
arc_item = self.circle(
arc.center, arc.radius,
path_style,
start_angle=arc.domain.start,
stop_angle=arc.domain.stop,
user_data=segment,
)
items.append(arc_item)
def add_by_method(method, segment):
item = method(
*segment.points,
path_style,
user_data=segment,
)
items.append(item)
def add_segment(segment):
add_by_method(self.segment, segment)
def add_ellipse(segment):
# add_segment(Segment.Segment2D(*segment.points))
ellipse = segment.geometry
# print(ellipse, ellipse.domain)
arc_item = self.ellipse(
ellipse.center,
ellipse.radius_x,
ellipse.radius_y,
ellipse.angle,
path_style,
start_angle=ellipse.domain.start,
stop_angle=ellipse.domain.stop,
user_data=segment,
)
items.append(arc_item)
def add_quadratic(segment):
add_by_method(self.quadratic_bezier, segment)
def add_cubic(segment):
add_by_method(self.cubic_bezier, segment)
for segment in path:
item = None
if isinstance(segment, Path.LinearSegment):
# if segment._start_radius is True:
# continue
if segment.radius is not None:
add_bulge(segment)
# if segment._closing is True:
# start_segment = path.start_segment
# add_bulge(start_segment)
# add_segment(start_segment)
add_segment(segment)
elif isinstance(segment, Path.QuadraticBezierSegment):
add_quadratic(segment)
elif isinstance(segment, Path.CubicBezierSegment):
add_cubic(segment)
elif isinstance(segment, Path.ArcSegment):
add_ellipse(segment)
elif isinstance(segment, Path.StringedQuadtraticBezierSegment):
pass
elif isinstance(segment, Path.StringedCubicBezierSegment):
pass
##############################################
# def quadratic_bezier(self, p0, p1, p2, *args, **kwargs):
# # Fixme:
# cubic = Bezier.QuadraticBezier2D(p0, p1, p2).to_cubic()
# return self.add_cubic(cubic, *args, **kwargs)
##############################################
def bezier_path(self, points, degree, *args, **kwargs): def bezier_path(self, points, degree, *args, **kwargs):
"""Add a Bézier curve with the given control points and degree""" """Add a Bézier curve with the given control points and degree"""
...@@ -368,7 +466,6 @@ class GraphicSceneScope: ...@@ -368,7 +466,6 @@ class GraphicSceneScope:
elif degree == 2: elif degree == 2:
# Fixme: # Fixme:
method = self.quadratic_bezier method = self.quadratic_bezier
raise NotImplementedError
elif degree == 3: elif degree == 3:
method = self.cubic_bezier method = self.cubic_bezier
else: else:
......
...@@ -127,7 +127,7 @@ class ReportlabPainter(PdfPainterBase): ...@@ -127,7 +127,7 @@ class ReportlabPainter(PdfPainterBase):
# self._canvas.setSubject(subject) # self._canvas.setSubject(subject)
bounding_box = scene.bounding_box bounding_box = scene.bounding_box
print(bounding_box, bounding_box.x.length, bounding_box.y.length) # print(bounding_box, bounding_box.x.length, bounding_box.y.length)
self._canvas.translate(-bounding_box.x.inf*cm, -bounding_box.y.inf*cm) self._canvas.translate(-bounding_box.x.inf*cm, -bounding_box.y.inf*cm)
self._canvas.translate(1*cm, 1*cm) # Fixme: margin self._canvas.translate(1*cm, 1*cm) # Fixme: margin
......
...@@ -18,9 +18,14 @@ ...@@ -18,9 +18,14 @@
# #
#################################################################################################### ####################################################################################################
"""Module to implement a Qt Painter.
"""
#################################################################################################### ####################################################################################################
import logging import logging
import math
import numpy as np import numpy as np
...@@ -47,12 +52,13 @@ _module_logger = logging.getLogger(__name__) ...@@ -47,12 +52,13 @@ _module_logger = logging.getLogger(__name__)
class QtScene(QObject, GraphicScene): class QtScene(QObject, GraphicScene):
"""Class to add Qt Object features to GraphicScene ."""
_logger = _module_logger.getChild('QtScene') _logger = _module_logger.getChild('QtScene')
############################################## ##############################################
def __init__(self): def __init__(self):
QObject.__init__(self) QObject.__init__(self)
GraphicScene.__init__(self) GraphicScene.__init__(self)
...@@ -60,6 +66,8 @@ class QtScene(QObject, GraphicScene): ...@@ -60,6 +66,8 @@ class QtScene(QObject, GraphicScene):
class QtPainter(Painter): class QtPainter(Painter):
"""Class to implement a Qt painter."""
__STROKE_STYLE__ = { __STROKE_STYLE__ = {
None: None, # Fixme: ??? None: None, # Fixme: ???
StrokeStyle.NoPen: Qt.NoPen, StrokeStyle.NoPen: Qt.NoPen,
...@@ -94,7 +102,6 @@ class QtPainter(Painter): ...@@ -94,7 +102,6 @@ class QtPainter(Painter):
self._show_grid = True self._show_grid = True
# self._paper = paper # self._paper = paper
# self._translation = QPointF(0, 0) # self._translation = QPointF(0, 0)
# self._scale = 1 # self._scale = 1
...@@ -153,12 +160,12 @@ class QtPainter(Painter): ...@@ -153,12 +160,12 @@ class QtPainter(Painter):
def paint(self, painter): def paint(self, painter):
self._logger.info('paint') self._logger.info('Start painting')
self._painter = painter self._painter = painter
if self._show_grid: if self._show_grid:
self._paint_grid() self._paint_grid()
super().paint() super().paint()
self._logger.info('Paint done')
############################################## ##############################################
...@@ -249,6 +256,7 @@ class QtPainter(Painter): ...@@ -249,6 +256,7 @@ class QtPainter(Painter):
xinf, xsup = area.x.inf, area.x.sup xinf, xsup = area.x.inf, area.x.sup
yinf, ysup = area.y.inf, area.y.sup yinf, ysup = area.y.inf, area.y.sup
length = min(area.x.length, area.y.length)
color = QColor('black') color = QColor('black')
brush = QBrush(color) brush = QBrush(color)
...@@ -256,7 +264,9 @@ class QtPainter(Painter): ...@@ -256,7 +264,9 @@ class QtPainter(Painter):
self._painter.setPen(pen) self._painter.setPen(pen)
self._painter.setBrush(Qt.NoBrush) self._painter.setBrush(Qt.NoBrush)
step = 10 step = max(10**int(math.log10(length)), 10)
small_step = step // 10
self._logger.info('Grid of {}/{} for {:.1f} mm'.format(step, small_step, length))
self._paint_axis_grid(xinf, xsup, yinf, ysup, True, step) self._paint_axis_grid(xinf, xsup, yinf, ysup, True, step)
self._paint_axis_grid(yinf, ysup, xinf, xsup, False, step) self._paint_axis_grid(yinf, ysup, xinf, xsup, False, step)
...@@ -266,9 +276,8 @@ class QtPainter(Painter): ...@@ -266,9 +276,8 @@ class QtPainter(Painter):
self._painter.setPen(pen) self._painter.setPen(pen)
self._painter.setBrush(Qt.NoBrush) self._painter.setBrush(Qt.NoBrush)
step = 1 self._paint_axis_grid(xinf, xsup, yinf, ysup, True, small_step)
self._paint_axis_grid(xinf, xsup, yinf, ysup, True, step) self._paint_axis_grid(yinf, ysup, xinf, xsup, False, small_step)
self._paint_axis_grid(yinf, ysup, xinf, xsup, False, step)
############################################## ##############################################
...@@ -294,20 +303,15 @@ class QtPainter(Painter): ...@@ -294,20 +303,15 @@ class QtPainter(Painter):
############################################## ##############################################
def paint_CircleItem(self, item): def _paint_arc(self, item, center, radius_x, radius_y):
center = self.cast_position(item.position)
radius = self.length_scene_to_viewport(item.radius)
pen = self._set_pen(item)
if item.is_closed: if item.is_closed:
self._painter.drawEllipse(center, radius, radius) self._painter.drawEllipse(center, radius, radius)
else: else:
# drawArc cannot be filled ! # drawArc cannot be filled !
rectangle = QRectF( rectangle = QRectF(
center + QPointF(-radius, radius), center + QPointF(-radius_x, radius_y),
center + QPointF(radius, -radius), center + QPointF(radius_x, -radius_y),
) )
start_angle, stop_angle = [int(angle*16) for angle in (item.start_angle, item.stop_angle)] start_angle, stop_angle = [int(angle*16) for angle in (item.start_angle, item.stop_angle)]
span_angle = stop_angle - start_angle span_angle = stop_angle - start_angle
...@@ -318,21 +322,31 @@ class QtPainter(Painter): ...@@ -318,21 +322,31 @@ class QtPainter(Painter):
############################################## ##############################################
def paint_EllipseItem(self, item): def paint_CircleItem(self, item):
center = self.cast_position(item.position) center = self.cast_position(item.position)
x_radius = self.length_scene_to_viewport(item.x_radius) radius = self.length_scene_to_viewport(item.radius)
y_radius = self.length_scene_to_viewport(item.y_radius)
pen = self._set_pen(item) pen = self._set_pen(item)
self._painter.drawEllipse(center, x_radius, y_radius) self._paint_arc(item, center, radius, radius)
############################################## ##############################################
def paint_CubicBezierItem(self, item): def paint_EllipseItem(self, item):
vertices = self.cast_item_positions(item) center = self.cast_position(item.position)
radius_x = self.length_scene_to_viewport(item.radius_x)
radius_y = self.length_scene_to_viewport(item.radius_y)
pen = self._set_pen(item)
# Fixme: angle !!!
self._paint_arc(item, center, radius_x, radius_y)
##############################################
def _paint_cubic(self, item, vertices):
pen = self._set_pen(item) pen = self._set_pen(item)
path = QPainterPath() path = QPainterPath()
...@@ -363,6 +377,20 @@ class QtPainter(Painter): ...@@ -363,6 +377,20 @@ class QtPainter(Painter):
############################################## ##############################################
def paint_QuadraticBezierItem(self, item):
vertices = [self.cast_position(position) for position in item.cubic_positions]
self._paint_cubic(item, vertices)
##############################################
def paint_CubicBezierItem(self, item):
vertices = self.cast_item_positions(item)
self._paint_cubic(item, vertices)
##############################################
def paint_ImageItem(self, item): def paint_ImageItem(self, item):
vertices = self.cast_item_positions(item) vertices = self.cast_item_positions(item)
...@@ -416,6 +444,10 @@ class QtPainter(Painter): ...@@ -416,6 +444,10 @@ class QtPainter(Painter):
class ViewportArea: class ViewportArea:
"""Class to implement a viewport."""
_logger = _module_logger.getChild('ViewportArea')
############################################## ##############################################
def __init__(self): def __init__(self):
...@@ -433,11 +465,24 @@ class ViewportArea: ...@@ -433,11 +465,24 @@ class ViewportArea:
############################################## ##############################################
def __bool__(self): def __bool__(self):
return self._scene is not None return self._scene is not None
############################################## ##############################################
@classmethod
def _to_np_array(cls, *args):
if len(args) == 1:
args = args[0]
return np.array(args, dtype=np.float)
##############################################
@classmethod
def _point_to_np(cls, point):
return cls._to_np_array(point.x(), point.y())
##############################################
@property @property
def viewport_size(self): def viewport_size(self):
return self._viewport_size return self._viewport_size
...@@ -447,7 +492,7 @@ class ViewportArea: ...@@ -447,7 +492,7 @@ class ViewportArea:
def viewport_size(self, geometry): def viewport_size(self, geometry):
# self._width = geometry.width() # self._width = geometry.width()
# self._height = geometry.height() # self._height = geometry.height()
self._viewport_size = np.array((geometry.width(), geometry.height()), dtype=np.float) self._viewport_size = self._to_np_array(geometry.width(), geometry.height())
if self: if self:
self._update_viewport_area() self._update_viewport_area()
...@@ -459,6 +504,8 @@ class ViewportArea: ...@@ -459,6 +504,8 @@ class ViewportArea:
@scene.setter @scene.setter
def scene(self, value): def scene(self, value):
if not isinstance(value, GraphicScene):
raise ValueError
self._scene = value self._scene = value
@property @property
...@@ -476,6 +523,7 @@ class ViewportArea: ...@@ -476,6 +523,7 @@ class ViewportArea:
@property @property
def scale_px_by_mm(self): def scale_px_by_mm(self):
# Fixme: assume unit is mm
return self._scale return self._scale
@property @property
...@@ -499,8 +547,8 @@ class ViewportArea: ...@@ -499,8 +547,8 @@ class ViewportArea:
def _update_viewport_area(self): def _update_viewport_area(self):
offset = self._viewport_size / 2 * self.scale_mm_by_px offset = self._viewport_size / 2 * self.scale_mm_by_px
x, y = list(self._center) x, y = self._center
dx, dy = list(offset) dx, dy = offset
self._area = Interval2D( self._area = Interval2D(
(x - dx, x + dx), (x - dx, x + dx),
...@@ -509,7 +557,7 @@ class ViewportArea: ...@@ -509,7 +557,7 @@ class ViewportArea:
# Fixme: QPointF ??? # Fixme: QPointF ???
self._translation = - QPointF(self._area.x.inf, self._area.y.sup) self._translation = - QPointF(self._area.x.inf, self._area.y.sup)
print('_update_viewport_area', self._center, self.scale_mm_by_px, self._area) # self._logger.debug('_update_viewport_area', self._center, self.scale_mm_by_px, self._area)
############################################## ##############################################
...@@ -520,7 +568,9 @@ class ViewportArea: ...@@ -520,7 +568,9 @@ class ViewportArea:
# scale = min(width_scale, height_scale) # scale = min(width_scale, height_scale)
# scale [px/mm] # scale [px/mm]
axis_scale = self._viewport_size / np.array(self.scene_area.size, dtype=np.float) # Add 2% to scene for margin
margin_scale = 1 + 2 / 100
axis_scale = self._viewport_size / (self._to_np_array(self.scene_area.size) * margin_scale)
axis = axis_scale.argmin() axis = axis_scale.argmin()
scale = axis_scale[axis] scale = axis_scale[axis]
...@@ -529,7 +579,6 @@ class ViewportArea: ...@@ -529,7 +579,6 @@ class ViewportArea:
############################################## ##############################################
def zoom_at(self, center, scale): def zoom_at(self, center, scale):
self._center = center self._center = center
self._scale = scale self._scale = scale
self._update_viewport_area() self._update_viewport_area()
...@@ -540,7 +589,7 @@ class ViewportArea: ...@@ -540,7 +589,7 @@ class ViewportArea:
# Fixme: AttributeError: 'NoneType' object has no attribute 'center' # Fixme: AttributeError: 'NoneType' object has no attribute 'center'
if self: if self:
center = np.array(self.scene_area.center, dtype=np.float) center = self._to_np_array(self.scene_area.center)
scale, axis = self._compute_scale_to_fit_scene() scale, axis = self._compute_scale_to_fit_scene()
self.zoom_at(center, scale) self.zoom_at(center, scale)
...@@ -556,31 +605,36 @@ class ViewportArea: ...@@ -556,31 +605,36 @@ class ViewportArea:
############################################## ##############################################
def length_scene_to_viewport(self, length):
return length * self._scale
##############################################
def viewport_to_scene(self, position): def viewport_to_scene(self, position):
point = QPointF(position.x(), -position.y()) point = QPointF(position.x(), -position.y())
point /= self._scale point /= self._scale
point -= self._translation point -= self._translation
return np.array((point.x(), point.y()), dtype=np.float) return self._point_to_np(point)
############################################## ##############################################
def pan_delta_to_scene(self, position): def length_scene_to_viewport(self, length):
return length * self._scale
# Fixme: ##############################################
point = QPointF(position.x(), position.y())
# point /= self._scale def length_viewport_to_scene(self, length):
return np.array((point.x(), point.y()), dtype=np.float) return length / self._scale
##############################################
def pan_delta_to_scene(self, position):
point = self._point_to_np(position)
point *= self.scale_mm_by_px
return point
#################################################################################################### ####################################################################################################
class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
"""Class to implement a painter as Qt Quick item"""
_logger = _module_logger.getChild('QtQuickPaintedSceneItem') _logger = _module_logger.getChild('QtQuickPaintedSceneItem')
############################################## ##############################################
...@@ -590,6 +644,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -590,6 +644,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
QQuickPaintedItem.__init__(self, parent) QQuickPaintedItem.__init__(self, parent)
QtPainter.__init__(self) QtPainter.__init__(self)
# Setup backend rendering
self.setAntialiasing(True) self.setAntialiasing(True)
# self.setRenderTarget(QQuickPaintedItem.Image) # high quality antialiasing # self.setRenderTarget(QQuickPaintedItem.Image) # high quality antialiasing
self.setRenderTarget(QQuickPaintedItem.FramebufferObject) # use OpenGL self.setRenderTarget(QQuickPaintedItem.FramebufferObject) # use OpenGL
...@@ -600,7 +655,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -600,7 +655,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
def geometryChanged(self, new_geometry, old_geometry): def geometryChanged(self, new_geometry, old_geometry):
print('geometryChanged', new_geometry, old_geometry) # self._logger.info('geometryChanged', new_geometry, old_geometry)
self._viewport_area.viewport_size = new_geometry self._viewport_area.viewport_size = new_geometry
# if self._scene: # if self._scene:
# self._update_transformation() # self._update_transformation()
...@@ -609,7 +664,6 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -609,7 +664,6 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
############################################## ##############################################
# def _update_transformation(self): # def _update_transformation(self):
# area = self._viewport_area.area # area = self._viewport_area.area
# self.translation = - QPointF(area.x.inf, area.y.sup) # self.translation = - QPointF(area.x.inf, area.y.sup)
# self.scale = self._viewport_area.scale_px_by_mm # QtPainter # self.scale = self._viewport_area.scale_px_by_mm # QtPainter
...@@ -632,6 +686,11 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -632,6 +686,11 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
############################################## ##############################################
def length_viewport_to_scene(self, length):
return self._viewport_area.length_viewport_to_scene(length)
##############################################
sceneChanged = Signal() sceneChanged = Signal()
@Property(QtScene, notify=sceneChanged) @Property(QtScene, notify=sceneChanged)
...@@ -641,7 +700,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -641,7 +700,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
@scene.setter @scene.setter
def scene(self, scene): def scene(self, scene):
if self._scene is not scene: if self._scene is not scene:
print('QtQuickPaintedSceneItem set scene', scene) # self._logger.info('QtQuickPaintedSceneItem set scene', scene)
self._logger.info('set scene') # Fixme: don't print ??? self._logger.info('set scene') # Fixme: don't print ???
self._scene = scene self._scene = scene
self._viewport_area.scene = scene self._viewport_area.scene = scene
...@@ -691,7 +750,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -691,7 +750,7 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
@Slot(QPointF, float) @Slot(QPointF, float)
def zoom_at(self, position, zoom): def zoom_at(self, position, zoom):
print('zoom_at', position, zoom) # print('zoom_at', position, zoom)
scene_position = self._viewport_area.viewport_to_scene(position) scene_position = self._viewport_area.viewport_to_scene(position)
self._viewport_area.zoom_at(scene_position, zoom) self._viewport_area.zoom_at(scene_position, zoom)
self.update() self.update()
...@@ -714,24 +773,22 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter): ...@@ -714,24 +773,22 @@ class QtQuickPaintedSceneItem(QQuickPaintedItem, QtPainter):
############################################## ##############################################
@Slot(QPointF) @Slot(QPointF)
def item_at(self, position): def item_at(self, position, radius_px=10):
# Fixme: 1 = 1 cm
# as f of zoom ?
radius = 0.3
self._scene.update_rtree() self._scene.update_rtree()
self._scene.unselect_items() self._scene.unselect_items()
scene_position = Vector2D(self._viewport_area.viewport_to_scene(position)) scene_position = Vector2D(self._viewport_area.viewport_to_scene(position))
radius = self.length_viewport_to_scene(radius_px)
self._logger.info('Item selection at {} with radius {:1f} mm'.format(scene_position, radius))
items = self._scene.item_at(scene_position, radius) items = self._scene.item_at(scene_position, radius)
if items: if items:
distance, nearest_item = items[0] distance, nearest_item = items[0]
print('nearest item at {} #{:6.2f} {} {}'.format(scene_position, len(items), distance, nearest_item.user_data)) # print('nearest item at {} #{:6.2f} {} {}'.format(scene_position, len(items), distance, nearest_item.user_data))
nearest_item.selected = True nearest_item.selected = True
# Fixme: z_value ??? # Fixme: z_value ???
for pair in items[1:]: for pair in items[1:]:
distance, item = pair distance, item = pair
print(' {:6.2f} {}'.format(distance, item.user_data)) # print(' {:6.2f} {}'.format(distance, item.user_data))
self.update() self.update()
#################################################################################################### ####################################################################################################
......
...@@ -46,6 +46,7 @@ def __init__(): ...@@ -46,6 +46,7 @@ def __init__():
_color_data.VALENTINA_COLORS , _color_data.VALENTINA_COLORS ,
): ):
for name, value in color_set.items(): for name, value in color_set.items():
name = name.replace(' ', '_')
color = Color(value, name=name) color = Color(value, name=name)
if name in Colors and color != Colors[name]: if name in Colors and color != Colors[name]:
pass pass
......
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
# #
#################################################################################################### ####################################################################################################
"""Module to implement a Qt Application.
"""
####################################################################################################
__all__ = [ __all__ = [
'QmlApplication', 'QmlApplication',
] ]
...@@ -25,15 +31,20 @@ __all__ = [ ...@@ -25,15 +31,20 @@ __all__ = [
#################################################################################################### ####################################################################################################
import argparse import argparse
import datetime
import logging import logging
import sys import sys
import traceback
from pathlib import Path from pathlib import Path
# Fixme:
from PyQt5 import QtCore
from QtShim.QtCore import ( from QtShim.QtCore import (
Property, Signal, QObject, Property, Signal, QObject,
Qt, QTimer, QUrl, Qt, QTimer, QUrl
) )
from QtShim.QtGui import QGuiApplication from QtShim.QtGui import QGuiApplication, QIcon
from QtShim.QtQml import qmlRegisterType, QQmlApplicationEngine from QtShim.QtQml import qmlRegisterType, QQmlApplicationEngine
# Fixme: PYSIDE-574 qmlRegisterSingletonType and qmlRegisterUncreatableType missing in QtQml # Fixme: PYSIDE-574 qmlRegisterSingletonType and qmlRegisterUncreatableType missing in QtQml
from QtShim.QtQml import qmlRegisterUncreatableType from QtShim.QtQml import qmlRegisterUncreatableType
...@@ -41,6 +52,7 @@ from QtShim.QtQuick import QQuickPaintedItem, QQuickView ...@@ -41,6 +52,7 @@ from QtShim.QtQuick import QQuickPaintedItem, QQuickView
# from QtShim.QtQuickControls2 import QQuickStyle # from QtShim.QtQuickControls2 import QQuickStyle
from Patro.Common.Platform import QtPlatform from Patro.Common.Platform import QtPlatform
from Patro.Common.ArgparseAction import PathAction
from Patro.GraphicEngine.Painter.QtPainter import QtScene, QtQuickPaintedSceneItem from Patro.GraphicEngine.Painter.QtPainter import QtScene, QtQuickPaintedSceneItem
from .rcc import PatroRessource from .rcc import PatroRessource
...@@ -53,6 +65,8 @@ _module_logger = logging.getLogger(__name__) ...@@ -53,6 +65,8 @@ _module_logger = logging.getLogger(__name__)
class QmlApplication(QObject): class QmlApplication(QObject):
"""Class to implement a Qt QML Application."""
_logger = _module_logger.getChild('QmlApplication') _logger = _module_logger.getChild('QmlApplication')
############################################## ##############################################
...@@ -75,32 +89,16 @@ class QmlApplication(QObject): ...@@ -75,32 +89,16 @@ class QmlApplication(QObject):
@scene.setter @scene.setter
def scene(self, scene): def scene(self, scene):
if self._scene is not scene: if self._scene is not scene:
print('QmlApplication set scene', scene) self._logger.info('set scene {}'.format(scene))
self._logger.info('set scene') # Fixme: don't print ???
self._scene = scene self._scene = scene
self.sceneChanged.emit() self.sceneChanged.emit()
#################################################################################################### ####################################################################################################
class PathAction(argparse.Action):
##############################################
def __call__(self, parser, namespace, values, option_string=None):
if values is not None:
if isinstance(values, list):
path = [Path(x) for x in values]
else:
path = Path(values)
else:
path = None
setattr(namespace, self.dest, path)
####################################################################################################
class Application(QObject): class Application(QObject):
"""Class to implement a Qt Application."""
instance = None instance = None
_logger = _module_logger.getChild('Application') _logger = _module_logger.getChild('Application')
...@@ -124,12 +122,17 @@ class Application(QObject): ...@@ -124,12 +122,17 @@ class Application(QObject):
super().__init__() super().__init__()
QtCore.qInstallMessageHandler(self._message_handler)
self._parse_arguments() self._parse_arguments()
self._appplication = QGuiApplication(sys.argv) self._appplication = QGuiApplication(sys.argv)
self._engine = QQmlApplicationEngine() self._engine = QQmlApplicationEngine()
self._qml_application = QmlApplication(self) self._qml_application = QmlApplication(self)
logo_path = ':/icons/logo-256.png'
self._appplication.setWindowIcon(QIcon(logo_path))
self._platform = QtPlatform() self._platform = QtPlatform()
# self._logger.info('\n' + str(self._platform)) # self._logger.info('\n' + str(self._platform))
...@@ -164,6 +167,48 @@ class Application(QObject): ...@@ -164,6 +167,48 @@ class Application(QObject):
############################################## ##############################################
def _print_critical_message(self, message):
# print('\nCritical Error on {}'.format(datetime.datetime.now()))
# print('-'*80)
# print(message)
self._logger.critical(message)
##############################################
def _message_handler(self, msg_type, context, msg):
if msg_type == QtCore.QtDebugMsg:
method = self._logger.debug
elif msg_type == QtCore.QtInfoMsg:
method = self._logger.info
elif msg_type == QtCore.QtWarningMsg:
method = self._logger.warning
elif msg_type in (QtCore.QtCriticalMsg, QtCore.QtFatalMsg):
method = self._logger.critical
# method = None
# local_msg = msg.toLocal8Bit()
# localMsg.constData()
context_file = context.file
if context_file is not None:
file_path = Path(context_file).name
else:
file_path = ''
message = '{1} {3} — {0}'.format(msg, file_path, context.line, context.function)
if method is not None:
method(message)
else:
self._print_critical_message(message)
##############################################
def _on_critical_exception(self, exception):
message = str(exception) + '\n' + traceback.format_exc()
self._print_critical_message(message)
sys.exit(1)
##############################################
@classmethod @classmethod
def setup_gui_application(cls): def setup_gui_application(cls):
...@@ -227,7 +272,6 @@ class Application(QObject): ...@@ -227,7 +272,6 @@ class Application(QObject):
############################################## ##############################################
def _set_context_properties(self): def _set_context_properties(self):
context = self._engine.rootContext() context = self._engine.rootContext()
context.setContextProperty('application', self._qml_application) context.setContextProperty('application', self._qml_application)
...@@ -238,25 +282,29 @@ class Application(QObject): ...@@ -238,25 +282,29 @@ class Application(QObject):
# self._engine.addImportPath('qrc:///qml') # self._engine.addImportPath('qrc:///qml')
qml_path = Path(__file__).parent.joinpath('qml', 'main.qml') qml_path = Path(__file__).parent.joinpath('qml', 'main.qml')
qml_url = QUrl.fromLocalFile(str(qml_path)) self._qml_url = QUrl.fromLocalFile(str(qml_path))
# QUrl('qrc:/qml/main.qml') # QUrl('qrc:/qml/main.qml')
self._engine.load(qml_url) self._engine.objectCreated.connect(self._check_qml_is_loaded)
self._engine.load(self._qml_url)
############################################## ##############################################
def exec_(self): def _check_qml_is_loaded(self, obj, url):
# See https://bugreports.qt.io/browse/QTBUG-39469
if (obj is None and url == self._qml_url):
sys.exit(-1)
##############################################
def exec_(self):
# self._view.show() # self._view.show()
sys.exit(self._appplication.exec_()) sys.exit(self._appplication.exec_())
############################################## ##############################################
def _post_init(self): def _post_init(self):
# Fixme: ui refresh ??? # Fixme: ui refresh ???
self._logger.info('post init') self._logger.info('post init')
if self._args.user_script is not None: if self._args.user_script is not None:
self.execute_user_script(self._args.user_script) self.execute_user_script(self._args.user_script)
...@@ -264,19 +312,24 @@ class Application(QObject): ...@@ -264,19 +312,24 @@ class Application(QObject):
def execute_user_script(self, script_path): def execute_user_script(self, script_path):
"""Execute an user script provided by file *script_path* in a context where is defined a variable """Execute an user script provided by file *script_path* in a context where is defined a
*application* that is a reference to the application instance. variable *application* that is a reference to the application instance.
""" """
script_path = Path(script_path).absolute() script_path = Path(script_path).absolute()
self._logger.info('Execute user script: {}'.format(script_path)) self._logger.info('Execute user script:\n {}'.format(script_path))
try: try:
source = open(script_path).read() source = open(script_path).read()
except FileNotFoundError: except FileNotFoundError:
self._logger.info('File {} not found'.format(script_path)) self._logger.info('File {} not found'.format(script_path))
sys.exit(1) sys.exit(1)
try:
bytecode = compile(source, script_path, 'exec') bytecode = compile(source, script_path, 'exec')
except SyntaxError as exception:
self._on_critical_exception(exception)
try:
exec(bytecode, {'application':self}) exec(bytecode, {'application':self})
except Exception as exception:
self._on_critical_exception(exception)
self._logger.info('User script done') self._logger.info('User script done')
...@@ -98,8 +98,8 @@ ApplicationWindow { ...@@ -98,8 +98,8 @@ ApplicationWindow {
scene: application.scene scene: application.scene
focus: true focus: true
property int pan_speed: 10 property real pan_speed: 1.5 // scale to speedup the mouse paning
property int pan_step: 10 property int pan_step: 10 // px
Keys.onLeftPressed: scene_view.pan_x_y(-pan_step, 0) Keys.onLeftPressed: scene_view.pan_x_y(-pan_step, 0)
Keys.onRightPressed: scene_view.pan_x_y(pan_step, 0) Keys.onRightPressed: scene_view.pan_x_y(pan_step, 0)
...@@ -108,7 +108,7 @@ ApplicationWindow { ...@@ -108,7 +108,7 @@ ApplicationWindow {
function pan_x_y(dx, dy) { function pan_x_y(dx, dy) {
var dxy = Qt.point(dx, dy) var dxy = Qt.point(dx, dy)
console.info('pan', dxy) // console.info('pan', dxy)
scene_view.pan(dxy) scene_view.pan(dxy)
} }
...@@ -150,8 +150,10 @@ ApplicationWindow { ...@@ -150,8 +150,10 @@ ApplicationWindow {
onPositionChanged: { onPositionChanged: {
// console.info('onPositionChanged', mouse.button, mouse_start) // console.info('onPositionChanged', mouse.button, mouse_start)
if (mouse_start !== null) { if (mouse_start !== null) {
var dx = (mouse.x - mouse_start.x) / scene_view.pan_speed var dx = (mouse.x - mouse_start.x)
var dy = (mouse_start.y - mouse.y) / scene_view.pan_speed var dy = (mouse_start.y - mouse.y)
dx *= scene_view.pan_speed
dy *= scene_view.pan_speed
// if (dx^2 + dy^2 > 100) // if (dx^2 + dy^2 > 100)
scene_view.pan_x_y(dx, dy) scene_view.pan_x_y(dx, dy)
mouse_start = Qt.point(mouse.x, mouse.y) mouse_start = Qt.point(mouse.x, mouse.y)
...@@ -162,7 +164,7 @@ ApplicationWindow { ...@@ -162,7 +164,7 @@ ApplicationWindow {
onWheel: { onWheel: {
var direction = wheel.angleDelta.y > 0 var direction = wheel.angleDelta.y > 0
console.info('Mouse wheel', wheel.x, wheel.y, direction) // console.info('Mouse wheel', wheel.x, wheel.y, direction)
var zoom = scene_view.zoom var zoom = scene_view.zoom
if (direction) if (direction)
zoom *= 2 zoom *= 2
......
../../../../doc/sphinx/source/_static/logo-128.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-256.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-32.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-512.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-64.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/logo-96.png
\ No newline at end of file
../../../../doc/sphinx/source/_static/make-logo-png
\ No newline at end of file
../../../../doc/sphinx/source/_static/svg/
\ No newline at end of file
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
<file>qtquickcontrols2.conf</file> <file>qtquickcontrols2.conf</file>
</qresource> </qresource>
<qresource prefix="/"> <qresource prefix="/">
<file>icons/svg/logo.svg</file>
<file>icons/logo-256.png</file>
<file>icons/36x36/settings-overscan-black.png</file> <file>icons/36x36/settings-overscan-black.png</file>
<file>icons/36x36/zoom-fit-width.png</file> <file>icons/36x36/zoom-fit-width.png</file>
<file>icons/36x36/zoom-out-black.png</file> <file>icons/36x36/zoom-out-black.png</file>
......
...@@ -22,9 +22,13 @@ ...@@ -22,9 +22,13 @@
#################################################################################################### ####################################################################################################
import logging
from Patro.Common.Logging import Logging from Patro.Common.Logging import Logging
Logging.setup_logging() Logging.setup_logging()
_module_logger = logging.getLogger(__name__)
_module_logger.info('Started Patro')
#################################################################################################### ####################################################################################################
from Patro.QtApplication.QmlApplication import Application from Patro.QtApplication.QmlApplication import Application
......