[6d96bf9] | 1 | # global |
---|
| 2 | import sys |
---|
| 3 | from PyQt4 import QtCore |
---|
| 4 | from PyQt4 import QtGui |
---|
| 5 | from PyQt4 import QtWebKit |
---|
| 6 | |
---|
| 7 | from twisted.internet import threads |
---|
| 8 | from twisted.internet import reactor |
---|
| 9 | |
---|
| 10 | # sas-global |
---|
| 11 | from sas.qtgui.Plotting.PlotterData import Data1D |
---|
| 12 | import sas.qtgui.Utilities.GuiUtils as GuiUtils |
---|
[b2c8aef] | 13 | from sas.sascalc.corfunc.corfunc_calculator import CorfuncCalculator |
---|
[6d96bf9] | 14 | |
---|
| 15 | # local |
---|
| 16 | from UI.CorfuncPanel import Ui_CorfuncDialog |
---|
| 17 | # from InvariantDetails import DetailsDialog |
---|
[59183b7] | 18 | from CorfuncUtils import WIDGETS as W |
---|
[6d96bf9] | 19 | |
---|
[22e6043] | 20 | from matplotlib.backends import qt_compat |
---|
| 21 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas |
---|
| 22 | from matplotlib.figure import Figure |
---|
| 23 | |
---|
| 24 | |
---|
| 25 | class MyMplCanvas(FigureCanvas): |
---|
| 26 | """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).""" |
---|
| 27 | def __init__(self, parent=None, width=5, height=4, dpi=100): |
---|
| 28 | self.fig = Figure(figsize=(width, height), dpi=dpi) |
---|
| 29 | self.axes = self.fig.add_subplot(111) |
---|
| 30 | |
---|
| 31 | FigureCanvas.__init__(self, self.fig) |
---|
| 32 | # self.reparent(parent, QPoint(0, 0)) |
---|
| 33 | |
---|
| 34 | # FigureCanvas.setSizePolicy(self, |
---|
| 35 | # QSizePolicy.Expanding, |
---|
| 36 | # QSizePolicy.Expanding) |
---|
| 37 | # FigureCanvas.updateGeometry(self) |
---|
| 38 | |
---|
[f159d1b] | 39 | self.data = None |
---|
| 40 | self.qmin = None |
---|
| 41 | self.qmax1 = None |
---|
| 42 | self.qmax2 = None |
---|
| 43 | self.extrap = None |
---|
| 44 | |
---|
| 45 | def drawQSpace(self): |
---|
| 46 | self.fig.clf() |
---|
| 47 | |
---|
| 48 | self.axes = self.fig.add_subplot(111) |
---|
| 49 | self.axes.set_xscale("log") |
---|
| 50 | self.axes.set_yscale("log") |
---|
| 51 | |
---|
| 52 | if self.data: |
---|
| 53 | self.axes.plot(self.data.x, self.data.y) |
---|
| 54 | if self.qmin: |
---|
| 55 | self.axes.axvline(self.qmin) |
---|
| 56 | if self.qmax1: |
---|
| 57 | self.axes.axvline(self.qmax1) |
---|
| 58 | if self.qmax2: |
---|
| 59 | self.axes.axvline(self.qmax2) |
---|
| 60 | if self.extrap: |
---|
| 61 | self.axes.plot(self.extrap.x, self.extrap.y) |
---|
| 62 | |
---|
| 63 | self.draw() |
---|
| 64 | |
---|
[f7b73d5] | 65 | def drawRealSpace(self): |
---|
| 66 | self.fig.clf() |
---|
| 67 | |
---|
| 68 | self.axes = self.fig.add_subplot(111) |
---|
| 69 | self.axes.set_xscale("linear") |
---|
| 70 | self.axes.set_yscale("linear") |
---|
| 71 | |
---|
| 72 | if self.data: |
---|
| 73 | self.axes.plot(self.data.x, self.data.y) |
---|
| 74 | |
---|
| 75 | self.draw() |
---|
[22e6043] | 76 | |
---|
[f159d1b] | 77 | |
---|
| 78 | # def sizeHint(self): |
---|
| 79 | # w, h = self.get_width_height() |
---|
| 80 | # return QSize(w, h) |
---|
| 81 | |
---|
| 82 | # def minimumSizeHint(self): |
---|
| 83 | # return QSize(10, 10) |
---|
[22e6043] | 84 | |
---|
[6d96bf9] | 85 | |
---|
| 86 | class CorfuncWindow(QtGui.QDialog, Ui_CorfuncDialog): |
---|
| 87 | # The controller which is responsible for managing signal slots connections |
---|
| 88 | # for the gui and providing an interface to the data model. |
---|
| 89 | name = "Corfunc" # For displaying in the combo box |
---|
| 90 | #def __init__(self, manager=None, parent=None): |
---|
| 91 | def __init__(self, parent=None): |
---|
| 92 | #super(InvariantWindow, self).__init__(parent) |
---|
| 93 | super(CorfuncWindow, self).__init__() |
---|
| 94 | self.setupUi(self) |
---|
| 95 | |
---|
| 96 | self.setWindowTitle("Corfunc Perspective") |
---|
| 97 | |
---|
[59183b7] | 98 | self.model = QtGui.QStandardItemModel(self) |
---|
[6d96bf9] | 99 | self.communicate = GuiUtils.Communicate() |
---|
[b2c8aef] | 100 | self._calculator = CorfuncCalculator() |
---|
[6d96bf9] | 101 | |
---|
[22e6043] | 102 | self._canvas = MyMplCanvas(self) |
---|
[f7b73d5] | 103 | self._realplot = MyMplCanvas(self) |
---|
[f159d1b] | 104 | self.verticalLayout_7.insertWidget(0,self._canvas) |
---|
[f7b73d5] | 105 | self.verticalLayout_7.insertWidget(1, self._realplot) |
---|
[22e6043] | 106 | |
---|
[59183b7] | 107 | # Connect buttons to slots. |
---|
| 108 | # Needs to be done early so default values propagate properly. |
---|
| 109 | self.setupSlots() |
---|
| 110 | |
---|
| 111 | # Set up the model. |
---|
| 112 | self.setupModel() |
---|
| 113 | |
---|
| 114 | # Set up the mapper |
---|
| 115 | self.setupMapper() |
---|
| 116 | |
---|
| 117 | def setupSlots(self): |
---|
[7b536da] | 118 | self.extrapolateBtn.clicked.connect(self.extrapolate) |
---|
[f7b73d5] | 119 | self.transformBtn.clicked.connect(self.transform) |
---|
[59183b7] | 120 | |
---|
[b2c8aef] | 121 | self.calculateBgBtn.clicked.connect(self.calculateBackground) |
---|
| 122 | |
---|
[59183b7] | 123 | self.hilbertBtn.clicked.connect(self.action) |
---|
| 124 | self.fourierBtn.clicked.connect(self.action) |
---|
| 125 | |
---|
[b2c8aef] | 126 | self.model.itemChanged.connect(self.modelChanged) |
---|
| 127 | |
---|
[59183b7] | 128 | def setupModel(self): |
---|
| 129 | self.model.setItem(W.W_QMIN, |
---|
| 130 | QtGui.QStandardItem("0")) |
---|
| 131 | self.model.setItem(W.W_QMAX, |
---|
| 132 | QtGui.QStandardItem("0")) |
---|
| 133 | self.model.setItem(W.W_QCUTOFF, |
---|
| 134 | QtGui.QStandardItem("0")) |
---|
| 135 | self.model.setItem(W.W_BACKGROUND, |
---|
| 136 | QtGui.QStandardItem("0")) |
---|
| 137 | self.model.setItem(W.W_TRANSFORM, |
---|
| 138 | QtGui.QStandardItem("Fourier")) |
---|
| 139 | |
---|
[b2c8aef] | 140 | def modelChanged(self, item): |
---|
| 141 | if item.row() == W.W_QMIN: |
---|
| 142 | value = float(self.model.item(W.W_QMIN).text()) |
---|
| 143 | self.qMin.setValue(value) |
---|
| 144 | self._calculator.lowerq = value |
---|
[f159d1b] | 145 | self._canvas.qmin = value |
---|
[b2c8aef] | 146 | elif item.row() == W.W_QMAX: |
---|
| 147 | value = float(self.model.item(W.W_QMAX).text()) |
---|
| 148 | self.qMax1.setValue(value) |
---|
| 149 | self._calculator.upperq = (value, self._calculator.upperq[1]) |
---|
[f159d1b] | 150 | self._canvas.qmax1 = value |
---|
[b2c8aef] | 151 | elif item.row() == W.W_QCUTOFF: |
---|
| 152 | value = float(self.model.item(W.W_QCUTOFF).text()) |
---|
| 153 | self.qMax2.setValue(value) |
---|
| 154 | self._calculator.upperq = (self._calculator.upperq[0], value) |
---|
[f159d1b] | 155 | self._canvas.qmax2 = value |
---|
[b2c8aef] | 156 | elif item.row() == W.W_BACKGROUND: |
---|
| 157 | value = float(self.model.item(W.W_BACKGROUND).text()) |
---|
[7b536da] | 158 | self.bg.setValue(value) |
---|
| 159 | self._calculator.background = value |
---|
[b2c8aef] | 160 | else: |
---|
| 161 | print("{} Changed".format(item)) |
---|
| 162 | |
---|
[f159d1b] | 163 | self._canvas.drawQSpace() |
---|
| 164 | |
---|
[b2c8aef] | 165 | |
---|
[7b536da] | 166 | def extrapolate(self): |
---|
| 167 | params, extrapolation = self._calculator.compute_extrapolation() |
---|
| 168 | self.guinierA.setValue(params['A']) |
---|
| 169 | self.guinierB.setValue(params['B']) |
---|
| 170 | self.porodK.setValue(params['K']) |
---|
| 171 | self.porodSigma.setValue(params['sigma']) |
---|
[f159d1b] | 172 | self._canvas.extrap = extrapolation |
---|
| 173 | self._canvas.drawQSpace() |
---|
[7b536da] | 174 | |
---|
| 175 | |
---|
[f7b73d5] | 176 | def transform(self): |
---|
| 177 | if self.fourierBtn.isChecked(): |
---|
| 178 | method = "fourier" |
---|
| 179 | elif self.hilbertBtn.isChecked(): |
---|
| 180 | method = "hilbert" |
---|
| 181 | |
---|
| 182 | extrap = self._canvas.extrap |
---|
| 183 | bg = self._calculator.background |
---|
| 184 | def updatefn(*args, **kwargs): |
---|
| 185 | pass |
---|
| 186 | |
---|
| 187 | def completefn(transform): |
---|
| 188 | self._realplot.data = transform |
---|
| 189 | self._realplot.drawRealSpace() |
---|
[c1b3ffb] | 190 | params = self._calculator.extract_parameters(transform) |
---|
| 191 | self.avgCoreThick.setValue(params["d0"]) |
---|
| 192 | self.avgHardBlock.setValue(params["Lc"]) |
---|
| 193 | self.avgIntThick.setValue(params["dtr"]) |
---|
| 194 | self.localCrystal.setValue(params["fill"]) |
---|
| 195 | self.polydisp.setValue(params["A"]) |
---|
| 196 | self.longPeriod.setValue(params["max"]) |
---|
[f7b73d5] | 197 | |
---|
| 198 | self._calculator.compute_transform(extrap, method, bg, completefn, updatefn) |
---|
| 199 | |
---|
[7b536da] | 200 | |
---|
[59183b7] | 201 | def setupMapper(self): |
---|
| 202 | self.mapper = QtGui.QDataWidgetMapper(self) |
---|
| 203 | self.mapper.setOrientation(QtCore.Qt.Vertical) |
---|
| 204 | self.mapper.setModel(self.model) |
---|
| 205 | |
---|
| 206 | self.mapper.addMapping(self.qMin, W.W_QMIN) |
---|
| 207 | self.mapper.addMapping(self.qMax1, W.W_QMAX) |
---|
| 208 | self.mapper.addMapping(self.qMax2, W.W_QCUTOFF) |
---|
| 209 | self.mapper.addMapping(self.bg, W.W_BACKGROUND) |
---|
| 210 | |
---|
| 211 | self.mapper.toFirst() |
---|
| 212 | |
---|
[b2c8aef] | 213 | def calculateBackground(self): |
---|
| 214 | bg = self._calculator.compute_background() |
---|
| 215 | print(bg) |
---|
| 216 | self.model.setItem(W.W_BACKGROUND, QtGui.QStandardItem(str(bg))) |
---|
[59183b7] | 217 | |
---|
| 218 | def action(self): |
---|
| 219 | print("Called an action!") |
---|
| 220 | print(self.model) |
---|
| 221 | print(self.mapper) |
---|
| 222 | |
---|
[6d96bf9] | 223 | def allowBatch(self): |
---|
| 224 | """ |
---|
| 225 | We cannot perform corfunc analysis in batch at this time. |
---|
| 226 | """ |
---|
| 227 | return False |
---|
| 228 | |
---|
| 229 | def setData(self, data_item, is_batch=False): |
---|
| 230 | """ |
---|
| 231 | Obtain a QStandardItem object and dissect it to get Data1D/2D |
---|
| 232 | Pass it over to the calculator |
---|
| 233 | """ |
---|
| 234 | if not isinstance(data_item, list): |
---|
| 235 | msg = "Incorrect type passed to the Corfunc Perpsective" |
---|
| 236 | raise AttributeError(msg) |
---|
| 237 | |
---|
| 238 | if not isinstance(data_item[0], QtGui.QStandardItem): |
---|
| 239 | msg = "Incorrect type passed to the Corfunc Perspective" |
---|
| 240 | raise AttributeError(msg) |
---|
| 241 | |
---|
| 242 | self._model_item = data_item[0] |
---|
| 243 | data = GuiUtils.dataFromItem(self._model_item) |
---|
[b2c8aef] | 244 | self._calculator.lowerq = 1e-3 |
---|
| 245 | self._calculator.upperq = (2e-1, 3e-1) |
---|
| 246 | self._calculator.set_data(data) |
---|
[6d96bf9] | 247 | |
---|
[f159d1b] | 248 | self._canvas.data = data |
---|
| 249 | self._canvas.drawQSpace() |
---|
[22e6043] | 250 | |
---|
[6d96bf9] | 251 | # self.model.item(WIDGETS.W_FILENAME).setData(QtCoreQVariant(self._model_item.text())) |
---|
| 252 | |
---|
| 253 | def setClosable(self, value=True): |
---|
| 254 | """ |
---|
| 255 | Allow outsiders close this widget |
---|
| 256 | """ |
---|
| 257 | assert isinstance(value, bool) |
---|
| 258 | |
---|
| 259 | self._allow_close = value |
---|
| 260 | |
---|
| 261 | |
---|
| 262 | if __name__ == "__main__": |
---|
| 263 | app = QtGui.QApplication([]) |
---|
| 264 | import qt4reactor |
---|
| 265 | # qt4reactor.install() |
---|
| 266 | # DO NOT move the following import to the top! |
---|
| 267 | # (unless you know what you're doing) |
---|
| 268 | from twisted.internet import reactor |
---|
| 269 | dlg = CorfuncWindow(reactor) |
---|
| 270 | print(dlg) |
---|
| 271 | dlg.show() |
---|
| 272 | # print(reactor) |
---|
| 273 | # reactor.run() |
---|
| 274 | sys.exit(app.exec_()) |
---|