- Timestamp:
- Jun 27, 2018 5:33:52 AM (7 years ago)
- Branches:
- ESS_GUI, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- b1a7a81
- Parents:
- c5e0d84
- git-author:
- Piotr Rozyczko <rozyczko@…> (05/31/18 07:15:47)
- git-committer:
- Piotr Rozyczko <rozyczko@…> (06/27/18 05:33:52)
- Location:
- src/sas/qtgui
- Files:
-
- 5 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/MainWindow/DataExplorer.py
raed0532 re20870bc 91 91 self.communicator.activeGraphName.connect(self.updatePlotName) 92 92 self.communicator.plotUpdateSignal.connect(self.updatePlot) 93 self.communicator.maskEditorSignal.connect(self.showEditDataMask) 93 94 94 95 self.cbgraph.editTextChanged.connect(self.enableGraphCombo) … … 990 991 new_plot.show() 991 992 992 def showEditDataMask(self ):993 def showEditDataMask(self, data=None): 993 994 """ 994 995 Mask Editor for 2D plots 995 996 """ 996 index = self.current_view.selectedIndexes()[0] 997 proxy = self.current_view.model() 998 model = proxy.sourceModel() 999 model_item = model.itemFromIndex(proxy.mapToSource(index)) 1000 1001 data = GuiUtils.dataFromItem(model_item) 997 if data is None or not isinstance(data, Data2D): 998 index = self.current_view.selectedIndexes()[0] 999 proxy = self.current_view.model() 1000 model = proxy.sourceModel() 1001 model_item = model.itemFromIndex(proxy.mapToSource(index)) 1002 1003 data = GuiUtils.dataFromItem(model_item) 1002 1004 1003 1005 mask_editor = MaskEditor(self, data) -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r9a7c81c re20870bc 384 384 self.chk2DView.setVisible(False) 385 385 self.chkMagnetism.setEnabled(self.is2D) 386 self.tabFitting.setTabEnabled(TAB_MAGNETISM, self. is2D)386 self.tabFitting.setTabEnabled(TAB_MAGNETISM, self.chkMagnetism.isChecked()) 387 387 # Combo box or label for file name" 388 388 if self.is_batch_fitting: … … 510 510 511 511 # Signals from separate tabs asking for replot 512 self.options_widget.plot_signal.connect(self.onOptionsUpdate) 512 513 self.options_widget.plot_signal.connect(self.onOptionsUpdate) 513 514 -
src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py
r9a7c81c re20870bc 127 127 Callback for running the mask editor 128 128 """ 129 self.parent.communicate.maskEditorSignal.emit(self.logic.data) 129 130 pass 130 131 -
src/sas/qtgui/Plotting/MaskEditor.py
r4992ff2 re20870bc 1 from PyQt5 import QtCore 2 from PyQt5 import QtGui 1 from functools import partial 2 import copy 3 import numpy as np 4 3 5 from PyQt5 import QtWidgets 4 6 … … 10 12 from sas.qtgui.Plotting.Plotter2D import Plotter2DWidget 11 13 14 from sas.qtgui.Plotting.Masks.SectorMask import SectorMask 15 from sas.qtgui.Plotting.Masks.BoxMask import BoxMask 16 from sas.qtgui.Plotting.Masks.CircularMask import CircularMask 17 18 12 19 class MaskEditor(QtWidgets.QDialog, Ui_MaskEditorUI): 13 20 def __init__(self, parent=None, data=None): 14 21 super(MaskEditor, self).__init__() 15 22 16 assert (isinstance(data, Data2D))23 assert isinstance(data, Data2D) 17 24 18 25 self.setupUi(self) 19 26 20 27 self.data = data 28 self.parent = parent 21 29 filename = data.name 30 31 self.current_slicer = None 32 self.slicer_mask = None 33 22 34 self.setWindowTitle("Mask Editor for %s" % filename) 23 35 24 36 self.plotter = Plotter2DWidget(self, manager=parent, quickplot=True) 25 37 self.plotter.data = self.data 38 self.slicer_z = 0 39 self.default_mask = copy.deepcopy(data.mask) 26 40 27 41 layout = QtWidgets.QHBoxLayout() … … 31 45 32 46 self.plotter.plot() 33 47 self.subplot = self.plotter.ax 48 49 # update mask 50 self.updateMask(self.default_mask) 51 52 self.initializeSignals() 53 54 def initializeSignals(self): 55 """ 56 Attach slots to signals from radio boxes 57 """ 58 self.rbWings.toggled.connect(partial(self.onMask, slicer=SectorMask, inside=True)) 59 self.rbCircularDisk.toggled.connect(partial(self.onMask, slicer=CircularMask, inside=True)) 60 self.rbRectangularDisk.toggled.connect(partial(self.onMask, slicer=BoxMask, inside=True)) 61 self.rbDoubleWingWindow.toggled.connect(partial(self.onMask, slicer=SectorMask, inside=False)) 62 self.rbCircularWindow.toggled.connect(partial(self.onMask, slicer=CircularMask, inside=False)) 63 self.rbRectangularWindow.toggled.connect(partial(self.onMask, slicer=BoxMask, inside=False)) 64 65 # Button groups defined so we can uncheck all buttons programmatically 66 self.buttonGroup = QtWidgets.QButtonGroup() 67 self.buttonGroup.addButton(self.rbWings) 68 self.buttonGroup.addButton(self.rbCircularDisk) 69 self.buttonGroup.addButton(self.rbRectangularDisk) 70 self.buttonGroup.addButton(self.rbDoubleWingWindow) 71 self.buttonGroup.addButton(self.rbCircularWindow) 72 self.buttonGroup.addButton(self.rbRectangularWindow) 73 74 # Push buttons 75 self.cmdAdd.clicked.connect(self.onAdd) 76 self.cmdReset.clicked.connect(self.onReset) 77 self.cmdClear.clicked.connect(self.onClear) 78 79 def emptyRadioButtons(self): 80 """ 81 Uncheck all buttons without them firing signals causing unnecessary slicer updates 82 """ 83 self.buttonGroup.setExclusive(False) 84 self.rbWings.blockSignals(True) 85 self.rbWings.setChecked(False) 86 self.rbWings.blockSignals(False) 87 88 self.rbCircularDisk.blockSignals(True) 89 self.rbCircularDisk.setChecked(False) 90 self.rbCircularDisk.blockSignals(False) 91 92 self.rbRectangularDisk.blockSignals(True) 93 self.rbRectangularDisk.setChecked(False) 94 self.rbRectangularDisk.blockSignals(False) 95 96 self.rbDoubleWingWindow.blockSignals(True) 97 self.rbDoubleWingWindow.setChecked(False) 98 self.rbDoubleWingWindow.blockSignals(False) 99 100 self.rbCircularWindow.blockSignals(True) 101 self.rbCircularWindow.setChecked(False) 102 self.rbCircularWindow.blockSignals(False) 103 104 self.rbRectangularWindow.blockSignals(True) 105 self.rbRectangularWindow.setChecked(False) 106 self.rbRectangularWindow.blockSignals(False) 107 self.buttonGroup.setExclusive(True) 108 109 def setSlicer(self, slicer): 110 """ 111 Clear the previous slicer and create a new one. 112 slicer: slicer class to create 113 """ 114 # Clear current slicer 115 if self.current_slicer is not None: 116 self.current_slicer.clear() 117 # Create a new slicer 118 self.slicer_z += 1 119 self.current_slicer = slicer(self, self.ax, zorder=self.slicer_z) 120 self.ax.set_ylim(self.data.ymin, self.data.ymax) 121 self.ax.set_xlim(self.data.xmin, self.data.xmax) 122 # Draw slicer 123 self.figure.canvas.draw() 124 self.current_slicer.update() 125 126 def onMask(self, slicer=None, inside=True): 127 """ 128 Clear the previous mask and create a new one. 129 """ 130 self.clearSlicer() 131 # modifying data in-place 132 self.slicer_z += 1 133 134 self.current_slicer = slicer(self.plotter, self.plotter.ax, zorder=self.slicer_z, side=inside) 135 136 self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax) 137 self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax) 138 139 self.plotter.canvas.draw() 140 141 self.slicer_mask = self.current_slicer.update() 142 143 def update(self): 144 """ 145 Redraw the canvas 146 """ 147 self.plotter.draw() 148 149 def onAdd(self): 150 """ 151 Generate required mask and modify underlying DATA 152 """ 153 if self.current_slicer is None: 154 return 155 data = Data2D() 156 data = self.data 157 self.slicer_mask = self.current_slicer.update() 158 data.mask = self.data.mask & self.slicer_mask 159 self.updateMask(data.mask) 160 self.emptyRadioButtons() 161 162 def onClear(self): 163 """ 164 Remove the current mask(s) 165 """ 166 self.slicer_z += 1 167 self.clearSlicer() 168 self.current_slicer = BoxMask(self.plotter, self.plotter.ax, 169 zorder=self.slicer_z, side=True) 170 self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax) 171 self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax) 172 173 self.data.mask = copy.deepcopy(self.default_mask) 174 # update mask plot 175 self.updateMask(self.data.mask) 176 self.emptyRadioButtons() 177 178 def onReset(self): 179 """ 180 Removes all the masks from data 181 """ 182 self.slicer_z += 1 183 self.clearSlicer() 184 self.current_slicer = BoxMask(self.plotter, self.plotter.ax, 185 zorder=self.slicer_z, side=True) 186 self.plotter.ax.set_ylim(self.data.ymin, self.data.ymax) 187 self.plotter.ax.set_xlim(self.data.xmin, self.data.xmax) 188 mask = np.ones(len(self.data.mask), dtype=bool) 189 self.data.mask = mask 190 # update mask plot 191 self.updateMask(mask) 192 self.emptyRadioButtons() 193 194 def clearSlicer(self): 195 """ 196 Clear the slicer on the plot 197 """ 198 if self.current_slicer is None: 199 return 200 201 self.current_slicer.clear() 202 self.plotter.draw() 203 self.current_slicer = None 204 205 def updateMask(self, mask): 206 """ 207 Respond to changes in masking 208 """ 209 # the case of liitle numbers of True points 210 if len(mask[mask]) < 10 and self.data is not None: 211 self.data.mask = copy.deepcopy(self.mask) 212 else: 213 self.mask = mask 214 # make temperary data to plot 215 temp_mask = np.zeros(len(mask)) 216 temp_data = copy.deepcopy(self.data) 217 # temp_data default is None 218 # This method is to distinguish between masked point and data point = 0. 219 temp_mask = temp_mask / temp_mask 220 temp_mask[mask] = temp_data.data[mask] 221 222 temp_data.data[mask == False] = temp_mask[mask == False] 223 224 if self.current_slicer is not None: 225 self.current_slicer.clear() 226 self.current_slicer = None 227 228 # modify imshow data 229 self.plotter.plot(data=temp_data) 230 self.plotter.draw() 231 232 self.subplot = self.plotter.ax -
src/sas/qtgui/Plotting/Plotter2D.py
rd6b8a1d re20870bc 61 61 self.vmin = None 62 62 self.vmax = None 63 self.im = None 63 64 64 65 self.manager = manager … … 439 440 zmin_temp = self.vmin 440 441 zmax_temp = self.vmax 441 442 im = self.ax.imshow(output, interpolation='nearest', 442 if self.im is not None: 443 self.im.set_data(output) 444 else: 445 self.im = self.ax.imshow(output, interpolation='nearest', 443 446 # origin='lower', 444 447 vmin=zmin_temp, vmax=zmax_temp, … … 459 462 if cbax is None: 460 463 ax.set_frame_on(False) 461 cb = self.figure.colorbar( im, shrink=0.8, aspect=20)464 cb = self.figure.colorbar(self.im, shrink=0.8, aspect=20) 462 465 else: 463 cb = self.figure.colorbar( im, cax=cbax)464 465 cb.update_bruteforce( im)466 cb = self.figure.colorbar(self.im, cax=cbax) 467 468 cb.update_bruteforce(self.im) 466 469 cb.set_label('$' + self.scale + '$') 467 470 -
src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py
r4992ff2 re20870bc 382 382 self.set_cursor(x, self._inner_mouse_y) 383 383 384 class CircularMask(BaseInteractor): 385 """ 386 Draw a ring Given a radius 387 """ 388 def __init__(self, base, axes, color='grey', zorder=3, side=None): 389 """ 390 :param: the color of the line that defined the ring 391 :param r: the radius of the ring 392 :param sign: the direction of motion the the marker 393 """ 394 BaseInteractor.__init__(self, base, axes, color=color) 395 self.markers = [] 396 self.axes = axes 397 self.base = base 398 self.is_inside = side 399 self.qmax = min(numpy.fabs(self.base.data.xmax), 400 numpy.fabs(self.base.data.xmin)) # must be positive 401 self.connect = self.base.connect 402 403 # Cursor position of Rings (Left(-1) or Right(1)) 404 self.xmaxd = self.base.data.xmax 405 self.xmind = self.base.data.xmin 406 407 if (self.xmaxd + self.xmind) > 0: 408 self.sign = 1 409 else: 410 self.sign = -1 411 # Inner circle 412 self.outer_circle = RingInteractor(self, self.axes, 'blue', 413 zorder=zorder + 1, r=self.qmax / 1.8, 414 sign=self.sign) 415 self.outer_circle.qmax = self.qmax * 1.2 416 self.update() 417 self._post_data() 418 419 def set_layer(self, n): 420 """ 421 Allow adding plot to the same panel 422 :param n: the number of layer 423 """ 424 self.layernum = n 425 self.update() 426 427 def clear(self): 428 """ 429 Clear the slicer and all connected events related to this slicer 430 """ 431 self.clear_markers() 432 self.outer_circle.clear() 433 self.base.connect.clearall() 434 435 def update(self): 436 """ 437 Respond to changes in the model by recalculating the profiles and 438 resetting the widgets. 439 """ 440 # Update locations 441 self.outer_circle.update() 442 self._post_data() 443 out = self._post_data() 444 return out 445 446 def save(self, ev): 447 """ 448 Remember the roughness for this layer and the next so that we 449 can restore on Esc. 450 """ 451 self.outer_circle.save(ev) 452 453 def _post_data(self): 454 """ 455 Uses annulus parameters to plot averaged data into 1D data. 456 457 :param nbins: the number of points to plot 458 459 """ 460 # Data to average 461 data = self.base.data 462 463 # If we have no data, just return 464 if data is None: 465 return 466 mask = data.mask 467 from sas.sascalc.dataloader.manipulations import Ringcut 468 469 rmin = 0 470 rmax = numpy.fabs(self.outer_circle.get_radius()) 471 472 # Create the data1D Q average of data2D 473 mask = Ringcut(r_min=rmin, r_max=rmax) 474 475 if self.is_inside: 476 out = (mask(data) == False) 477 else: 478 out = (mask(data)) 479 return out 480 481 482 def moveend(self, ev): 483 """ 484 Called when any dragging motion ends. 485 Post an event (type =SlicerParameterEvent) 486 to plotter 2D with a copy slicer parameters 487 Call _post_data method 488 """ 489 self.base.thaw_axes() 490 # create a 1D data plot 491 self._post_data() 492 493 def restore(self): 494 """ 495 Restore the roughness for this layer. 496 """ 497 self.outer_circle.restore() 498 499 def move(self, x, y, ev): 500 """ 501 Process move to a new position, making sure that the move is allowed. 502 """ 503 pass 504 505 def set_cursor(self, x, y): 506 pass 507 508 def getParams(self): 509 """ 510 Store a copy of values of parameters of the slicer into a dictionary. 511 512 :return params: the dictionary created 513 514 """ 515 params = {} 516 params["outer_radius"] = numpy.fabs(self.outer_circle._inner_mouse_x) 517 return params 518 519 def setParams(self, params): 520 """ 521 Receive a dictionary and reset the slicer with values contained 522 in the values of the dictionary. 523 524 :param params: a dictionary containing name of slicer parameters and 525 values the user assigned to the slicer. 526 """ 527 outer = numpy.fabs(params["outer_radius"]) 528 # Update the picture 529 self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y) 530 # Post the data given the nbins entered by the user 531 self._post_data() 532 533 def draw(self): 534 self.base.update() 535 384 -
src/sas/qtgui/Plotting/Slicers/Arc.py
- Property mode changed from 100755 to 100644
rd744767 re20870bc 2 2 Arc slicer for 2D data 3 3 """ 4 import math4 import numpy as np 5 5 6 from .BaseInteractor import BaseInteractor6 from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor 7 7 8 8 class ArcInteractor(BaseInteractor): … … 11 11 """ 12 12 def __init__(self, base, axes, color='black', zorder=5, r=1.0, 13 theta1= math.pi / 8, theta2=math.pi / 4):13 theta1=np.pi / 8, theta2=np.pi / 4): 14 14 BaseInteractor.__init__(self, base, axes, color=color) 15 15 self.markers = [] … … 55 55 Return arc radius 56 56 """ 57 radius = math.sqrt(math.pow(self._mouse_x, 2) + \58 math.pow(self._mouse_y, 2))57 radius = np.sqrt(np.power(self._mouse_x, 2) + \ 58 np.power(self._mouse_y, 2)) 59 59 return radius 60 60 … … 75 75 self.theta2 = theta2 76 76 while self.theta2 < self.theta1: 77 self.theta2 += (2 * math.pi)78 while self.theta2 >= (self.theta1 + 2 * math.pi):79 self.theta2 -= (2 * math.pi)80 npts = int((self.theta2 - self.theta1) / ( math.pi / 120))77 self.theta2 += (2 * np.pi) 78 while self.theta2 >= (self.theta1 + 2 * np.pi): 79 self.theta2 -= (2 * np.pi) 80 npts = int((self.theta2 - self.theta1) / (np.pi / 120)) 81 81 82 82 if r is None: 83 self.radius = math.sqrt(math.pow(self._mouse_x, 2) + \84 math.pow(self._mouse_y, 2))83 self.radius = np.sqrt(np.power(self._mouse_x, 2) + \ 84 np.power(self._mouse_y, 2)) 85 85 else: 86 86 self.radius = r 87 87 for i in range(self.npts): 88 88 phi = (self.theta2 - self.theta1) / (self.npts - 1) * i + self.theta1 89 xval = 1.0 * self.radius * math.cos(phi)90 yval = 1.0 * self.radius * math.sin(phi)89 xval = 1.0 * self.radius * np.cos(phi) 90 yval = 1.0 * self.radius * np.sin(phi) 91 91 92 92 x.append(xval) -
src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py
- Property mode changed from 100755 to 100644
rb3e8629 re20870bc 4 4 # TODO: NEED MAJOR REFACTOR 5 5 # 6 import math 7 from .BaseInteractor import BaseInteractor 6 import numpy as np 7 from sas.qtgui.Plotting.Slicers.Arc import ArcInteractor 8 from sas.qtgui.Plotting.Slicers.RadiusInteractor import RadiusInteractor 9 from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor 8 10 9 11 class SectorInteractor(BaseInteractor): … … 22 24 # # Number of points on the plot 23 25 self.nbins = 20 24 theta1 = 2 * math.pi / 325 theta2 = -2 * math.pi / 326 theta1 = 2 * np.pi / 3 27 theta2 = -2 * np.pi / 3 26 28 27 29 # Inner circle 28 from .Arc import ArcInteractor29 30 self.inner_circle = ArcInteractor(self, self.base.subplot, 30 31 zorder=zorder, … … 40 41 self.outer_circle.qmax = self.qmax * 1.2 41 42 # self.outer_circle.set_cursor(self.base.qmax/1.8, 0) 42 from Edge import RadiusInteractor43 43 self.right_edge = RadiusInteractor(self, self.base.subplot, 44 44 zorder=zorder + 1, … … 132 132 phimin = self.left_edge.get_angle() 133 133 phimax = self.right_edge.get_angle() 134 # print "phimin, phimax, rmin ,rmax",math.degrees(phimin),135 # math.degrees(phimax), rmin ,rmax136 # from sas.sascalc.dataloader.manipulations import SectorQ137 134 138 135 sect = new_sector(r_min=rmin, r_max=rmax, -
src/sas/qtgui/Plotting/Slicers/BoxSlicer.py
r4992ff2 re20870bc 1 1 import numpy 2 2 3 from .BaseInteractor import BaseInteractor3 from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor 4 4 from sas.qtgui.Plotting.PlotterData import Data1D 5 5 import sas.qtgui.Utilities.GuiUtils as GuiUtils -
src/sas/qtgui/Plotting/Slicers/BoxSum.py
rfbfc488 re20870bc 8 8 from sas.qtgui.Utilities.GuiUtils import formatNumber, toDouble 9 9 10 from .BaseInteractor import BaseInteractor10 from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor 11 11 from sas.sascalc.dataloader.manipulations import Boxavg 12 12 from sas.sascalc.dataloader.manipulations import Boxsum -
src/sas/qtgui/Plotting/Slicers/SectorSlicer.py
rd6b8a1d re20870bc 5 5 import logging 6 6 7 from .BaseInteractor import BaseInteractor7 from sas.qtgui.Plotting.Slicers.BaseInteractor import BaseInteractor 8 8 from sas.qtgui.Plotting.PlotterData import Data1D 9 9 import sas.qtgui.Utilities.GuiUtils as GuiUtils … … 283 283 self.markers = [] 284 284 self.axes = axes 285 self.color = color 285 286 # compute the value of the angle between the current line and 286 287 # the x-axis … … 438 439 if self.phi > numpy.pi: 439 440 self.phi = 2 * numpy.pi - numpy.fabs(self.theta2 - self.theta) 440 self.base.base.update() 441 #self.base.base.update() 442 self.base.update() 441 443 442 444 def set_cursor(self, x, y): … … 464 466 465 467 self.markers = [] 468 self.color = color 466 469 self.axes = axes 467 470 self.save_theta = theta … … 541 544 self.theta = numpy.arctan2(y, x) 542 545 self.has_move = True 543 self.base.base.update() 546 #self.base.base.update() 547 self.base.update() 544 548 545 549 def set_cursor(self, x, y): -
src/sas/qtgui/Plotting/UI/MaskEditorUI.ui
r3010f68 re20870bc 7 7 <x>0</x> 8 8 <y>0</y> 9 <width>6 42</width>10 <height> 431</height>9 <width>687</width> 10 <height>507</height> 11 11 </rect> 12 12 </property> … … 104 104 </sizepolicy> 105 105 </property> 106 <property name="toolTip"> 107 <string>Clear all the masks</string> 108 </property> 106 109 <property name="text"> 107 110 <string>Reset</string> … … 116 119 <verstretch>0</verstretch> 117 120 </sizepolicy> 121 </property> 122 <property name="toolTip"> 123 <string>Clear recent masks</string> 118 124 </property> 119 125 <property name="text"> -
src/sas/qtgui/Utilities/GuiUtils.py
rc5e0d84 re20870bc 251 251 # Action Save Analysis triggered 252 252 saveAnalysisSignal = QtCore.pyqtSignal() 253 254 # Mask Editor requested 255 maskEditorSignal = QtCore.pyqtSignal(Data2D) 253 256 254 257 def updateModelItemWithPlot(item, update_data, name=""):
Note: See TracChangeset
for help on using the changeset viewer.