~ubuntu-branches/ubuntu/trusty/guiqwt/trusty

« back to all changes in this revision

Viewing changes to guiqwt/shapes.py

  • Committer: Bazaar Package Importer
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2011-04-07 22:41:50 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20110407224150-kkhppnq7rp2c8b3c
Tags: 2.1.0-1
* Imported Upstream version 2.1.0
* debian/patches/
  - 0001-features-01_bts614083.patch (delete)
  - 0001-feature-fix-the-documentation-build.patch (new)
* add and install the sift desktop file
* recommends python-dicom (Closes: #621421)
  thanks Junqian Gordon Xu for the report

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
    * :py:class:`guiqwt.shapes.EllipseShape`
18
18
    * :py:class:`guiqwt.shapes.Axes`
19
19
    * :py:class:`guiqwt.shapes.XRangeSelection`
 
20
    * :py:class:`guiqwt.shapes.VerticalCursor`
 
21
    * :py:class:`guiqwt.shapes.HorizontalCursor`
20
22
 
21
23
A shape is a plot item (derived from QwtPlotItem) that may be displayed 
22
24
on a 2D plotting widget like :py:class:`guiqwt.curve.CurvePlot` 
68
70
.. autoclass:: XRangeSelection
69
71
   :members:
70
72
   :inherited-members:
 
73
.. autoclass:: VerticalCursor
 
74
   :members:
 
75
   :inherited-members:
 
76
.. autoclass:: HorizontalCursor
 
77
   :members:
 
78
   :inherited-members:
71
79
"""
72
80
 
73
81
import sys, numpy as np
75
83
 
76
84
from PyQt4.QtGui import QPen, QBrush, QPolygonF, QTransform, QPainter
77
85
from PyQt4.QtCore import Qt, QRectF, QPointF, QPoint, QLineF
78
 
from PyQt4.Qwt5 import QwtPlotItem, QwtSymbol, QwtPlotMarker
79
86
 
80
87
from guidata.utils import assert_interfaces_valid, update_dataset
81
88
 
82
89
# Local imports
 
90
from guiqwt.transitional import QwtPlotItem, QwtSymbol, QwtPlotMarker
83
91
from guiqwt.config import CONF, _
84
92
from guiqwt.interfaces import IBasePlotItem, IShapeItemType, ISerializableType
85
93
from guiqwt.styles import (MarkerParam, ShapeParam, RangeShapeParam,
86
 
                           AxesShapeParam)
 
94
                           AxesShapeParam, CursorShapeParam)
87
95
from guiqwt.signals import (SIG_RANGE_CHANGED, SIG_MARKER_CHANGED,
88
 
                            SIG_AXES_CHANGED, SIG_ITEM_MOVED)
 
96
                            SIG_AXES_CHANGED, SIG_ITEM_MOVED, SIG_CURSOR_MOVED)
89
97
 
90
98
class AbstractShape(QwtPlotItem):
91
99
    """Interface pour les objets manipulables
97
105
    __implements__ = (IBasePlotItem,)
98
106
 
99
107
    _readonly = False
 
108
    _private = False
100
109
    _can_select = True
101
110
    _can_resize = True
102
111
    _can_rotate = False #TODO: implement shape rotation?
124
133
    def is_readonly(self):
125
134
        """Return object readonly state"""
126
135
        return self._readonly
 
136
        
 
137
    def set_private(self, state):
 
138
        """Set object as private"""
 
139
        self._private = state
 
140
        
 
141
    def is_private(self):
 
142
        """Return True if object is private"""
 
143
        return self._private
127
144
 
128
145
    def hit_test(self, pos):
129
146
        """return (dist,handle,inside)"""
139
156
        plot = self.plot()
140
157
        return plot.transform(self.xAxis(), x), plot.transform(self.yAxis(), y)
141
158
 
142
 
    def move_point_to(self, handle, pos):
 
159
    def move_point_to(self, handle, pos, ctrl=None):
143
160
        pass
144
161
    
145
 
    def move_local_point_to(self, handle, pos):
 
162
    def move_local_point_to(self, handle, pos, ctrl=None):
 
163
        """Move a handle as returned by hit_test to the new position pos
 
164
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
146
165
        pt = self.canvas_to_axes(pos)
147
 
        self.move_point_to(handle, pt)
 
166
        self.move_point_to(handle, pt, ctrl)
148
167
        
149
168
    def move_local_shape(self, old_pos, new_pos):
150
169
        """Translate the shape such that old_pos becomes new_pos
155
174
        if self.plot():
156
175
            self.plot().emit(SIG_ITEM_MOVED, self, *(old_pt+new_pt))
157
176
 
158
 
    def move_with_selection(self, dx, dy):
 
177
    def move_with_selection(self, delta_x, delta_y):
159
178
        """
160
179
        Translate the shape together with other selected items
161
 
        dx, dy: translation in plot coordinates
 
180
        delta_x, delta_y: translation in plot coordinates
162
181
        """
163
 
        self.move_shape([0, 0], [dx, dy])
 
182
        self.move_shape([0, 0], [delta_x, delta_y])
164
183
 
165
184
    def move_shape(self, old_pos, new_pos):
166
185
        """Translate the shape such that old_pos becomes new_pos
202
221
    """
203
222
    __implements__ = (IBasePlotItem,)
204
223
    _readonly = True
 
224
    _private = False
205
225
 
206
226
    def __init__(self, label_cb=None, constraint_cb=None):
207
227
        super(Marker, self).__init__()
233
253
    def is_readonly(self):
234
254
        """Return object readonly state"""
235
255
        return self._readonly
 
256
        
 
257
    def set_private(self, state):
 
258
        """Set object as private"""
 
259
        self._private = state
 
260
        
 
261
    def is_private(self):
 
262
        """Return True if object is private"""
 
263
        return self._private
236
264
 
237
265
    def hit_test(self, pos):
238
266
        """return (dist,handle,inside)"""
263
291
        plot = self.plot()
264
292
        return plot.transform(self.xAxis(), x), plot.transform(self.yAxis(), y)
265
293
 
266
 
    def move_point_to(self, handle, pos):
 
294
    def move_point_to(self, handle, pos, ctrl=None):
267
295
        x, y = pos
268
296
        if self.constraint_cb:
269
297
            x, y = self.constraint_cb(self, x, y)
272
300
        if self.plot():
273
301
            self.plot().emit(SIG_MARKER_CHANGED, self)
274
302
    
275
 
    def move_local_point_to(self, handle, pos):
 
303
    def move_local_point_to(self, handle, pos, ctrl=None):
 
304
        """Move a handle as returned by hit_test to the new position pos
 
305
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
276
306
        pt = self.canvas_to_axes(pos)
277
307
        self.move_point_to(handle, pt)
278
308
        
283
313
        new_pt = self.canvas_to_axes(new_pos)
284
314
        self.move_shape(old_pt, new_pt)
285
315
 
286
 
    def move_with_selection(self, dx, dy):
 
316
    def move_with_selection(self, delta_x, delta_y):
287
317
        """
288
318
        Translate the shape together with other selected items
289
 
        dx, dy: translation in plot coordinates
 
319
        delta_x, delta_y: translation in plot coordinates
290
320
        """
291
 
        self.move_shape([0, 0], [dx, dy])
 
321
        self.move_shape([0, 0], [delta_x, delta_y])
292
322
 
293
323
    def move_shape(self, old_pos, new_pos):
294
324
        """Translate the shape such that old_pos becomes new_pos
497
527
        else:
498
528
            return self.points.shape[0]-1
499
529
    
500
 
    def move_point_to(self, handle, pos):
 
530
    def move_point_to(self, handle, pos, ctrl=None):
501
531
        self.points[handle, :] = pos
502
532
        
503
533
    def move_shape(self, old_pos, new_pos):
530
560
        """Return the point coordinates"""
531
561
        return tuple(self.points[0])
532
562
    
533
 
    def move_point_to(self, handle, pos):
 
563
    def move_point_to(self, handle, pos, ctrl=None):
534
564
        nx, ny = pos
535
565
        self.points[0] = (nx, ny)
536
566
 
560
590
        self.points[1] = (self.points[0]+self.points[2])/2.
561
591
        super(SegmentShape, self).draw(painter, xMap, yMap, canvasRect)
562
592
    
563
 
    def move_point_to(self, handle, pos):
 
593
    def move_point_to(self, handle, pos, ctrl=None):
564
594
        nx, ny = pos
565
595
        if handle == 0:
566
596
            self.points[0] = (nx, ny)
592
622
    def get_rect(self):
593
623
        return tuple(self.points[0])+tuple(self.points[2])
594
624
 
595
 
    def move_point_to(self, handle, pos):
 
625
    def move_point_to(self, handle, pos, ctrl=None):
596
626
        nx, ny = pos
597
627
        x1, y1 = self.points[0]
598
628
        x2, y2 = self.points[2]
616
646
 
617
647
 
618
648
#FIXME: EllipseShape's ellipse drawing is invalid when aspect_ratio != 1
619
 
class EllipseShape(RectangleShape):
 
649
class EllipseShape(PolygonShape):
620
650
    def __init__(self, x1, y1, x2, y2, ratio=None):
 
651
        self.ratio = ratio
 
652
        super(EllipseShape, self).__init__([], closed=True)
621
653
        self.is_ellipse = False
622
 
        self.ratio = ratio
623
 
        super(EllipseShape, self).__init__(x1, y1, x2, y2)
 
654
        self.set_xdiameter(x1, y1, x2, y2)
624
655
        
625
656
    def switch_to_ellipse(self):
626
657
        self.is_ellipse = True
627
658
 
628
 
    def set_rect(self, x1, y1, x2, y2):
629
 
        """
630
 
        Set the start point of the ellipse's X-axis diameter to (x1, y1) 
631
 
        and its end point to (x2, y2)
632
 
        """
633
 
        self.set_xdiameter(x1, y1, x2, y2)
 
659
    def set_xdiameter(self, x0, y0, x1, y1):
 
660
        """Set the coordinates of the ellipse's X-axis diameter"""
 
661
        xline = QLineF(x0, y0, x1, y1)
 
662
        yline = xline.normalVector()
 
663
        yline.translate(xline.pointAt(.5)-xline.p1())
 
664
        if self.is_ellipse:
 
665
            yline.setLength(self.get_yline().length())
 
666
        elif self.ratio is not None:
 
667
            yline.setLength(xline.length()*self.ratio)
 
668
        yline.translate(yline.pointAt(.5)-yline.p2())
 
669
        self.set_points([(x0, y0), (x1, y1),
 
670
                         (yline.x1(), yline.y1()), (yline.x2(), yline.y2())])
 
671
                         
 
672
    def get_xdiameter(self):
 
673
        """Return the coordinates of the ellipse's X-axis diameter"""
 
674
        return tuple(self.points[0])+tuple(self.points[1])
 
675
                         
 
676
    def set_ydiameter(self, x2, y2, x3, y3):
 
677
        """Set the coordinates of the ellipse's Y-axis diameter"""
 
678
        yline = QLineF(x2, y2, x3, y3)
 
679
        xline = yline.normalVector()
 
680
        xline.translate(yline.pointAt(.5)-yline.p1())
 
681
        if self.is_ellipse:
 
682
            xline.setLength(self.get_xline().length())
 
683
        xline.translate(xline.pointAt(.5)-xline.p2())
 
684
        self.set_points([(xline.x1(), xline.y1()), (xline.x2(), xline.y2()),
 
685
                         (x2, y2), (x3, y3)])
 
686
                         
 
687
    def get_ydiameter(self):
 
688
        """Return the coordinates of the ellipse's Y-axis diameter"""
 
689
        return tuple(self.points[2])+tuple(self.points[3])
 
690
        
 
691
    def get_rect(self):
 
692
        """Circle only!"""
 
693
        (x0, y0), (x1, y1) = self.points[0], self.points[1]
 
694
        xc, yc = .5*(x0+x1), .5*(y0+y1)
 
695
        radius = .5*np.sqrt((x1-x0)**2+(y1-y0)**2)
 
696
        return xc-radius, yc-radius, xc+radius, yc+radius
 
697
    
 
698
    def set_rect(self, x0, y0, x1, y1):
 
699
        """Circle only!"""
 
700
        self.set_xdiameter(x0, .5*(y0+y1), x1, .5*(y0+y1))
634
701
 
635
702
    def compute_elements(self, xMap, yMap):
636
703
        """Return points, lines and ellipse rect"""
680
747
    def get_yline(self):
681
748
        return QLineF(*(tuple(self.points[2])+tuple(self.points[3])))
682
749
 
683
 
    def set_xdiameter(self, x0, y0, x1, y1):
684
 
        xline = QLineF(x0, y0, x1, y1)
685
 
        yline = xline.normalVector()
686
 
        yline.translate(xline.pointAt(.5)-xline.p1())
687
 
        if self.is_ellipse:
688
 
            yline.setLength(self.get_yline().length())
689
 
        elif self.ratio is not None:
690
 
            yline.setLength(xline.length()*self.ratio)
691
 
        yline.translate(yline.pointAt(.5)-yline.p2())
692
 
        self.set_points([(x0, y0), (x1, y1),
693
 
                         (yline.x1(), yline.y1()), (yline.x2(), yline.y2())])
694
 
                         
695
 
    def set_ydiameter(self, x2, y2, x3, y3):
696
 
        yline = QLineF(x2, y2, x3, y3)
697
 
        xline = yline.normalVector()
698
 
        xline.translate(yline.pointAt(.5)-yline.p1())
699
 
        if self.is_ellipse:
700
 
            xline.setLength(self.get_xline().length())
701
 
        xline.translate(xline.pointAt(.5)-xline.p2())
702
 
        self.set_points([(xline.x1(), xline.y1()), (xline.x2(), xline.y2()),
703
 
                         (x2, y2), (x3, y3)])
704
 
 
705
 
    def move_point_to(self, handle, pos):
 
750
    def move_point_to(self, handle, pos, ctrl=None):
706
751
        nx, ny = pos
707
752
        if handle == 0:
708
753
            x1, y1 = self.points[1]
 
754
            if ctrl:
 
755
                # When <Ctrl> is pressed, the center position is unchanged
 
756
                x0, y0 = self.points[0]
 
757
                x1, y1 = x1+x0-nx, y1+y0-ny
709
758
            self.set_xdiameter(nx, ny, x1, y1)
710
759
        elif handle == 1:
711
760
            x0, y0 = self.points[0]
 
761
            if ctrl:
 
762
                # When <Ctrl> is pressed, the center position is unchanged
 
763
                x1, y1 = self.points[1]
 
764
                x0, y0 = x0+x1-nx, y0+y1-ny
712
765
            self.set_xdiameter(x0, y0, nx, ny)
713
 
        elif handle == 2 and self.is_ellipse:
 
766
        elif handle == 2:
714
767
            x3, y3 = self.points[3]
 
768
            if ctrl:
 
769
                # When <Ctrl> is pressed, the center position is unchanged
 
770
                x2, y2 = self.points[2]
 
771
                x3, y3 = x3+x2-nx, y3+y2-ny
715
772
            self.set_ydiameter(nx, ny, x3, y3)
716
 
        elif handle == 3 and self.is_ellipse:
 
773
        elif handle == 3:
717
774
            x2, y2 = self.points[2]
 
775
            if ctrl:
 
776
                # When <Ctrl> is pressed, the center position is unchanged
 
777
                x3, y3 = self.points[3]
 
778
                x2, y2 = x2+x3-nx, y2+y3-ny
718
779
            self.set_ydiameter(x2, y2, nx, ny)
719
 
        elif handle in (2, 3):
720
 
            delta = (nx, ny)-self.points[handle]
721
 
            self.points += delta
722
780
        elif handle == -1:
723
781
            delta = (nx, ny)-self.points.mean(axis=0)
724
782
            self.points += delta
725
783
 
 
784
    def __reduce__(self):
 
785
        state = (self.shapeparam, self.points, self.z())
 
786
        return (self.__class__, (0,0,0,0), state)
 
787
 
726
788
assert_interfaces_valid(EllipseShape)
727
789
 
728
790
 
769
831
        self.axesparam.read_config(CONF, section, option)
770
832
        self.axesparam.update_axes(self)
771
833
 
772
 
    def move_point_to(self, handle, pos):
 
834
    def move_point_to(self, handle, pos, ctrl=None):
773
835
        _nx, _ny = pos
774
836
        p0, p1, _p3, p2 = list(self.points)
775
837
        d1x = p1[0]-p0[0]
865
927
        
866
928
assert_interfaces_valid(Axes)
867
929
 
868
 
        
 
930
 
869
931
class XRangeSelection(AbstractShape):
870
932
    def __init__(self, _min, _max):
871
 
        super(XRangeSelection,self).__init__()
 
933
        super(XRangeSelection, self).__init__()
872
934
        self._min = _min
873
935
        self._max = _max
874
936
        self.shapeparam = RangeShapeParam(_("Range"), icon="xrange.png")
920
982
        sym.draw(painter, QPoint(x0,y))
921
983
        sym.draw(painter, QPoint(x1,y))
922
984
        
923
 
    def get_handle_rect(self, xMap, yMap, xpos, ypos):
924
 
        cx = xMap.transform(xpos)
925
 
        cy = yMap.transform(ypos)
926
 
        hs = self._handle_size
927
 
        return QRectF(cx-hs, cy-hs, 2*hs, 2*hs)
928
 
        
929
985
    def hit_test(self, pos):
930
986
        x, _y = pos.x(), pos.y()
931
987
        x0, x1, _yp = self.get_handles_pos()
938
994
        inside = bool(x0<x<x1)
939
995
        return dist, handle, inside, None
940
996
        
941
 
    def move_local_point_to(self, handle, pos):
 
997
    def move_local_point_to(self, handle, pos, ctrl=None):
 
998
        """Move a handle as returned by hit_test to the new position pos
 
999
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
942
1000
        val = self.plot().invTransform(self.xAxis(), pos.x())
943
1001
        self.move_point_to(handle, (val, 0))
944
1002
        
945
 
    def move_point_to(self, hnd, pos):
 
1003
    def move_point_to(self, hnd, pos, ctrl=None):
946
1004
        val, _ = pos
947
1005
        if hnd == 0:
948
1006
            self._min = val
967
1025
 
968
1026
    def move_shape(self, old_pos, new_pos):
969
1027
        dx = new_pos[0]-old_pos[0]
970
 
        _dy = new_pos[1]-old_pos[1]
971
1028
        self._min += dx
972
1029
        self._max += dx
973
1030
        self.plot().emit(SIG_RANGE_CHANGED, self, self._min, self._max)
983
1040
        self.shapeparam.update_range(self)
984
1041
        self.sel_brush = QBrush(self.brush)
985
1042
        
986
 
assert_interfaces_valid(XRangeSelection)
 
 
b'\\ No newline at end of file'
 
1043
assert_interfaces_valid(XRangeSelection)
 
1044
 
 
1045
 
 
1046
class Cursor(AbstractShape):
 
1047
    """Horizontal/Vertical cursor base class"""
 
1048
    ICON = None
 
1049
    
 
1050
    def __init__(self, pos, moveable=True):
 
1051
        super(Cursor, self).__init__()
 
1052
        self.pos = pos
 
1053
        self.handle_pos = None
 
1054
        self.shapeparam = CursorShapeParam(_("Cursor"), icon=self.ICON)
 
1055
        self.shapeparam.read_config(CONF, "histogram", "range")
 
1056
        self.pen = None
 
1057
        self.sel_pen = None
 
1058
        self.handle = None
 
1059
        self.symbol = None
 
1060
        self.sel_symbol = None
 
1061
        self.shapeparam.update_range(self) # creates all the above QObjects
 
1062
        self._can_move = moveable
 
1063
        self._can_resize = moveable
 
1064
        
 
1065
    def update_handle_pos(self, xMap, yMap, canvasRect):
 
1066
        raise NotImplementedError
 
1067
        
 
1068
    def get_line(self, xMap, yMap, plot):
 
1069
        """Return line to be drawn (QLineF object)"""
 
1070
        raise NotImplementedError
 
1071
        
 
1072
    def draw(self, painter, xMap, yMap, canvasRect):
 
1073
        if self.selected:
 
1074
            pen = self.sel_pen
 
1075
            sym = self.sel_symbol
 
1076
        else:
 
1077
            pen = self.pen
 
1078
            sym = self.symbol
 
1079
        painter.setPen(pen)
 
1080
        pos1, pos2 = self.get_line(xMap, yMap, canvasRect)
 
1081
        painter.drawLine(pos1, pos2)
 
1082
        
 
1083
        self.update_handle_pos(xMap, yMap, canvasRect) 
 
1084
        if self.can_move():
 
1085
            painter.setPen(pen)
 
1086
            sym.draw(painter, QPoint(*self.handle_pos))
 
1087
        
 
1088
    def get_distance_from_point(self, point):
 
1089
        raise NotImplementedError
 
1090
        
 
1091
    def hit_test(self, pos):
 
1092
        dist = self.get_distance_from_point(pos)
 
1093
        handle = 0
 
1094
        inside = False
 
1095
        return dist, handle, inside, None
 
1096
        
 
1097
    def move_local_point_to(self, handle, pos, ctrl=None):
 
1098
        """Move a handle as returned by hit_test to the new position pos
 
1099
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
 
1100
        raise NotImplementedError
 
1101
        
 
1102
    def move_point_to(self, hnd, pos, ctrl=None):
 
1103
        val, _ = pos
 
1104
        self.pos = val
 
1105
        if self.plot():
 
1106
            self.plot().emit(SIG_CURSOR_MOVED, self, self.pos)
 
1107
 
 
1108
    def get_pos(self):
 
1109
        return self.pos
 
1110
        
 
1111
    def set_pos(self, pos, dosignal=True):
 
1112
        self.pos = pos
 
1113
        if dosignal:
 
1114
            self.plot().emit(SIG_CURSOR_MOVED, self, self.pos)
 
1115
 
 
1116
    def move_shape(self, old_pos, new_pos):
 
1117
        raise NotImplementedError
 
1118
        
 
1119
    def get_item_parameters(self, itemparams):
 
1120
        self.shapeparam.update_param(self)
 
1121
        itemparams.add("ShapeParam", self, self.shapeparam)
 
1122
    
 
1123
    def set_item_parameters(self, itemparams):
 
1124
        update_dataset(self.shapeparam, itemparams.get("ShapeParam"),
 
1125
                       visible_only=True)
 
1126
        self.shapeparam.update_range(self)
 
1127
        
 
1128
assert_interfaces_valid(Cursor)
 
1129
 
 
1130
class VerticalCursor(Cursor):
 
1131
    """Vertical cursor"""
 
1132
    ICON = 'vcursor.png'
 
1133
    def update_handle_pos(self, xMap, yMap, canvasRect):
 
1134
        x = xMap.transform(self.pos)
 
1135
        y = canvasRect.center().y()
 
1136
        self.handle_pos = x, y
 
1137
        
 
1138
    def get_line(self, xMap, yMap, canvasRect):
 
1139
        """Return line to be drawn (QLineF object)"""
 
1140
        rect = QRectF(canvasRect)
 
1141
        rect.setLeft(xMap.transform(self.pos))
 
1142
        return rect.topLeft(), rect.bottomLeft()
 
1143
        
 
1144
    def get_distance_from_point(self, point):
 
1145
        x, y = self.handle_pos
 
1146
        return fabs(x-point.x())
 
1147
        
 
1148
    def move_local_point_to(self, handle, pos, ctrl=None):
 
1149
        """Move a handle as returned by hit_test to the new position pos
 
1150
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
 
1151
        val = self.plot().invTransform(self.xAxis(), pos.x())
 
1152
        self.move_point_to(handle, (val, 0))
 
1153
 
 
1154
    def move_shape(self, old_pos, new_pos):
 
1155
        dx = new_pos[0]-old_pos[0]
 
1156
        self.pos += dx
 
1157
        self.plot().emit(SIG_CURSOR_MOVED, self, self.pos)
 
1158
        self.plot().replot()
 
1159
        
 
1160
assert_interfaces_valid(VerticalCursor)
 
1161
 
 
1162
class HorizontalCursor(Cursor):
 
1163
    """Horizontal cursor"""
 
1164
    ICON = 'hcursor.png'
 
1165
    def update_handle_pos(self, xMap, yMap, canvasRect):
 
1166
        x = canvasRect.center().x()
 
1167
        y = yMap.transform(self.pos)
 
1168
        self.handle_pos = x, y
 
1169
        
 
1170
    def get_line(self, xMap, yMap, canvasRect):
 
1171
        """Return line to be drawn (QLineF object)"""
 
1172
        rect = QRectF(canvasRect)
 
1173
        rect.setTop(yMap.transform(self.pos))
 
1174
        return rect.topLeft(), rect.topRight()
 
1175
        
 
1176
    def get_distance_from_point(self, point):
 
1177
        x, y = self.handle_pos
 
1178
        return fabs(y-point.y())
 
1179
        
 
1180
    def move_local_point_to(self, handle, pos, ctrl=None):
 
1181
        """Move a handle as returned by hit_test to the new position pos
 
1182
        ctrl: True if <Ctrl> button is being pressed, False otherwise"""
 
1183
        val = self.plot().invTransform(self.yAxis(), pos.y())
 
1184
        self.move_point_to(handle, (val, 0))
 
1185
 
 
1186
    def move_shape(self, old_pos, new_pos):
 
1187
        dy = new_pos[0]-old_pos[0]
 
1188
        self.pos += dy
 
1189
        self.plot().emit(SIG_CURSOR_MOVED, self, self.pos)
 
1190
        self.plot().replot()
 
1191
        
 
1192
assert_interfaces_valid(HorizontalCursor)