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

« back to all changes in this revision

Viewing changes to guiqwt/cross_section.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:
52
52
from guiqwt.interfaces import (ICSImageItemType, IPanel, IBasePlotItem,
53
53
                               ICurveItemType)
54
54
from guiqwt.panels import PanelWidget, ID_XCS, ID_YCS
55
 
from guiqwt.curve import CurvePlot, CurveItem
56
 
from guiqwt.image import ImagePlot
 
55
from guiqwt.curve import CurvePlot, ErrorBarCurveItem
 
56
from guiqwt.image import ImagePlot, LUT_MAX
57
57
from guiqwt.styles import CurveParam
58
 
from guiqwt.tools import SelectTool, BasePlotMenuTool, AntiAliasingTool
 
58
from guiqwt.tools import (SelectTool, BasePlotMenuTool, AntiAliasingTool,
 
59
                          DeleteItemTool)
59
60
from guiqwt.signals import (SIG_MARKER_CHANGED, SIG_PLOT_LABELS_CHANGED,
60
61
                            SIG_ANNOTATION_CHANGED, SIG_AXIS_DIRECTION_CHANGED,
61
62
                            SIG_ITEMS_CHANGED, SIG_ACTIVE_ITEM_CHANGED,
62
 
                            SIG_LUT_CHANGED)
 
63
                            SIG_LUT_CHANGED, SIG_CS_CURVE_CHANGED,
 
64
                            SIG_MASK_CHANGED)
63
65
from guiqwt.plot import PlotManager
64
66
from guiqwt.builder import make
65
67
 
66
68
 
67
 
class CrossSectionItem(CurveItem):
 
69
class CrossSectionItem(ErrorBarCurveItem):
68
70
    """A Qwt item representing cross section data"""
69
71
    __implements__ = (IBasePlotItem,)
70
72
    _inverted = None
71
73
    
72
 
    def __init__(self, curveparam=None):
73
 
        super(CrossSectionItem, self).__init__(curveparam)
 
74
    def __init__(self, curveparam=None, errorbarparam=None):
 
75
        ErrorBarCurveItem.__init__(self, curveparam, errorbarparam)
74
76
        self.perimage_mode = True
75
77
        self.autoscale_mode = True
76
78
        self.apply_lut = False
77
79
        self.source = None
78
 
 
 
80
        
79
81
    def set_source_image(self, src):
80
82
        """
81
83
        Set source image
91
93
    def get_cross_section(self, obj):
92
94
        """Get cross section data from source image"""
93
95
        raise NotImplementedError
94
 
        
95
 
    def update_item(self, obj):
96
 
        plot = self.plot()
97
 
        if not plot:
98
 
            return
99
 
        source = self.get_source_image()
100
 
        if source is None or not plot.isVisible():
101
 
            return
 
96
 
 
97
    def update_curve_data(self, obj):
102
98
        sectx, secty = self.get_cross_section(obj)
103
99
        if secty.size == 0 or np.all(np.isnan(secty)):
104
100
            sectx, secty = np.array([]), np.array([])
105
101
        if self._inverted:
106
 
            self.set_data(secty, sectx)
 
102
            self.process_curve_data(secty, sectx)
107
103
        else:
108
 
            self.set_data(sectx, secty)
 
104
            self.process_curve_data(sectx, secty)
 
105
            
 
106
    def process_curve_data(self, x, y, dx=None, dy=None):
 
107
        """
 
108
        Override this method to process data 
 
109
        before updating the displayed curve
 
110
        """
 
111
        self.set_data(x, y, dx, dy)
 
112
 
 
113
    def update_item(self, obj):
 
114
        plot = self.plot()
 
115
        if not plot:
 
116
            return
 
117
        source = self.get_source_image()
 
118
        if source is None or not plot.isVisible():
 
119
            return
 
120
        self.update_curve_data(obj)
 
121
        self.plot().emit(SIG_CS_CURVE_CHANGED, self)
109
122
        if not self.autoscale_mode:
110
123
            self.update_scale()
111
124
 
112
125
    def update_scale(self):
113
126
        raise NotImplementedError
114
127
 
115
 
assert_interfaces_valid(CrossSectionItem)
116
 
 
117
128
 
118
129
def get_image_data(plot, p0, p1, apply_lut=False):
119
130
    """
120
131
    Save rectangular plot area
121
132
    p0, p1: resp. top left and bottom right points (QPoint objects)
122
133
    """
123
 
    from guiqwt.image import (ImageItem, XYImageItem, TrImageItem,
124
 
                              get_image_from_plot, get_plot_source_rect)
125
 
                           
126
 
    items = [item for item in plot.items if isinstance(item, ImageItem)
127
 
             and not isinstance(item, XYImageItem)]
 
134
    from guiqwt.image import TrImageItem, get_image_from_plot, get_plot_qrect
 
135
    items = plot.get_items(item_type=ICSImageItemType)
128
136
    if not items:
129
 
        return
130
 
    _src_x, _src_y, src_w, src_h = get_plot_source_rect(plot, p0, p1)
 
137
        raise TypeError, _("There is no supported image item in current plot.")
 
138
    _src_x, _src_y, src_w, src_h = get_plot_qrect(plot, p0, p1).getRect()
131
139
    trparams = [item.get_transform() for item in items
132
140
                if isinstance(item, TrImageItem)]
133
141
    if trparams:
143
151
    Return None if object does not support this feature 
144
152
    (like markers, points, ...)
145
153
    """
146
 
    try:
 
154
    if hasattr(obj, 'get_rect'):
147
155
        return obj.get_rect()
148
 
    except AttributeError:
149
 
        return
150
156
 
151
157
def get_object_coordinates(obj):
152
158
    """Return Marker or PointShape/AnnotatedPoint object coordinates"""
153
 
    try:
 
159
    if hasattr(obj, 'get_pos'):
154
160
        return obj.get_pos()
155
 
    except AttributeError:
 
161
    else:
156
162
        return obj.xValue(), obj.yValue()
157
163
 
158
164
def get_plot_x_section(obj, apply_lut=False):
162
168
    """
163
169
    _x0, y0 = get_object_coordinates(obj)
164
170
    plot = obj.plot()
165
 
    xmap = plot.canvasMap(plot.AXES["bottom"])
 
171
    xmap = plot.canvasMap(plot.X_BOTTOM)
166
172
    xc0, xc1 = xmap.p1(), xmap.p2()
167
173
    _xc0, yc0 = obj.axes_to_canvas(0, y0)
168
174
    if plot.get_axis_direction("left"):
172
178
    try:
173
179
        data = get_image_data(plot, QPoint(xc0, yc0), QPoint(xc1, yc1),
174
180
                              apply_lut=apply_lut)
175
 
    except (ValueError, ZeroDivisionError):
 
181
    except (ValueError, ZeroDivisionError, TypeError):
176
182
        return np.array([]), np.array([])
177
183
    y = data.mean(axis=0)
178
184
    x0, _y0 = obj.canvas_to_axes(QPoint(xc0, yc0))
187
193
    """
188
194
    x0, _y0 = get_object_coordinates(obj)
189
195
    plot = obj.plot()
190
 
    ymap = plot.canvasMap(plot.AXES["left"])
 
196
    ymap = plot.canvasMap(plot.Y_LEFT)
191
197
    yc0, yc1 = ymap.p1(), ymap.p2()
192
198
    if plot.get_axis_direction("left"):
193
199
        yc1, yc0 = yc0, yc1
196
202
    try:
197
203
        data = get_image_data(plot, QPoint(xc0, yc0), QPoint(xc1, yc1),
198
204
                              apply_lut=apply_lut)
199
 
    except (ValueError, ZeroDivisionError):
 
205
    except (ValueError, ZeroDivisionError, TypeError):
200
206
        return np.array([]), np.array([])
201
207
    y = data.mean(axis=1)
202
208
    _x0, y0 = obj.canvas_to_axes(QPoint(xc0, yc0))
224
230
    try:
225
231
        data = get_image_data(obj.plot(), QPoint(xc0, yc0), QPoint(xc1, yc1),
226
232
                              apply_lut=apply_lut)
227
 
    except (ValueError, ZeroDivisionError):
 
233
    except (ValueError, ZeroDivisionError, TypeError):
228
234
        return np.array([]), np.array([])
229
235
    y = data.mean(axis=0)
230
236
    if invert:
251
257
    try:
252
258
        data = get_image_data(obj.plot(), QPoint(xc0, yc0), QPoint(xc1, yc1),
253
259
                              apply_lut=apply_lut)
254
 
    except (ValueError, ZeroDivisionError):
 
260
    except (ValueError, ZeroDivisionError, TypeError):
255
261
        return np.array([]), np.array([])
256
262
    y = data.mean(axis=1)
257
263
    x = np.linspace(y0, y1, len(y))
284
290
            
285
291
    def update_scale(self):
286
292
        plot = self.plot()
287
 
        axis_id = plot.xBottom
 
293
        axis_id = plot.X_BOTTOM
288
294
        source = self.get_source_image()
289
295
        sdiv = source.plot().axisScaleDiv(axis_id)
290
296
        plot.setAxisScale(axis_id, sdiv.lowerBound(), sdiv.upperBound())
314
320
            
315
321
    def update_scale(self):
316
322
        plot = self.plot()
317
 
        axis_id = plot.yLeft
 
323
        axis_id = plot.Y_LEFT
318
324
        source = self.get_source_image()
319
325
        sdiv = source.plot().axisScaleDiv(axis_id)
320
326
        plot.setAxisScale(axis_id, sdiv.lowerBound(), sdiv.upperBound())
321
327
        plot.replot()
322
328
 
323
329
 
 
330
LUT_AXIS_TITLE = _("LUT scale")+(" (0-%d)" % LUT_MAX)
 
331
 
324
332
class CrossSectionPlot(CurvePlot):
325
333
    """Cross section plot"""
 
334
    CURVE_LABEL = _("Cross section")
 
335
    LABEL_TEXT = _("Enable a marker")
326
336
    _height = None
327
337
    _width = None
328
338
    CS_AXIS = None
329
339
    Z_AXIS = None
330
340
    Z_MAX_MAJOR = 5
331
341
    CURVETYPE = None
 
342
    SHADE = .2
332
343
    def __init__(self, parent=None):
333
344
        super(CrossSectionPlot, self).__init__(parent=parent, title="",
334
345
                                               section="cross_section")
335
346
        self.perimage_mode = True
336
347
        self.autoscale_mode = True
 
348
        self.autorefresh_mode = True
337
349
        self.apply_lut = False
338
350
        
339
351
        self.last_obj = None
341
353
        self._shapes = {}
342
354
        
343
355
        self.curveparam = CurveParam(_("Curve"), icon="curve.png")
344
 
        self.curveparam.read_config(CONF, "cross_section", "curve")
345
 
        self.curveparam.curvetype = self.CURVETYPE
 
356
        self.set_curve_style("cross_section", "curve")
346
357
        
347
358
        if self._height is not None:
348
359
            self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
349
 
        if self._width is not None:
 
360
        elif self._width is not None:
350
361
            self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
351
362
            
352
 
        self.label = make.label(_("Enable a marker"), "C", (0,0), "C")
 
363
        self.label = make.label(self.LABEL_TEXT, "C", (0,0), "C")
 
364
        self.label.set_readonly(True)
353
365
        self.add_item(self.label)
354
366
        
355
367
        self.setAxisMaxMajor(self.Z_AXIS, self.Z_MAX_MAJOR)
356
368
        self.setAxisMaxMinor(self.Z_AXIS, 0)
357
369
 
 
370
    def set_curve_style(self, section, option):
 
371
        self.curveparam.read_config(CONF, section, option)
 
372
        self.curveparam.curvetype = self.CURVETYPE
 
373
        self.curveparam.label = self.CURVE_LABEL
 
374
        
358
375
    def connect_plot(self, plot):
359
376
        if not isinstance(plot, ImagePlot):
360
377
            # Connecting only to image plot widgets (allow mixing image and 
362
379
            return
363
380
        self.connect(plot, SIG_ITEMS_CHANGED, self.items_changed)
364
381
        self.connect(plot, SIG_LUT_CHANGED, self.lut_changed)
 
382
        self.connect(plot, SIG_MASK_CHANGED, lambda item: self.update_plot())
365
383
        self.connect(plot, SIG_ACTIVE_ITEM_CHANGED, self.active_item_changed)
366
384
        self.connect(plot, SIG_MARKER_CHANGED, self.marker_changed)
367
385
        self.connect(plot, SIG_ANNOTATION_CHANGED, self.shape_changed)
368
386
        self.connect(plot, SIG_PLOT_LABELS_CHANGED, self.plot_labels_changed)
369
387
        self.connect(plot, SIG_AXIS_DIRECTION_CHANGED, self.axis_dir_changed)
370
388
        self.plot_labels_changed(plot)
371
 
        for axis_id in plot.AXES:
 
389
        for axis_id in plot.AXIS_IDS:
372
390
            self.axis_dir_changed(plot, axis_id)
373
391
        self.items_changed(plot)
374
392
        
389
407
                shapes.pop(shapes.index(shape))
390
408
                break
391
409
        
392
 
    def standard_tools(self, manager):
393
 
        manager.add_tool(SelectTool)
394
 
        manager.add_tool(BasePlotMenuTool, "item")
395
 
        manager.add_tool(BasePlotMenuTool, "axes")
396
 
        manager.add_tool(BasePlotMenuTool, "grid")
397
 
        manager.add_tool(AntiAliasingTool)
398
 
        manager.get_default_tool().activate()
399
 
        
400
410
    def create_cross_section_item(self):
401
411
        raise NotImplementedError
 
412
        
 
413
    def add_cross_section_item(self, source):
 
414
        curve = self.create_cross_section_item()
 
415
        curve.set_source_image(source)
 
416
        curve.set_readonly(True)
 
417
        self.add_item(curve, z=0)
 
418
        self.known_items[source] = curve
 
419
        
 
420
    def get_cross_section_curves(self):
 
421
        return self.known_items.values()
402
422
 
403
423
    def items_changed(self, plot):
404
424
        self.known_items = {}
411
431
            self.replot()
412
432
            return
413
433
            
414
 
        self.curveparam.shade = min([.3, .8/len(items)])
 
434
        self.curveparam.shade = self.SHADE/len(items)
415
435
        for item in items:
416
 
            curve = self.create_cross_section_item()
417
 
            curve.set_source_image(item)
418
 
            self.add_item(curve, z=0)
419
 
            self.known_items[item] = curve
 
436
            if item.isVisible():
 
437
                self.add_cross_section_item(source=item)
420
438
 
421
439
    def active_item_changed(self, plot):
422
440
        """Active item has just changed"""
441
459
            return False
442
460
        
443
461
    def shape_changed(self, shape):
444
 
        if self.is_shape_known(shape):
445
 
            self.update_plot(shape)
 
462
        if self.autorefresh_mode:
 
463
            if self.is_shape_known(shape):
 
464
                self.update_plot(shape)
446
465
            
447
466
    def get_last_obj(self):
448
467
        if self.last_obj is not None:
480
499
                curve.update_item(obj)
481
500
        if self.autoscale_mode:
482
501
            self.do_autoscale(replot=True)
 
502
        if self.apply_lut:
 
503
            self.set_axis_title(self.Z_AXIS, LUT_AXIS_TITLE)
 
504
            self.set_axis_color(self.Z_AXIS, "red")
 
505
        else:
 
506
            self.plot_labels_changed(obj.plot())
483
507
                
484
508
    def export(self):
485
509
        """Export cross-section plot in a text file"""
495
519
            QMessageBox.warning(self, _("Export"),
496
520
                                _("Please select a cross section plot."))
497
521
            return
498
 
        x, y = items[0].get_data()
499
 
        data = np.array([x, y]).T
 
522
        item_data = items[0].get_data()
 
523
        if len(item_data) > 2:
 
524
            x, y, dx, dy = item_data
 
525
            array_list = [x, y]
 
526
            if dx is not None:
 
527
                array_list.append(dx)
 
528
            if dy is not None:
 
529
                array_list.append(dy)
 
530
            data = np.array(array_list).T
 
531
        else:
 
532
            x, y = item_data
 
533
            data = np.array([x, y]).T
500
534
        fname = QFileDialog.getSaveFileName(self, _("Export"),
501
535
                                            "", _("Text file")+" (*.txt)")
502
536
        if fname:
516
550
        self.autoscale_mode = state
517
551
        self.update_plot()
518
552
        
 
553
    def toggle_autorefresh(self, state):
 
554
        self.autorefresh_mode = state
 
555
        self.update_plot()
 
556
        
519
557
    def toggle_apply_lut(self, state):
520
558
        self.apply_lut = state
521
559
        self.update_plot()
525
563
            self.update_plot()
526
564
 
527
565
 
528
 
class XCrossSectionPlot(CrossSectionPlot):
529
 
    """X-axis cross section plot"""
530
 
    _height = 130
531
 
    CS_AXIS = CurvePlot.xBottom
532
 
    Z_AXIS = CurvePlot.yLeft
 
566
class HorizontalCrossSectionPlot(CrossSectionPlot):
 
567
    CS_AXIS = CurvePlot.X_BOTTOM
 
568
    Z_AXIS = CurvePlot.Y_LEFT
533
569
    CURVETYPE = "Yfx"
534
 
    def sizeHint(self):
535
 
        return QSize(self.width(), self._height)
536
 
        
537
 
    def create_cross_section_item(self):
538
 
        return XCrossSectionItem(self.curveparam)
539
 
 
540
570
    def plot_labels_changed(self, plot):
541
571
        """Plot labels have changed"""
542
572
        self.set_axis_title("left", plot.get_axis_title("right"))       
543
573
        self.set_axis_title("bottom", plot.get_axis_title("bottom"))
 
574
        self.set_axis_color("left", plot.get_axis_color("right"))       
 
575
        self.set_axis_color("bottom", plot.get_axis_color("bottom"))
544
576
        
545
577
    def axis_dir_changed(self, plot, axis_id):
546
578
        """An axis direction has changed"""
547
 
        if axis_id == plot.xBottom:
 
579
        if axis_id == plot.X_BOTTOM:
548
580
            self.set_axis_direction("bottom", plot.get_axis_direction("bottom"))
549
581
            self.replot()
550
 
        
551
582
 
552
 
class YCrossSectionPlot(CrossSectionPlot):
553
 
    """Y-axis cross section plot"""
554
 
    _width = 140
555
 
    CS_AXIS = CurvePlot.yLeft
556
 
    Z_AXIS = CurvePlot.xBottom
 
583
class VerticalCrossSectionPlot(CrossSectionPlot):
 
584
    CS_AXIS = CurvePlot.Y_LEFT
 
585
    Z_AXIS = CurvePlot.X_BOTTOM
557
586
    Z_MAX_MAJOR = 3
558
587
    CURVETYPE = "Xfy"
559
 
    def sizeHint(self):
560
 
        return QSize(self._width, self.height())
561
 
    
562
 
    def create_cross_section_item(self):
563
 
        return YCrossSectionItem(self.curveparam)
564
 
 
565
588
    def plot_labels_changed(self, plot):
566
589
        """Plot labels have changed"""
567
590
        self.set_axis_title("bottom", plot.get_axis_title("right"))       
568
591
        self.set_axis_title("left", plot.get_axis_title("left"))
 
592
        self.set_axis_color("bottom", plot.get_axis_color("right"))       
 
593
        self.set_axis_color("left", plot.get_axis_color("left"))
569
594
        
570
595
    def axis_dir_changed(self, plot, axis_id):
571
596
        """An axis direction has changed"""
572
 
        if axis_id == plot.yLeft:
 
597
        if axis_id == plot.Y_LEFT:
573
598
            self.set_axis_direction("left", plot.get_axis_direction("left"))
574
599
            self.replot()
575
600
 
576
601
 
 
602
class XCrossSectionPlot(HorizontalCrossSectionPlot):
 
603
    """X-axis cross section plot"""
 
604
    _height = 130
 
605
    def sizeHint(self):
 
606
        return QSize(self.width(), self._height)
 
607
        
 
608
    def create_cross_section_item(self):
 
609
        return XCrossSectionItem(self.curveparam)
 
610
        
 
611
class YCrossSectionPlot(VerticalCrossSectionPlot):
 
612
    """Y-axis cross section plot"""
 
613
    _width = 140
 
614
    def sizeHint(self):
 
615
        return QSize(self._width, self.height())
 
616
    
 
617
    def create_cross_section_item(self):
 
618
        return YCrossSectionItem(self.curveparam)
 
619
 
 
620
 
577
621
class CrossSectionWidget(PanelWidget):
578
 
    """Cross section widget"""
 
622
    PANEL_ID = None
 
623
    PANEL_TITLE = _("Cross section tool")
 
624
    PANEL_ICON = "csection.png"
579
625
    CrossSectionPlotKlass = None
580
 
    OTHER_PANEL_ID = None
581
 
    
 
626
        
582
627
    __implements__ = (IPanel,)
583
628
 
584
629
    def __init__(self, parent=None):
585
630
        super(CrossSectionWidget, self).__init__(parent)
586
631
        
587
 
        widget_title = _("Cross section tool")
588
 
        widget_icon = "csection.png"
 
632
        self.export_ac = None
 
633
        self.autoscale_ac = None
 
634
        self.refresh_ac = None
 
635
        self.autorefresh_ac = None
589
636
        
590
637
        self.manager = None # manager for the associated image plot
591
638
        
592
639
        self.local_manager = PlotManager(self)
593
640
        self.cs_plot = self.CrossSectionPlotKlass(parent)
594
 
        
595
 
        self.spacer1 = QSpacerItem(0, 0)
596
 
        self.spacer2 = QSpacerItem(0, 0)
597
 
        
598
 
        self.toolbar = toolbar = QToolBar(self)
599
 
        if self.CrossSectionPlotKlass is YCrossSectionPlot:
600
 
            toolbar.setOrientation(Qt.Horizontal)
601
 
            layout = QVBoxLayout()
602
 
            layout.addSpacerItem(self.spacer1)
603
 
            layout.addWidget(toolbar)
604
 
            layout.addWidget(self.cs_plot)
605
 
            layout.addSpacerItem(self.spacer2)
606
 
        else:
607
 
            toolbar.setOrientation(Qt.Vertical)
608
 
            layout = QHBoxLayout()
609
 
            layout.addWidget(self.cs_plot)
610
 
            layout.addWidget(toolbar)
611
 
        layout.setContentsMargins(0, 0, 0, 0)
612
 
        self.setLayout(layout)
613
 
        
614
 
        self.local_manager.add_plot(self.cs_plot)
615
 
        
616
 
        self.cs_plot.standard_tools(self.local_manager)
617
 
        self.setWindowIcon(get_icon(widget_icon))
618
 
        self.setWindowTitle(widget_title)
619
 
        
620
 
    def set_options(self, peritem=None, applylut=None, autoscale=None):
 
641
        self.connect(self.cs_plot, SIG_CS_CURVE_CHANGED,
 
642
                     self.cs_curve_has_changed)
 
643
        
 
644
        # Configure the local manager
 
645
        lman = self.local_manager
 
646
        lman.add_plot(self.cs_plot)
 
647
        lman.add_tool(SelectTool)
 
648
        lman.add_tool(BasePlotMenuTool, "item")
 
649
        lman.add_tool(BasePlotMenuTool, "axes")
 
650
        lman.add_tool(BasePlotMenuTool, "grid")
 
651
        lman.add_tool(AntiAliasingTool)
 
652
        lman.add_tool(DeleteItemTool)
 
653
        lman.get_default_tool().activate()
 
654
        
 
655
        self.toolbar = QToolBar(self)
 
656
        self.toolbar.setOrientation(Qt.Vertical)
 
657
        
 
658
        self.setup_widget()
 
659
        
 
660
    def set_options(self, autoscale=None, autorefresh=None):
621
661
        assert self.manager is not None, "Panel '%s' must be registered to plot manager before changing options" % self.PANEL_ID
622
 
        if peritem is not None:
623
 
            self.peritem_ac.setChecked(peritem)
624
 
        if applylut is not None:
625
 
            self.applylut_ac.setChecked(applylut)
626
662
        if autoscale is not None:
627
663
            self.autoscale_ac.setChecked(autoscale)
628
 
    
 
664
        if autorefresh is not None:
 
665
            self.autorefresh_ac.setChecked(autorefresh)
 
666
        
 
667
    def setup_widget(self):
 
668
        layout = QHBoxLayout()
 
669
        layout.addWidget(self.cs_plot)
 
670
        layout.addWidget(self.toolbar)
 
671
        layout.setContentsMargins(0, 0, 0, 0)
 
672
        self.setLayout(layout)
 
673
        
 
674
    def cs_curve_has_changed(self, curve):
 
675
        """Cross section curve has just changed"""
 
676
        # Do something with curve's data for example
 
677
        pass
 
678
        
629
679
    def register_panel(self, manager):
630
680
        """Register panel to plot manager"""
631
681
        self.manager = manager
632
682
        for plot in manager.get_plots():
633
683
            self.cs_plot.connect_plot(plot)
634
684
        self.setup_actions()
635
 
        other = manager.get_panel(self.OTHER_PANEL_ID)
 
685
        self.add_actions_to_toolbar()
 
686
                         
 
687
    def configure_panel(self):
 
688
        """Configure panel"""
 
689
        pass
 
690
 
 
691
    def get_plot(self):
 
692
        return self.manager.get_active_plot()
 
693
        
 
694
    def setup_actions(self):
 
695
        self.export_ac = create_action(self, _("Export"),
 
696
                                   icon=get_std_icon("DialogSaveButton", 16),
 
697
                                   triggered=self.cs_plot.export,
 
698
                                   tip=_("Export cross section data"))
 
699
        self.autoscale_ac = create_action(self, _("Auto-scale"),
 
700
                                   icon=get_icon('csautoscale.png'),
 
701
                                   toggled=self.cs_plot.toggle_autoscale)
 
702
        self.autoscale_ac.setChecked(self.cs_plot.autoscale_mode)
 
703
        self.refresh_ac = create_action(self, _("Refresh"),
 
704
                                   icon=get_icon('refresh.png'),
 
705
                                   triggered=lambda: self.cs_plot.update_plot())
 
706
        self.autorefresh_ac = create_action(self, _("Auto-refresh"),
 
707
                                   icon=get_icon('autorefresh.png'),
 
708
                                   toggled=self.cs_plot.toggle_autorefresh)
 
709
        self.autorefresh_ac.setChecked(self.cs_plot.autorefresh_mode)
 
710
        
 
711
    def add_actions_to_toolbar(self):
 
712
        add_actions(self.toolbar, (self.export_ac, self.autoscale_ac, None,
 
713
                                   self.refresh_ac, self.autorefresh_ac))
 
714
        
 
715
    def register_shape(self, shape, final):
 
716
        plot = self.get_plot()
 
717
        self.cs_plot.register_shape(plot, shape, final)
 
718
        
 
719
    def unregister_shape(self, shape):
 
720
        self.cs_plot.unregister_shape(shape)
 
721
        
 
722
    def update_plot(self, obj=None):
 
723
        """
 
724
        Update cross section curve(s) associated to object *obj*
 
725
        
 
726
        *obj* may be a marker or a rectangular shape
 
727
        (see :py:class:`guiqwt.tools.CrossSectionTool` 
 
728
        and :py:class:`guiqwt.tools.AverageCrossSectionTool`)
 
729
        
 
730
        If obj is None, update the cross sections of the last active object
 
731
        """
 
732
        self.cs_plot.update_plot(obj)
 
733
 
 
734
assert_interfaces_valid(CrossSectionWidget)
 
735
        
 
736
 
 
737
class XCrossSection(CrossSectionWidget):
 
738
    """X-axis cross section widget"""
 
739
    PANEL_ID = ID_XCS
 
740
    OTHER_PANEL_ID = ID_YCS
 
741
    CrossSectionPlotKlass = XCrossSectionPlot
 
742
    def __init__(self, parent=None):
 
743
        super(XCrossSection, self).__init__(parent)
 
744
        self.peritem_ac = None
 
745
        self.applylut_ac = None
 
746
        
 
747
    def set_options(self, autoscale=None, autorefresh=None,
 
748
                    peritem=None, applylut=None):
 
749
        assert self.manager is not None, "Panel '%s' must be registered to plot manager before changing options" % self.PANEL_ID
 
750
        if autoscale is not None:
 
751
            self.autoscale_ac.setChecked(autoscale)
 
752
        if autorefresh is not None:
 
753
            self.autorefresh_ac.setChecked(autorefresh)
 
754
        if peritem is not None:
 
755
            self.peritem_ac.setChecked(peritem)
 
756
        if applylut is not None:
 
757
            self.applylut_ac.setChecked(applylut)
 
758
            
 
759
    def add_actions_to_toolbar(self):
 
760
        other = self.manager.get_panel(self.OTHER_PANEL_ID)
636
761
        if other is None:
637
762
            add_actions(self.toolbar,
638
763
                        (self.peritem_ac, self.applylut_ac, None,
639
 
                         self.export_ac, self.autoscale_ac, self.refresh_ac))
 
764
                         self.export_ac, self.autoscale_ac,
 
765
                         self.refresh_ac, self.autorefresh_ac))
640
766
        else:
641
767
            add_actions(self.toolbar,
642
768
                        (other.peritem_ac, other.applylut_ac, None,
643
 
                         self.export_ac, other.autoscale_ac, other.refresh_ac))
 
769
                         self.export_ac, other.autoscale_ac,
 
770
                         other.refresh_ac, other.autorefresh_ac))
644
771
            self.connect(other.peritem_ac, SIGNAL("toggled(bool)"),
645
772
                         self.cs_plot.toggle_perimage_mode)
646
773
            self.connect(other.applylut_ac, SIGNAL("toggled(bool)"),
648
775
            self.connect(other.autoscale_ac, SIGNAL("toggled(bool)"),
649
776
                         self.cs_plot.toggle_autoscale)
650
777
            self.connect(other.refresh_ac, SIGNAL("triggered()"),
651
 
                         self.cs_plot.update_plot)
652
 
 
653
 
    def get_plot(self):
654
 
        return self.manager.get_active_plot()
 
778
                         lambda: self.cs_plot.update_plot())
 
779
            self.connect(other.autorefresh_ac, SIGNAL("toggled(bool)"),
 
780
                         self.cs_plot.toggle_autorefresh)
655
781
        
656
782
    def closeEvent(self, event):
657
783
        self.hide()
658
784
        event.ignore()
659
785
        
660
786
    def setup_actions(self):
 
787
        super(XCrossSection, self).setup_actions()
661
788
        self.peritem_ac = create_action(self, _("Per image cross-section"),
662
789
                        icon=get_icon('csperimage.png'),
663
790
                        toggled=self.cs_plot.toggle_perimage_mode,
674
801
                              "This is the easiest way to compare images "
675
802
                              "which have slightly different level ranges.\n\n"
676
803
                              "Note: LUT is coded over 1024 levels (0...1023)"))
677
 
        self.export_ac = create_action(self, _("Export"),
678
 
                                   icon=get_std_icon("DialogSaveButton", 16),
679
 
                                   triggered=self.cs_plot.export,
680
 
                                   tip=_("Export cross section data"))
681
 
        self.autoscale_ac = create_action(self, _("Auto-scale"),
682
 
                                   icon=get_icon('csautoscale.png'),
683
 
                                   toggled=self.cs_plot.toggle_autoscale)
684
 
        self.refresh_ac = create_action(self, _("Refresh"),
685
 
                                   icon=get_icon('refresh.png'),
686
 
                                   triggered=self.cs_plot.update_plot)
687
 
 
688
804
        self.peritem_ac.setChecked(True)
689
 
        self.autoscale_ac.setChecked(True)
690
805
        self.applylut_ac.setChecked(False)
691
 
        
692
 
    def register_shape(self, shape, final):
693
 
        plot = self.get_plot()
694
 
        self.cs_plot.register_shape(plot, shape, final)
695
 
        
696
 
    def update_plot(self, obj=None):
697
 
        """
698
 
        Update cross section curve(s) associated to object *obj*
699
 
        
700
 
        *obj* may be a marker or a rectangular shape
701
 
        (see :py:class:`guiqwt.tools.CrossSectionTool` 
702
 
        and :py:class:`guiqwt.tools.AverageCrossSectionTool`)
703
 
        
704
 
        If obj is None, update the cross sections of the last active object
705
 
        """
706
 
        self.cs_plot.update_plot(obj)
707
 
 
708
 
assert_interfaces_valid(CrossSectionWidget)
709
 
 
710
 
 
711
 
class XCrossSection(CrossSectionWidget):
712
 
    """X-axis cross section widget"""
713
 
    PANEL_ID = ID_XCS
714
 
    OTHER_PANEL_ID = ID_YCS
715
 
    CrossSectionPlotKlass = XCrossSectionPlot
716
 
 
717
 
class YCrossSection(CrossSectionWidget):
 
806
 
 
807
 
 
808
class YCrossSection(XCrossSection):
718
809
    """
719
810
    Y-axis cross section widget
720
811
    parent (QWidget): parent widget
723
814
    PANEL_ID = ID_YCS
724
815
    OTHER_PANEL_ID = ID_XCS
725
816
    CrossSectionPlotKlass = YCrossSectionPlot
726
 
    def __init__(self, parent=None, position="right"):
727
 
        CrossSectionWidget.__init__(self, parent)
 
817
    def __init__(self, parent=None, position="right", xsection_pos="top"):
 
818
        self.xsection_pos = xsection_pos
 
819
        self.spacer = QSpacerItem(0, 0)
 
820
        super(YCrossSection, self).__init__(parent)
728
821
        self.cs_plot.set_axis_direction("bottom", reverse=position == "left")
 
822
        
 
823
    def setup_widget(self):
 
824
        toolbar = self.toolbar
 
825
        toolbar.setOrientation(Qt.Horizontal)
 
826
        layout = QVBoxLayout()
 
827
        if self.xsection_pos == "top":
 
828
            layout.addSpacerItem(self.spacer)
 
829
        layout.addWidget(toolbar)
 
830
        layout.addWidget(self.cs_plot)
 
831
        if self.xsection_pos == "bottom":
 
832
            layout.addSpacerItem(self.spacer)
 
833
        layout.setContentsMargins(0, 0, 0, 0)
 
834
        self.setLayout(layout)
 
835
        
 
836
    def adjust_height(self, height):
 
837
        self.spacer.changeSize(0, height, QSizePolicy.Fixed, QSizePolicy.Fixed)
 
838
        self.layout().invalidate()