From dfa12bfc3c1b225e1272493fa6edafba9632f02c Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Tue, 18 Mar 2014 19:48:39 +0100 Subject: [PATCH] new logarithmic double spin box, real and imag fit data are shown, proper cursor position found --- ConductivityGroupBox.py | 6 +-- ConductivityGroupBox.ui | 4 +- CustomWidgets.py | 37 +++++++++++++++ PeakGroupBox.py | 2 +- PeakWidget.py | 4 +- QDS.py | 100 ++++++++++++++++++++++++---------------- QDSMain.py | 8 ++-- QDSMain.ui | 11 ++++- data.py | 29 ++++++------ images_rc.py | 2 +- mathlib.py | 33 ++++++------- 11 files changed, 153 insertions(+), 83 deletions(-) create mode 100644 CustomWidgets.py diff --git a/ConductivityGroupBox.py b/ConductivityGroupBox.py index 249e6da..3bd33ff 100644 --- a/ConductivityGroupBox.py +++ b/ConductivityGroupBox.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'ConductivityGroupBox.ui' # -# Created: Fri Feb 21 17:15:59 2014 +# Created: Sat Mar 15 21:26:00 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_ConductivityGroupBox(object): def setupUi(self, ConductivityGroupBox): ConductivityGroupBox.setObjectName(_fromUtf8("ConductivityGroupBox")) - ConductivityGroupBox.resize(255, 150) + ConductivityGroupBox.resize(254, 150) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -196,7 +196,7 @@ class Ui_ConductivityGroupBox(object): self.pushButton_6.setText(_translate("ConductivityGroupBox", "+", None)) self.lineEdit_2.setText(_translate("ConductivityGroupBox", "0.00e+00", None)) self.lineEdit_1.setText(_translate("ConductivityGroupBox", "0.00e+00", None)) - self.label_1.setText(_translate("ConductivityGroupBox", "εstatic", None)) + self.label_1.setText(_translate("ConductivityGroupBox", "

εinfty

", None)) self.pushButton_3.setText(_translate("ConductivityGroupBox", "-", None)) self.pushButton_4.setText(_translate("ConductivityGroupBox", "+", None)) self.pushButton_1.setText(_translate("ConductivityGroupBox", "-", None)) diff --git a/ConductivityGroupBox.ui b/ConductivityGroupBox.ui index a2626f0..347ca28 100644 --- a/ConductivityGroupBox.ui +++ b/ConductivityGroupBox.ui @@ -6,7 +6,7 @@ 0 0 - 255 + 254 150 @@ -219,7 +219,7 @@ - ε<sub>static</sub> + <html><head/><body><p>ε<span style=" vertical-align:sub;">infty</span></p></body></html> Qt::AlignCenter diff --git a/CustomWidgets.py b/CustomWidgets.py new file mode 100644 index 0000000..9bca6df --- /dev/null +++ b/CustomWidgets.py @@ -0,0 +1,37 @@ +__author__ = 'markusro' + +from PyQt4.QtGui import * +from PyQt4.QtCore import QRegExp +import PeakWidget + + +class LogFSpinBox(QDoubleSpinBox): + scientificNotationValidator = QRegExpValidator(QRegExp("[+-]?(?:0|[1-9]\\d*)(?:\\.\\d*)?(?:[eE][+-]?\\d+)?")) + + def __init__(self, parent = None): + super(LogFSpinBox, self).__init__(parent) + self.setRange(1e-16,1e16) + self.setMinimum(1e-16) + self.setDecimals(17) + self.setValue(1.0) + def stepBy(self, up_down): + print self.minimum(),self.maximum() + if self.value() != 0.0: + self.setValue(self.value()*10**(up_down/9.0)) # 19 steps per decade + + def textFromValue(self, value): + return "%.3e"%value + + def valueFromText(self, str_value): + return str_value.toDouble()[0] + + def validate(self, str_value, p_int): + return self.scientificNotationValidator.validate(str_value, p_int) + +class ParameterWidget(QWidget): + def __init__(self, parent = None): + super(ParameterWidget, self).__init__(parent) + layout = QGridLayout(self) + +if __name__ == "__main__": + app = QApplication([]) diff --git a/PeakGroupBox.py b/PeakGroupBox.py index eb28b6d..d4b1053 100644 --- a/PeakGroupBox.py +++ b/PeakGroupBox.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'PeakGroupBox.ui' # -# Created: Fri Feb 21 17:15:59 2014 +# Created: Sat Mar 15 21:26:00 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! diff --git a/PeakWidget.py b/PeakWidget.py index a46a0f1..c13aaea 100644 --- a/PeakWidget.py +++ b/PeakWidget.py @@ -74,14 +74,14 @@ class PeakWidget(QGroupBox): def peakParameter(self): tmp = [i.text().toDouble()[0] # selects the number, ignores the status for i in self.lineEdits] - print "peakParams:", tmp + #print "peakParams:", tmp return tmp def update(self): self.changedTable.emit() def updateTable(self, *args): - print "updateTable", args + #print "updateTable", args for i, arg in enumerate(args): self.lineEdits[i].setText("%g" % (args[i])) diff --git a/QDS.py b/QDS.py index 26b8374..cb55d67 100755 --- a/QDS.py +++ b/QDS.py @@ -28,6 +28,7 @@ import pyqtgraph as pg #import yaff +USE_CROSSH=False class AppWindow(QMainWindow): def __init__(self, parent=None): @@ -94,26 +95,54 @@ class AppWindow(QMainWindow): self.ui.graphicsView.addItem(self.data.data_curve_real) self.ui.graphicsView.addItem(self.data.fitted_curve_imag) self.ui.graphicsView.addItem(self.data.fitted_curve_real) + + # cross hair + if USE_CROSSH: + #self.poslabel = pg.LabelItem(justify='right') + self.horizontalLine = pg.InfiniteLine(angle=0) + self.verticalLine = pg.InfiniteLine(angle=90) + + #self.ui.graphicsView.plotItem.addItem(self.poslabel) + self.ui.graphicsView.addItem(self.horizontalLine) + self.ui.graphicsView.addItem(self.verticalLine) + self.ui.graphicsView.addItem(self.fit_boundary) self.ui.graphicsView.setLogMode(x=True, y=True) self.ui.graphicsView.showGrid(x=True, y=True) self.ui.graphicsView.setLabel("bottom","Frequency",units="Hz") self.ui.graphicsView.setLabel("left", "Dielectric loss", units="Debye") + self.ui.graphicsView.disableAutoRange() + self.peak_box_layout = QGridLayout() self.ui.scrollAreaWidgetContents.setLayout(self.peak_box_layout) + ##self.huh = pg.SignalProxy(, slot=self.mPE) + sc = self.ui.graphicsView.scene() + sc.sigMouseClicked.connect(self.mousePress) + + sc.sigMouseMoved.connect(self.updateCrosshair) - def mousePressEvent(self, evt): - pos = evt.pos() - if self.ui.graphicsView.sceneRect().contains(pos): - # translate position to data coordinates - data_pos = self.ui.graphicsView.plotItem.vb.mapSceneToView(pos) - if self.ui.actionAdd_Peak.isChecked() and not self.ui.actionAdd_Cond.isChecked(): - self.addPeak(data_pos) - self.ui.actionAdd_Peak.setChecked(False) - if self.ui.actionAdd_Cond.isChecked() and not self.ui.actionAdd_Peak.isChecked(): - self.addCond(data_pos) - self.ui.actionAdd_Cond.setChecked(False) + def updateCrosshair(self,evt): + vb = self.ui.graphicsView.getPlotItem().vb + pos = vb.mapSceneToView(evt) + self.last_pos = pos + if USE_CROSSH: + self.horizontalLine.setBounds([self.data.frequency.min(), self.data.frequency.max()]) + self.horizontalLine.setPos(pos.y()) + self.verticalLine.setPos(pos.x()) + #self.poslabel.setText("f=%0.1f Hz e=%0.1f"\ + # % (pos.x(),pos.y())) + + def mousePress(self, evt): + vb = self.ui.graphicsView.getPlotItem().vb + data_pos = self.last_pos + + if self.ui.actionAdd_Peak.isChecked() and not self.ui.actionAdd_Cond.isChecked(): + self.addPeak(data_pos) + self.ui.actionAdd_Peak.setChecked(False) + if self.ui.actionAdd_Cond.isChecked() and not self.ui.actionAdd_Peak.isChecked(): + self.addCond(data_pos) + self.ui.actionAdd_Cond.setChecked(False) def saveFitResult(self): """ @@ -186,7 +215,8 @@ class AppWindow(QMainWindow): self.statusBar().showMessage("Click on graph") self.Conductivity = Conductivity(mpl=self.ui.graphicsView, limits=self.data.fit_limits) self.Conductivity.changedData.connect(self.updatePlot) - self.Conductivity.setParameter(0, 1 / (10**pos.x() / 10**pos.y() / 2 / N.pi), 1.0) + print pos.x(), pos.y(),"test", 10**pos.x(),10**pos.y() + self.Conductivity.setParameter(0, 10**(pos.y() + pos.x())*2*N.pi , 1.0) self.ui.scrollAreaWidgetContents.layout().addWidget(self.Conductivity.widget) self.Conductivity.widget.ui.removeButton.clicked.connect(self.delCond) @@ -208,7 +238,8 @@ class AppWindow(QMainWindow): # connect to delPeak peak.widget.ui.removeButton.clicked.connect(self.delPeak) peak.changedData.connect(self.updatePlot) - peak.setParameter(delta_eps=10**pos.y(), tau=1 / (10**pos.x()/2/N.pi), a=1, b=1) + print pos, 10**pos.x(), 10**pos.y() + peak.setParameter(delta_eps=(10**pos.y())*2, tau=1 / (2*N.pi*10**pos.x()), a=1, b=1) self.peakBoxes[peak] = None wdgt_num = self.peak_box_layout.rowCount() @@ -261,7 +292,7 @@ class AppWindow(QMainWindow): print "Set fit data" self.data.set_fit(newres.beta, funcs) print newres.beta,newres.sd_beta - + result = newres.beta self.fitresult = result for i, pb in enumerate(self.peakBoxes.keys()): @@ -276,10 +307,10 @@ class AppWindow(QMainWindow): self.updatePlot() def openFile(self): - path = unicode(QFileDialog.getOpenFileName(self, "Open file")) + #path = unicode(QFileDialog.getOpenFileName(self, "Open file")) + path = "MCM42PG0_199.96K.dat" self.filepath=path - #path = "MCM42PG0_199.96K.dat" - # TODO anaylize file (LF,MF, HF) and act accordingly + # TODO analyize file (LF,MF, HF) and act accordingly data = N.loadtxt(path, skiprows=4) self.setWindowTitle(os.path.basename(path)) numpat = re.compile('\d+\.\d+') @@ -302,39 +333,30 @@ class AppWindow(QMainWindow): self.data.set_data(_freq, _die_stor, _die_loss) self.data.meta["T"] = Temp self.fit_boundary.setRegion([N.log10(_freq.min()), N.log10(_freq.max())]) - + self.ui.graphicsView.enableAutoRange() self.updatePlot() + self.ui.graphicsView.disableAutoRange() def updatePlot(self): - nu = self.data.frequency - fit = N.zeros(len(nu)) - if self.Conductivity != None: - print "Cond. given" - params = self.Conductivity.getParameter()[1:] - fit += conductivity(params, nu) - fit += self.Conductivity.getParameter()[0] # eps static - funcs = ["static", "power"] - p0 = self.Conductivity.getParameter() if self.Conductivity != None else [0.0, 0.0, 1.0] + # assemble fit function + funcs = ["static", "power"] + p0 = self.Conductivity.getParameter() if self.Conductivity is not None else [0.0, 0.0, 1.0] for peak in self.peakBoxes.keys(): params = peak.getParameter() - fit += hn(params, nu) - #fit += peak.get_data()[1] p0.extend(params) funcs.append("hn") - #self.data.epsilon_fit = fit[:] - - print "p0",p0 + # calculate fit self.data.set_fit(p0, funcs) - fit = self.data.epsilon_fit[:] - self.data.data_curve_imag.setData(self.data.frequency, self.data.epsilon.imag) - self.data.data_curve_real.setData(self.data.frequency, self.data.epsilon.real) - if len(self.peakBoxes) > 0 or self.Conductivity != None: - self.data.fitted_curve_imag.setData(nu, fit.imag) - self.data.fitted_curve_real.setData(nu, fit.real) + # replot data and fit, TODO: replot only if data changed + self.data.data_curve_real.setData(self.data.frequency, self.data.epsilon.real) + self.data.data_curve_imag.setData(self.data.frequency, self.data.epsilon.imag) + if len(self.peakBoxes) > 0 or self.Conductivity != None: + self.data.fitted_curve_real.setData(self.data.frequency, self.data.epsilon_fit.real) + self.data.fitted_curve_imag.setData(self.data.frequency, self.data.epsilon_fit.imag) def sigint_handler(*args): """Handler for the SIGINT signal (CTRL + C). @@ -342,7 +364,7 @@ def sigint_handler(*args): sys.stderr.write('\r') if QMessageBox.question(None, '', "Are you sure you want to quit?", QMessageBox.Yes | QMessageBox.No, - QMessageBox.No) == QMessageBox.Yes: + QMessageBox.Yes) == QMessageBox.Yes: QApplication.quit() if __name__ == '__main__': diff --git a/QDSMain.py b/QDSMain.py index 164f509..90197c7 100644 --- a/QDSMain.py +++ b/QDSMain.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'QDSMain.ui' # -# Created: Fri Feb 21 17:15:59 2014 +# Created: Sat Mar 15 21:26:00 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -39,8 +39,8 @@ class Ui_MainWindow(object): sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) self.centralwidget.setSizePolicy(sizePolicy) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) - self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget) - self.verticalLayout.setObjectName(_fromUtf8("verticalLayout")) + self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget) + self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.splitter = QtGui.QSplitter(self.centralwidget) self.splitter.setOrientation(QtCore.Qt.Horizontal) self.splitter.setObjectName(_fromUtf8("splitter")) @@ -65,7 +65,7 @@ class Ui_MainWindow(object): sizePolicy.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth()) self.graphicsView.setSizePolicy(sizePolicy) self.graphicsView.setObjectName(_fromUtf8("graphicsView")) - self.verticalLayout.addWidget(self.splitter) + self.horizontalLayout.addWidget(self.splitter) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 956, 22)) diff --git a/QDSMain.ui b/QDSMain.ui index 05a4883..ff13b37 100644 --- a/QDSMain.ui +++ b/QDSMain.ui @@ -26,7 +26,7 @@ 0 - + @@ -63,6 +63,15 @@ 572 + + + + + GroupBox + + + + diff --git a/data.py b/data.py index dd4246f..1980027 100644 --- a/data.py +++ b/data.py @@ -19,7 +19,7 @@ class Data: self.data_curve_imag = pg.PlotDataItem(x=[N.nan], y=[N.nan],pen=QColor(0,0,0,0), symbol='o', symbolBrush=(255,127,0,127)) self.data_curve_real = pg.PlotDataItem(x=[N.nan], y=[N.nan],pen=QColor(0,0,0,0), symbol='s', - symbolBrush=(53,159,50,127)) + symbolBrush=(119,202,92,127)) self.fitted_curve_imag = pg.PlotDataItem(N.array([N.nan]), N.array([N.nan]), pen=myPen_imag) self.fitted_curve_real = pg.PlotDataItem(N.array([N.nan]), N.array([N.nan]), pen=myPen_real) self.length = len(frequency) @@ -63,8 +63,8 @@ class Data: """ mask = N.ones(len(self.frequency), dtype='bool') mask = (self.frequency > self.fit_limits[0]) & (self.frequency < self.fit_limits[1]) - mask &= (self.epsilon.imag > self.fit_limits[2]) & (self.epsilon.imag < self.fit_limits[3]) - mask &= (self.epsilon.real > self.fit_limits[2]) & (self.epsilon.real < self.fit_limits[3]) + #mask &= (self.epsilon.imag > self.fit_limits[2]) & (self.epsilon.imag < self.fit_limits[3]) + #mask &= (self.epsilon.real > self.fit_limits[2]) & (self.epsilon.real < self.fit_limits[3]) return self.frequency[mask], self.epsilon[mask] def remove_curves(self): @@ -117,15 +117,16 @@ class Conductivity(QObject): y = conductivity([sigma, sigma_N], nu) y_static = N.ones(len(nu)) * eps_static # clip data to axes limits - mask_static = (y_static < y_max) & (y_static > y_min) - # clip data to axes limits - mask = (y < y_max) & (y > y_min) - self.frequency = nu[mask] - self.conductivity = y[mask] - self.epsilon_static = y_static[mask] - self.mpl_line.setData(x=nu[mask], y=y[mask], label="Cond.") - self.mpl_line_static.setData(x=nu[mask_static], y=y_static[mask_static]) + #mask_static = (y_static < y_max) & (y_static > y_min) + # clip data to axes limits + #mask = (y < y_max) & (y > y_min) + + self.frequency = nu + self.conductivity = y + self.epsilon_static = y_static + self.mpl_line.setData(x=nu, y=y, label="Cond.") + self.mpl_line_static.setData(x=nu, y=y_static) self.changedData.emit() @@ -184,9 +185,9 @@ class Peak(QObject): nu = N.logspace(N.log10(x_min), N.log10(x_max), 2048) y = hn(self.getParameter(), nu) # clip data to axes limits - mask = (y < y_max) & (y > y_min) - y = y[mask] - nu = nu[mask] + #mask = (y < y_max) & (y > y_min) + #y = y[mask] + #nu = nu[mask] self.frequency = nu[:] self.epsilon = y[:] self.mpl_line.setData(x=nu, y=y) diff --git a/images_rc.py b/images_rc.py index 0db5c38..ada7af3 100644 --- a/images_rc.py +++ b/images_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Fr. Feb. 21 17:15:59 2014 +# Created: Sa. Mär. 15 21:26:00 2014 # by: The Resource Compiler for PyQt (Qt v4.8.5) # # WARNING! All changes made in this file will be lost! diff --git a/mathlib.py b/mathlib.py index 52391b3..702cad0 100644 --- a/mathlib.py +++ b/mathlib.py @@ -74,7 +74,7 @@ def hn(p, nu): -b / 2.) * N.sin(b * Phi) e_stor = delta_eps * (1 + 2 * (om * tau) ** a * N.cos(N.pi * a / 2.) + (om * tau) ** (2. * a) ) ** ( -b / 2.) * N.cos(b * Phi) - return 2 * e_loss + return e_loss # 2* oder nicht? def id_to_color(id): @@ -139,32 +139,30 @@ class Functions: def hn_cmplx(self, p, x): om = 2*N.pi*x - hn = om*1j + #hn = om*1j eps,t,a,b = p - print p - hn = eps/(1-(1j*om*t)**a)**b - cplx = N.array([hn.real, hn.imag]) - print cplx + hn = eps/(1+(1j*om*t)**a)**b + cplx = N.array([hn.real, -hn.imag]) return cplx def cond_cmplx(self, p, x): om = 2*N.pi*x sgma = p[0] - cond = -sgma/(1j*om) - cplx = N.array([cond.real, cond.imag]) + cond = sgma/(1j*om) + cplx = N.array([cond.real, -cond.imag]) return cplx def power_cmplx(self, p, x): om = 2*N.pi*x sgma,n = p - power = -sgma/(om*1j)**n - cplx = N.array([power.real, power.imag]) + power = sgma/(om*1j)**n + cplx = N.array([power.real, -power.imag]) return cplx def static_cmplx(self, p, x): eps_inf = p[0] static = N.ones( (2,x.size) )*eps_inf - static[:,1] *= 0 # set imag part zero + static[1,:] *= 0 # set imag part zero #cplx = N.array([static.real, static.imag]) return static @@ -184,7 +182,6 @@ class FitFunctionCreator: self.data = N.zeros( (2,x.size) ) ndx = 0 for fn in funcs: # loop over functions and add the results up - f,num_p = self.functions.get(fn) p = p0[ndx:ndx + num_p] if x.ndim == 2: @@ -195,16 +192,20 @@ class FitFunctionCreator: def fit_odr_cmplx(x, y, p0, fixed, fcns): f = FitFunctionCreator() - if x.ndim < 2: - x = N.resize(x, (2,x.size)) + #if x.ndim < 2: + # x = N.resize(x, (2,x.size)) if N.iscomplexobj(y) and y.ndim == 1: + weights = 1/N.abs(y)**2 + we = N.resize(weights, (2, weights.size)) + #we = 1/N.array([y.real**2, y.imag**2]) y = N.array([y.real, y.imag]) else: raise NotImplementedError,"need complex input for now" - dat = odr.Data(x, y, 1.0 / y**2) + dat = odr.Data(x, y, we=we) mod = odr.Model(f.fitfcn, extra_args=fcns) - fit = odr.ODR(dat, mod, p0, ifixx=N.zeros(x.ndim), ifixb=fixed, maxit=5000) + fit = odr.ODR(dat, mod, p0, ifixx=(0,), ifixb=fixed, maxit=8000) fit.run() + print fit.output.pprint() return fit.output