source: sasview/src/sas/qtgui/Plotting/MaskEditor.py @ d32a594

Last change on this file since d32a594 was 33c0561, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Replace Apply button menu driven functionality with additional button.
Removed Cancel.
Removed the window system context help button from all affected widgets.
SASVIEW-1239

  • Property mode set to 100644
File size: 7.9 KB
Line 
1from functools import partial
2import copy
3import numpy as np
4
5from PyQt5 import QtWidgets, QtCore
6
7from sas.qtgui.Plotting.PlotterData import Data2D
8
9# Local UI
10from sas.qtgui.UI import main_resources_rc
11from sas.qtgui.Plotting.UI.MaskEditorUI import Ui_MaskEditorUI
12from sas.qtgui.Plotting.Plotter2D import Plotter2DWidget
13
14from sas.qtgui.Plotting.Masks.SectorMask import SectorMask
15from sas.qtgui.Plotting.Masks.BoxMask import BoxMask
16from sas.qtgui.Plotting.Masks.CircularMask import CircularMask
17
18
19class MaskEditor(QtWidgets.QDialog, Ui_MaskEditorUI):
20    def __init__(self, parent=None, data=None):
21        super(MaskEditor, self).__init__()
22
23        assert isinstance(data, Data2D)
24
25        self.setupUi(self)
26        # disable the context help icon
27        self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowContextHelpButtonHint)
28
29        self.data = data
30        self.parent = parent
31        filename = data.name
32
33        self.current_slicer = None
34        self.slicer_mask = None
35
36        self.setWindowTitle("Mask Editor for %s" % filename)
37
38        self.plotter = Plotter2DWidget(self, manager=parent, quickplot=True)
39        self.plotter.data = self.data
40        self.slicer_z = 0
41        self.default_mask = copy.deepcopy(data.mask)
42
43        layout = QtWidgets.QHBoxLayout()
44        layout.setContentsMargins(0, 0, 0, 0)
45        self.frame.setLayout(layout)
46
47        self.plotter.plot()
48        layout.addWidget(self.plotter)
49        self.subplot = self.plotter.ax
50
51        # update mask
52        self.updateMask(self.default_mask)
53
54        self.initializeSignals()
55
56    def initializeSignals(self):
57        """
58        Attach slots to signals from radio boxes
59        """
60        self.rbWings.toggled.connect(partial(self.onMask, slicer=SectorMask, inside=True))
61        self.rbCircularDisk.toggled.connect(partial(self.onMask, slicer=CircularMask, inside=True))
62        self.rbRectangularDisk.toggled.connect(partial(self.onMask, slicer=BoxMask, inside=True))
63        self.rbDoubleWingWindow.toggled.connect(partial(self.onMask, slicer=SectorMask, inside=False))
64        self.rbCircularWindow.toggled.connect(partial(self.onMask, slicer=CircularMask, inside=False))
65        self.rbRectangularWindow.toggled.connect(partial(self.onMask, slicer=BoxMask, inside=False))
66
67        # Button groups defined so we can uncheck all buttons programmatically
68        self.buttonGroup = QtWidgets.QButtonGroup()
69        self.buttonGroup.addButton(self.rbWings)
70        self.buttonGroup.addButton(self.rbCircularDisk)
71        self.buttonGroup.addButton(self.rbRectangularDisk)
72        self.buttonGroup.addButton(self.rbDoubleWingWindow)
73        self.buttonGroup.addButton(self.rbCircularWindow)
74        self.buttonGroup.addButton(self.rbRectangularWindow)
75
76        # Push buttons
77        self.cmdAdd.clicked.connect(self.onAdd)
78        self.cmdReset.clicked.connect(self.onReset)
79        self.cmdClear.clicked.connect(self.onClear)
80
81    def emptyRadioButtons(self):
82        """
83        Uncheck all buttons without them firing signals causing unnecessary slicer updates
84        """
85        self.buttonGroup.setExclusive(False)
86        self.rbWings.blockSignals(True)
87        self.rbWings.setChecked(False)
88        self.rbWings.blockSignals(False)
89
90        self.rbCircularDisk.blockSignals(True)
91        self.rbCircularDisk.setChecked(False)
92        self.rbCircularDisk.blockSignals(False)
93
94        self.rbRectangularDisk.blockSignals(True)
95        self.rbRectangularDisk.setChecked(False)
96        self.rbRectangularDisk.blockSignals(False)
97
98        self.rbDoubleWingWindow.blockSignals(True)
99        self.rbDoubleWingWindow.setChecked(False)
100        self.rbDoubleWingWindow.blockSignals(False)
101
102        self.rbCircularWindow.blockSignals(True)
103        self.rbCircularWindow.setChecked(False)
104        self.rbCircularWindow.blockSignals(False)
105
106        self.rbRectangularWindow.blockSignals(True)
107        self.rbRectangularWindow.setChecked(False)
108        self.rbRectangularWindow.blockSignals(False)
109        self.buttonGroup.setExclusive(True)
110
111    def setSlicer(self, slicer):
112        """
113        Clear the previous slicer and create a new one.
114        slicer: slicer class to create
115        """
116        # Clear current slicer
117        if self.current_slicer is not None:
118            self.current_slicer.clear()
119        # Create a new slicer
120        self.slicer_z += 1
121        self.current_slicer = slicer(self, self.ax, zorder=self.slicer_z)
122        self.ax.set_ylim(self.data.ymin, self.data.ymax)
123        self.ax.set_xlim(self.data.xmin, self.data.xmax)
124        # Draw slicer
125        self.figure.canvas.draw()
126        self.current_slicer.update()
127
128    def onMask(self, slicer=None, inside=True):
129        """
130        Clear the previous mask and create a new one.
131        """
132        self.clearSlicer()
133        # modifying data in-place
134        self.slicer_z += 1
135
136        self.current_slicer = slicer(self.plotter, self.plotter.ax, zorder=self.slicer_z, side=inside)
137
138        self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax)
139        self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax)
140
141        self.plotter.canvas.draw()
142
143        self.slicer_mask = self.current_slicer.update()
144
145    def update(self):
146        """
147        Redraw the canvas
148        """
149        self.plotter.draw()
150
151    def onAdd(self):
152        """
153        Generate required mask and modify underlying DATA
154        """
155        if self.current_slicer is None:
156            return
157        data = Data2D()
158        data = self.data
159        self.slicer_mask = self.current_slicer.update()
160        data.mask = self.data.mask & self.slicer_mask
161        self.updateMask(data.mask)
162        self.emptyRadioButtons()
163
164    def onClear(self):
165        """
166        Remove the current mask(s)
167        """
168        self.slicer_z += 1
169        self.clearSlicer()
170        self.current_slicer = BoxMask(self.plotter, self.plotter.ax,
171                                      zorder=self.slicer_z, side=True)
172        self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax)
173        self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax)
174
175        self.data.mask = copy.deepcopy(self.default_mask)
176        # update mask plot
177        self.updateMask(self.data.mask)
178        self.emptyRadioButtons()
179
180    def onReset(self):
181        """
182        Removes all the masks from data
183        """
184        self.slicer_z += 1
185        self.clearSlicer()
186        self.current_slicer = BoxMask(self.plotter, self.plotter.ax,
187                                      zorder=self.slicer_z, side=True)
188        self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax)
189        self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax)
190        mask = np.ones(len(self.data.mask), dtype=bool)
191        self.data.mask = mask
192        # update mask plot
193        self.updateMask(mask)
194        self.emptyRadioButtons()
195
196    def clearSlicer(self):
197        """
198        Clear the slicer on the plot
199        """
200        if self.current_slicer is None:
201            return
202
203        self.current_slicer.clear()
204        self.plotter.draw()
205        self.current_slicer = None
206
207    def updateMask(self, mask):
208        """
209        Respond to changes in masking
210        """
211        # the case of litle numbers of True points
212        if len(mask[mask]) < 10 and self.data is not None:
213            self.data.mask = copy.deepcopy(self.default_mask)
214        else:
215            self.default_mask = mask
216        # make temperary data to plot
217        temp_mask = np.zeros(len(mask))
218        temp_data = copy.deepcopy(self.data)
219        # temp_data default is None
220        # This method is to distinguish between masked point and data point = 0.
221        temp_mask = temp_mask / temp_mask
222        temp_mask[mask] = temp_data.data[mask]
223
224        temp_data.data[mask == False] = temp_mask[mask == False]
225
226        if self.current_slicer is not None:
227            self.current_slicer.clear()
228            self.current_slicer = None
229
230        # modify imshow data
231        self.plotter.plot(data=temp_data, update=True)
232        self.plotter.draw()
233
234        self.subplot = self.plotter.ax
Note: See TracBrowser for help on using the repository browser.