From e18364b0759294c8b499639274c8aade5109ce10 Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Wed, 19 Mar 2014 22:02:26 +0100 Subject: [PATCH] started major refactoring to simplify function addition/removal --- ConductivityGroupBox.py | 2 +- CustomWidgets.py | 92 +++++++++++++++++++++++++++++++++- Makefile | 1 + PeakGroupBox.py | 2 +- QDS.py | 79 ++++++++++++++++++++++------- QDSMain.py | 9 +++- QDSMain.ui | 16 ++++++ data.py | 107 ++++++++++++++++++++++++++++++++-------- images_rc.py | 2 +- mathlib.py | 18 +++++++ 10 files changed, 284 insertions(+), 44 deletions(-) diff --git a/ConductivityGroupBox.py b/ConductivityGroupBox.py index cb9f666..4e78236 100644 --- a/ConductivityGroupBox.py +++ b/ConductivityGroupBox.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'ConductivityGroupBox.ui' # -# Created: Wed Mar 19 19:18:39 2014 +# Created: Wed Mar 19 20:57:16 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! diff --git a/CustomWidgets.py b/CustomWidgets.py index a28bed4..3fa620b 100644 --- a/CustomWidgets.py +++ b/CustomWidgets.py @@ -3,6 +3,7 @@ from PyQt4.QtGui import QGroupBox, QPalette, QColor import ConductivityGroupBox import PeakGroupBox +import PowerLawGroupBox __author__ = 'markusro' @@ -51,6 +52,7 @@ if __name__ == "__main__": class PeakWidget(QGroupBox): changedTable = pyqtSignal() + removeMe = pyqtSignal() def __init__(self, parent=None): QGroupBox.__init__(self) @@ -93,6 +95,11 @@ class PeakWidget(QGroupBox): # self.ui.checkBox_4.stateChanged.connect(self._distrib_cd) self.ui.comboBox.currentIndexChanged.connect(self._distrib_select) + self.ui.removeButton.clicked.connect(self.remove) + + def remove(self): + self.removeMe.emit() + def changeValues(self): self.changedTable.emit() @@ -184,6 +191,7 @@ class PeakWidget(QGroupBox): class ConductivityWidget(QGroupBox): changedTable = pyqtSignal() + removeMe = pyqtSignal() def __init__(self, parent=None): QGroupBox.__init__(self) @@ -219,6 +227,11 @@ class ConductivityWidget(QGroupBox): for dsb in self.inputs: dsb.valueChanged.connect(self.changeValues) + self.ui.removeButton.clicked.connect(self.remove) + + def remove(self): + self.removeMe.emit() + def changeValues(self, num): self.changedTable.emit() @@ -245,9 +258,84 @@ class ConductivityWidget(QGroupBox): for i, arg in enumerate(beta): self.inputs[i].setValue(arg) sd_style="" - if i in (0,1) and sd_beta is not None: + if i in (1,) and sd_beta is not None: sd = "+/- %.3e"%(sd_beta[i]) - elif i in (2,3) and sd_beta is not None: + elif i in (0,2) and sd_beta is not None: + sd = "+/- %.2f"%(sd_beta[i]) + if sd_beta is not None: + if 0.0 < sd_beta[i]/arg < 0.2: + sd_style="background-color: rgba(0, 255, 0, 64);" + if 0.2 < sd_beta[i]/arg < 1.0: + sd_style="background-color: rgba(255,255, 0, 64);" + elif sd_beta[i]/arg > 1.0: + sd_style="background-color: rgba(255, 0, 0, 64);" + + else: + sd = "( --- )" + self.errors[i].setStyleSheet(sd_style) + self.errors[i].setText(sd) + + +class PowerLawWidget(QGroupBox): + changedTable = pyqtSignal() + removeMe = pyqtSignal() + def __init__(self, parent=None): + QGroupBox.__init__(self) + super(PowerLawWidget, self).__init__(parent) + self.setTitle(u"Conductivity …") + self.ui = PowerLawGroupBox.Ui_PowerLawGroupBox() + self.ui.setupUi(self) + + self.ui.doubleSpinBox_2.setParent(None) + self.ui.doubleSpinBox_2 = LogFSpinBox(self) + self.ui.gridLayout.addWidget(self.ui.doubleSpinBox_2,1,1) + + + self.errors = [self.ui.label_5, + self.ui.label_6] + + self.inputs = [self.ui.doubleSpinBox_2, + self.ui.doubleSpinBox_3] + + self.fixedCheckBoxes = [ self.ui.checkBox_2, + self.ui.checkBox_3] + for dsb in self.inputs: + dsb.valueChanged.connect(self.changeValues) + + self.ui.removeButton.clicked.connect(self.remove) + + def remove(self): + self.removeMe.emit() + + + def changeValues(self, num): + self.changedTable.emit() + + def fixedParameter(self): + return [0 if cb.isChecked() else 1 for cb in self.fixedCheckBoxes] + + def setColor(self, color): + r, g, b = color + palette = self.palette() + palette.setColor(QPalette.Foreground, QColor(r, g, b)) + self.setPalette(palette) + + def getTable(self): + tmp = [i.value() # selects the number, ignores the status + for i in self.inputs] + #print "peakParams:", tmp + return tmp + + def update(self): + self.changedTable.emit() + + def updateTable(self, beta, sd_beta=None): + for i, arg in enumerate(beta): + self.inputs[i].setValue(arg) + sd_style="" + if i in (0,) and sd_beta is not None: + sd = "+/- %.3e"%(sd_beta[i]) + elif i in (1,) and sd_beta is not None: sd = "+/- %.2f"%(sd_beta[i]) if sd_beta is not None: if 0.0 < sd_beta[i]/arg < 0.2: diff --git a/Makefile b/Makefile index 1b15769..b467e65 100644 --- a/Makefile +++ b/Makefile @@ -3,3 +3,4 @@ all: pyuic4 QDSMain.ui -o QDSMain.py pyuic4 PeakGroupBox.ui -o PeakGroupBox.py pyuic4 ConductivityGroupBox.ui -o ConductivityGroupBox.py + pyuic4 PowerLawGroupBox.ui -o PowerLawGroupBox.py diff --git a/PeakGroupBox.py b/PeakGroupBox.py index 44d8e26..18a1fc0 100644 --- a/PeakGroupBox.py +++ b/PeakGroupBox.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'PeakGroupBox.ui' # -# Created: Wed Mar 19 19:18:39 2014 +# Created: Wed Mar 19 20:57:16 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! diff --git a/QDS.py b/QDS.py index 7e459da..a160b4e 100755 --- a/QDS.py +++ b/QDS.py @@ -9,7 +9,7 @@ from PyQt4.QtCore import * from PyQt4.QtGui import * import matplotlib -from mathlib import fit_anneal, fit_lbfgsb, fit_odr_cmplx +from mathlib import fit_anneal, fit_lbfgsb, fit_odr_cmplx, FunctionRegister matplotlib.use('agg') @@ -22,7 +22,7 @@ import numpy as N import QDSMain -from data import Data, Conductivity, Peak +from data import Data, Conductivity, Peak, PowerComplex import pyqtgraph as pg #import yaff @@ -39,6 +39,16 @@ class AppWindow(QMainWindow): self.picked_artist = None self.data = None + actions = { + self.ui.actionAdd_Peak:self.addPeak, + self.ui.actionAdd_Cond:self.addCond, + self.ui.actionAdd_PowerLaw:self.addPowerComplex + } + self.myActionGroup = QActionGroup(self) + for a in actions.keys(): self.myActionGroup.addAction(a) + + self.function_registry = FunctionRegister() + self.Conductivity = None self._lines = dict() @@ -138,15 +148,17 @@ class AppWindow(QMainWindow): # % (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(): + if self.ui.actionAdd_Peak.isChecked(): self.addPeak(data_pos) self.ui.actionAdd_Peak.setChecked(False) - if self.ui.actionAdd_Cond.isChecked() and not self.ui.actionAdd_Peak.isChecked(): + if self.ui.actionAdd_Cond.isChecked(): self.addCond(data_pos) self.ui.actionAdd_Cond.setChecked(False) + if self.ui.actionAdd_PowerLaw.isChecked(): + self.addPowerComplex(data_pos) + self.ui.actionAdd_PowerLaw.setChecked(False) + def saveFitResult(self): """ @@ -198,7 +210,7 @@ class AppWindow(QMainWindow): pyplot.loglog(f,eps, ls="--", color=color , lw=0.75, label="Peak %i"%i) if self.Conductivity != None: - f,eps = self.Conductivity.get_conductivity() + f,eps = self.Conductivity.get_data() color = hex2color(str(self.Conductivity.get_color().name())) pyplot.loglog(f,eps, ls="-.", color=color, lw=0.75, label="Cond.") f,eps = self.Conductivity.get_epsilon_static() @@ -214,12 +226,13 @@ class AppWindow(QMainWindow): def addCond(self, pos): - win = self.parameterWidget - win.setWindowFlags(win.windowFlags() | Qt.WindowStaysOnTopHint) + #win = self.parameterWidget + #win.setWindowFlags(win.windowFlags() | Qt.WindowStaysOnTopHint) if self.Conductivity != None: return self.statusBar().showMessage("Click on graph") - self.Conductivity = Conductivity(mpl=self.ui.graphicsView, limits=self.data.fit_limits) + self.Conductivity = Conductivity(pgPlotWidget=self.ui.graphicsView, limits=self.data.fit_limits) + self.function_registry.register_function(self.Conductivity) self.Conductivity.changedData.connect(self.updatePlot) #print pos.x(), pos.y(),"test", 10**pos.x(),10**pos.y() cond_par = [0, 10**(pos.y() + pos.x())*2*N.pi , 1.0] @@ -233,17 +246,44 @@ class AppWindow(QMainWindow): def delCond(self): self.cond_param = None self.cond = None - self.ui.graphicsView.removeItem(self.Conductivity.mpl_line) - self.ui.graphicsView.removeItem(self.Conductivity.mpl_line_static) - del self.Conductivity - self.Conductivity = None + #self.ui.graphicsView.removeItem(self.Conductivity.data_curve_imag) + #self.ui.graphicsView.removeItem(self.Conductivity.mpl_line_static) + self.function_registry.unregister_function(self.Conductivity) + + #del self.Conductivity + #self.Conductivity = None + self.updatePlot() + + def addPowerComplex(self, pos): + #win = self.parameterWidget + #win.setWindowFlags(win.windowFlags() | Qt.WindowStaysOnTopHint) + self.statusBar().showMessage("Click on graph") + + power_complex = PowerComplex(pgPlotWidget=self.ui.graphicsView, limits=self.data.fit_limits) + power_complex.changedData.connect(self.updatePlot) + self.function_registry.register_function(power_complex) + + #print pos.x(), pos.y(),"test", 10**pos.x(),10**pos.y() + cond_par = [10**(pos.y() + pos.x())*2*N.pi , 1.0] + power_complex.setParameter(beta=cond_par) + self.parameterWidget.vlayout.insertWidget(1,power_complex.widget) + #power_complex.widget.ui.removeButton.clicked.connect(self.delPowerComplex) + self.parameterWidget.vlayout.update() + self.parameterWidget.showNormal() + self.parameterWidget.raise_() + + def delPowerComplex(self): + #self.ui.graphicsView.removeItem(self.PowerComplex.data_curve_real) + self.function_registry.unregister_function(self.PowerComplex) + + self.PowerComplex = None self.updatePlot() def addPeak(self, pos): - win = self.parameterWidget - win.setWindowFlags(win.windowFlags() | Qt.WindowStaysOnTopHint) + #win = self.parameterWidget + #win.setWindowFlags(win.windowFlags() | Qt.WindowStaysOnTopHint) id_list = [ self.peakBoxes[key] for key in self.peakBoxes.keys()] self.peakId = 1 while self.peakId in id_list: @@ -254,6 +294,8 @@ class AppWindow(QMainWindow): peak = Peak(id=self.peakId, mpl=self.ui.graphicsView, limits=self.data.fit_limits) # connect to delPeak peak.widget.ui.removeButton.clicked.connect(self.delPeak) + self.function_registry.register_function(peak) + #peak.widget.ui.removeButton.clicked.connect(self.delPeak) peak.changedData.connect(self.updatePlot) # delta_eps=(10**pos.y())*2, tau=1 / (2*N.pi*10**pos.x()), a=1, b=1 @@ -273,11 +315,14 @@ class AppWindow(QMainWindow): if peak.widget.isHidden(): self.ui.graphicsView.removeItem(peak.mpl_line) self.parameterWidget.vlayout.removeWidget(peak.widget) + self.function_registry.unregister_function(peak) + self.peakBoxes.pop(peak) self.parameterWidget.vlayout.update() self.updatePlot() def fitData(self, method): + if self.Conductivity != None: start_parameter = list(self.Conductivity.getParameter()) fixed_params = [i for i in self.Conductivity.getFixed()] @@ -353,7 +398,7 @@ class AppWindow(QMainWindow): def updatePlot(self): - + print self.function_registry.get_registered_functions() # assemble fit function funcs = ["static", "power"] p0 = self.Conductivity.getParameter() if self.Conductivity is not None else [0.0, 0.0, 1.0] diff --git a/QDSMain.py b/QDSMain.py index 424c546..68c38cb 100644 --- a/QDSMain.py +++ b/QDSMain.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'QDSMain.ui' # -# Created: Wed Mar 19 19:18:39 2014 +# Created: Wed Mar 19 20:57:16 2014 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -85,8 +85,13 @@ class Ui_MainWindow(object): icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/icons/save_fit.svg")), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.actionSave_FitResult.setIcon(icon2) self.actionSave_FitResult.setObjectName(_fromUtf8("actionSave_FitResult")) + self.actionAdd_PowerLaw = QtGui.QAction(MainWindow) + self.actionAdd_PowerLaw.setCheckable(True) + self.actionAdd_PowerLaw.setIcon(icon1) + self.actionAdd_PowerLaw.setObjectName(_fromUtf8("actionAdd_PowerLaw")) self.toolBar.addAction(self.actionAdd_Peak) self.toolBar.addAction(self.actionAdd_Cond) + self.toolBar.addAction(self.actionAdd_PowerLaw) self.toolBar.addAction(self.actionSave_FitResult) self.retranslateUi(MainWindow) @@ -100,6 +105,8 @@ class Ui_MainWindow(object): self.actionAdd_Cond.setToolTip(_translate("MainWindow", "Added Conductivity Term", None)) self.actionSave_FitResult.setText(_translate("MainWindow", "Save Fit", None)) self.actionSave_FitResult.setToolTip(_translate("MainWindow", "Save Fit Result", None)) + self.actionAdd_PowerLaw.setText(_translate("MainWindow", "Power Law", None)) + self.actionAdd_PowerLaw.setToolTip(_translate("MainWindow", "Add (complex) Power Law", None)) from pyqtgraph import PlotWidget import images_rc diff --git a/QDSMain.ui b/QDSMain.ui index a15fb74..12b74bc 100644 --- a/QDSMain.ui +++ b/QDSMain.ui @@ -65,6 +65,7 @@ + @@ -115,6 +116,21 @@ Save Fit Result + + + true + + + + :/icons/add_cond.svg:/icons/add_cond.svg + + + Power Law + + + Add (complex) Power Law + + diff --git a/data.py b/data.py index f0c7685..50446d8 100644 --- a/data.py +++ b/data.py @@ -7,7 +7,7 @@ import CustomWidgets import pyqtgraph as pg from PyQt4.QtCore import * -from mathlib import id_to_color, hn, FitFunctionCreator +from mathlib import id_to_color, hn, FitFunctionCreator, Functions class Data: @@ -78,26 +78,30 @@ class Data: class Conductivity(QObject): changedData = pyqtSignal() + def __init__(self, pgPlotWidget=None, limits=None): - def __init__(self, mpl=None, limits=None): QObject.__init__(self) super(Conductivity, self) self.widget = CustomWidgets.ConductivityWidget() self.widget.changedTable.connect(self.updateData) + + self.f=Functions() + myPen = pg.mkPen( style=Qt.DashLine, width=1) self.color=QColor("black") - self.mpl_line = pg.PlotDataItem(x=N.array([N.nan]), y=N.array([N.nan]), pen=myPen) - self.mpl_line_static = pg.PlotDataItem(x=N.array([N.nan]), y=N.array([N.nan]), pen=myPen) - self.mpl = mpl - self.mpl.addItem(self.mpl_line) - self.mpl.addItem(self.mpl_line_static) + self.data_curve_imag = pg.PlotDataItem(x=N.array([N.nan]), y=N.array([N.nan]), pen=myPen) + self.data_static_real = pg.PlotDataItem(x=N.array([N.nan]), y=N.array([N.nan]), pen=myPen) + self.pgPlotWidget = pgPlotWidget + self.pgPlotWidget.addItem(self.data_curve_imag) + self.pgPlotWidget.addItem(self.data_static_real) self.limits = limits self.frequency = None self.conductivity = None self.epsilon_static = None + self.id_string = "conductivity" def getParameter(self): p = self.widget.getTable() @@ -114,32 +118,91 @@ class Conductivity(QObject): def updateData(self): # get current axis limits x_min, x_max, y_min, y_max = self.limits - nu = N.logspace(N.log10(x_min), N.log10(x_max), 1024) + self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) eps_static, sigma, sigma_N = self.getParameter() - 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) + y = self.f.cond_cmplx([sigma, sigma_N], self.frequency) + self.epsilon_static = N.ones(len(self.frequency)) * eps_static - 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.conductivity = y[1] # imaginary part + + self.data_curve_imag.setData(x=self.frequency, y=self.conductivity, label="Cond.") + self.data_static_real.setData(x=self.frequency, y=self.epsilon_static) self.changedData.emit() def get_color(self): return self.color - def get_conductivity(self): + def get_data(self): return self.frequency, self.conductivity + def get_epsilon_static(self): return self.frequency, self.epsilon_static + def removeMe(self): + self.pg_plot_widget.removeItem(self.data_curve_imag) + self.pg_plot_widget.removeItem(self.data_static_real) + + +class PowerComplex(QObject): + changedData = pyqtSignal() + + def __init__(self, pgPlotWidget=None, limits=None): + QObject.__init__(self) + super(PowerComplex, self) + self.widget = CustomWidgets.PowerLawWidget() + self.widget.changedTable.connect(self.updateData) + self.widget.removeMe.connect(self.removeMe) + myPen = pg.mkPen( style=Qt.DashLine, width=1.5) + self.color=QColor("black") + + self.data_curve_real = pg.PlotDataItem(x=N.array([N.nan]), y=N.array([N.nan]), pen=myPen) + self.pg_plot_widget = pgPlotWidget + self.pg_plot_widget.addItem(self.data_curve_real) + + self.limits = limits + + self.frequency = None + self.powerlaw = None + + self.f = Functions() + self.id_string = 'power' + + def removeMe(self): + self.pg_plot_widget.removeItem(self.data_curve_real) + + def getParameter(self): + p = self.widget.getTable() + return p + + def getFixed(self): + p = self.widget.fixedParameter() + return p + + def setParameter(self, beta, sd_beta=None): + self.widget.updateTable(beta, sd_beta) + self.updateData() + + def updateData(self): + # get current axis limits + x_min, x_max, y_min, y_max = self.limits + + p = self.getParameter() + + self.frequency = N.logspace(N.log10(x_min), N.log10(x_max), 1024) + self.powerlaw = self.f.power_cmplx(p, self.frequency) [1] # imaginary part + self.data_curve_real.setData(x=self.frequency, y=self.powerlaw, label="Power Law") + self.changedData.emit() + + + def get_color(self): + return self.color + + def get_data(self): + return self.frequency, self.powerlaw + + def conductivity(p, nu): c = p[0] / (2 * N.pi * nu) ** p[1] return c @@ -164,6 +227,7 @@ class Peak(QObject): self.mpl.addItem(self.mpl_line) self.frequency = None self.epsilon = None + self.id_string = "hn" def getParameter(self): p = self.widget.peakParameter() @@ -183,7 +247,7 @@ class Peak(QObject): # x_min, x_max = self.mpl.canvas.axes.get_xlim() # y_min, y_max = self.mpl.canvas.axes.get_ylim() x_min,x_max, y_min, y_max = self.limits - nu = N.logspace(N.log10(x_min), N.log10(x_max), 2048) + nu = N.logspace(N.log10(x_min), N.log10(x_max), 1024) y = hn(self.getParameter(), nu) # clip data to axes limits #mask = (y < y_max) & (y > y_min) @@ -196,5 +260,6 @@ class Peak(QObject): def get_color(self): return self.color + def get_data(self): return self.frequency,self.epsilon \ No newline at end of file diff --git a/images_rc.py b/images_rc.py index a1a8097..737f0d9 100644 --- a/images_rc.py +++ b/images_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Mi. Mär. 19 19:18:38 2014 +# Created: Mi. Mär. 19 20:57:16 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 2cb2f78..7b620a1 100644 --- a/mathlib.py +++ b/mathlib.py @@ -135,6 +135,7 @@ def tau_peak(f, a, b): class Functions: def __init__(self): self.list = { + # provides functions: "id_string":(function, number_of_parameters) "hn":(self.hn_cmplx,4), "conductivity":(self.cond_cmplx,1), "power":(self.power_cmplx,2), @@ -213,4 +214,21 @@ def fit_odr_cmplx(x, y, p0, fixed, fcns): return fit.output +class FunctionRegister: + def __init__(self): + self.registry = {} + def register_function(self,obj): + id_string = obj.id_string + if self.registry.has_key(obj): + raise AssertionError,"The object is already registered! This should NOT happen" + self.registry[obj]=id_string + + def unregister_function(self, obj): + if self.registry.has_key(obj): + self.registry.pop(obj) + else: + raise AssertionError,"The object is not in the registry! This should NOT happen" + + def get_registered_functions(self): + return self.registry