def init_envectlayout(self): ''' In commandLayout - Initialization of the resulting energy vector section, with elow, ehigh and dE''' envect_box = QGroupBox() envect_box.setTitle("Energy vector parameters") envect_box.setSizePolicy(0, 0) envectlayout = QGridLayout() envectlayout.setSpacing(10) self.elow_le = QLineEdit("{:.2f}".format(cts.elow), self) self.ehigh_le = QLineEdit("{:.2f}".format(cts.ehigh), self) self.dE_le = QLineEdit(str(cts.dE), self) self.elow_le.returnPressed.connect(self.update_envect_fn) self.ehigh_le.returnPressed.connect(self.update_envect_fn) self.dE_le.returnPressed.connect(self.update_envect_fn) envectlayout.addWidget(QLabel("E low (eV)"), 0, 0) envectlayout.addWidget(self.elow_le, 1, 0) envectlayout.addWidget(QLabel("E high (eV)"), 0, 1) envectlayout.addWidget(self.ehigh_le, 1, 1) envectlayout.addWidget(QLabel("dE (eV)"), 0, 2) envectlayout.addWidget(self.dE_le, 1, 2) envect_box.setLayout(envectlayout) for widget in envect_box.children(): if isinstance(widget, QLabel) or isinstance(widget, QLineEdit): widget.setSizePolicy(0, 0) widget.setFixedSize(55, 20) self.commandlayout.addWidget(envect_box) self.update_envect_fn()
def _getVClampCtrlBox(self): vClampPanel = QGroupBox(self) self._vClampCtrlBox = vClampPanel self._holdingVLabel = QLabel("Holding Voltage (mV)", vClampPanel) self._holdingVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.holdingV']), vClampPanel) self._holdingTimeLabel = QLabel("Holding Time (ms)", vClampPanel) self._holdingTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.holdingT']), vClampPanel) self._prePulseVLabel = QLabel("Pre-pulse Voltage (mV)", vClampPanel) self._prePulseVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.prepulseV']), vClampPanel) self._prePulseTimeLabel = QLabel("Pre-pulse Time (ms)", vClampPanel) self._prePulseTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.prepulseT']), vClampPanel) self._clampVLabel = QLabel("Clamp Voltage (mV)", vClampPanel) self._clampVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.clampV']), vClampPanel) self._clampTimeLabel = QLabel("Clamp Time (ms)", vClampPanel) self._clampTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.clampT']), vClampPanel) for child in vClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(vClampPanel) layout.addWidget(self._holdingVLabel, 0, 0) layout.addWidget(self._holdingVEdit, 0, 1) layout.addWidget(self._holdingTimeLabel, 1, 0) layout.addWidget(self._holdingTimeEdit, 1, 1) layout.addWidget(self._prePulseVLabel, 2, 0) layout.addWidget(self._prePulseVEdit, 2, 1) layout.addWidget(self._prePulseTimeLabel,3,0) layout.addWidget(self._prePulseTimeEdit, 3, 1) layout.addWidget(self._clampVLabel, 4, 0) layout.addWidget(self._clampVEdit, 4, 1) layout.addWidget(self._clampTimeLabel, 5, 0) layout.addWidget(self._clampTimeEdit, 5, 1) layout.setRowStretch(6, 1.0) vClampPanel.setLayout(layout) return self._vClampCtrlBox
def init_rabbitlayout(self): ''' In commandLayout - Initialization of the "RABBIT" section''' rabbitlayout = QGridLayout() rabbit_box = QGroupBox("RABBIT", self) rabbit_box.setFixedSize(300, 100) self.normalrab_btn = QPushButton("Normal", self) self.FTcontrast_btn = QPushButton("FT/Contrast", self) self.rainbowrab_btn = QPushButton("Rainbow", self) self.clear_btn = QPushButton("Clear", self) self.normalrab_btn.clicked.connect(self.normalrab_lr) self.FTcontrast_btn.clicked.connect(self.FTcontrast_lr) self.rainbowrab_btn.clicked.connect(self.rainbowrab_lr) self.clear_btn.clicked.connect(self.clear_lr) rabbitlayout.addWidget(self.normalrab_btn, 0, 0) rabbitlayout.addWidget(self.FTcontrast_btn, 0, 1) rabbitlayout.addWidget(self.rainbowrab_btn, 0, 2) rabbitlayout.addWidget(self.clear_btn, 1, 0) rabbit_box.setLayout(rabbitlayout) for widget in rabbit_box.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) widget.setEnabled(False) self.commandLayout.addWidget(rabbit_box)
def selected_radio(self, groupBox: QGroupBox) -> str: # Get all radio buttons radioBtns = [ radio for radio in groupBox.children() if isinstance(radio, QRadioButton) ] # Find choosen radio and return text for radio in radioBtns: if radio.isChecked(): return radio.text()
class Dialog(QDialog): def __init__(self, form): super(Dialog, self).__init__() self.form = form self.formGroupBox = QGroupBox(form.identifier) self.create_form(form) buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) mainLayout = QVBoxLayout() mainLayout.addWidget(self.formGroupBox) mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) self.setWindowTitle("Form") def create_form(self, form): layout = QFormLayout() for question in form.block: show = question.evaluate_show_condition(self.form) question.pyqt5_render(layout, show) question.widget.onChange(form.update_show_condition_on_change) self.formGroupBox.setLayout(layout) # TODO unique file name @pyqtSlot() def accept(self): result = {} for child in self.formGroupBox.children()[1:]: question = self.form.find_question_of_widget(child) if question: result[question.identifier] = child.value() print(result) with open('out.json', 'w') as file: file.write(dumps(result)) self.close() QMessageBox.information( self, 'Submission', 'Your answers have been submitted successfully.', QMessageBox.Ok, QMessageBox.Ok) @pyqtSlot() def reject(self): self.hide()
def _getIClampCtrlBox(self): iClampPanel = QGroupBox(self) self._iClampCtrlBox = iClampPanel self._baseCurrentLabel = QLabel("Base Current Level (uA)", iClampPanel) self._baseCurrentEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.baseI']), iClampPanel) self._firstPulseLabel = QLabel("First Pulse Current (uA)", iClampPanel) self._firstPulseEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstI']), iClampPanel) self._firstDelayLabel = QLabel("First Onset Delay (ms)", iClampPanel) self._firstDelayEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstD']), iClampPanel) self._firstPulseWidthLabel = QLabel("First Pulse Width (ms)", iClampPanel) self._firstPulseWidthEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstT']), iClampPanel) self._secondPulseLabel = QLabel("Second Pulse Current (uA)", iClampPanel) self._secondPulseEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondI']), iClampPanel) self._secondDelayLabel = QLabel("Second Onset Delay (ms)", iClampPanel) self._secondDelayEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondD']), iClampPanel) self._secondPulseWidthLabel = QLabel("Second Pulse Width (ms)", iClampPanel) self._secondPulseWidthEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondT']), iClampPanel) self._pulseMode = QComboBox(iClampPanel) self._pulseMode.addItem("Single Pulse") self._pulseMode.addItem("Pulse Train") for child in iClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(iClampPanel) layout.addWidget(self._baseCurrentLabel, 0, 0) layout.addWidget(self._baseCurrentEdit, 0, 1) layout.addWidget(self._firstPulseLabel, 1, 0) layout.addWidget(self._firstPulseEdit, 1, 1) layout.addWidget(self._firstDelayLabel, 2, 0) layout.addWidget(self._firstDelayEdit, 2, 1) layout.addWidget(self._firstPulseWidthLabel, 3, 0) layout.addWidget(self._firstPulseWidthEdit, 3, 1) layout.addWidget(self._secondPulseLabel, 4, 0) layout.addWidget(self._secondPulseEdit, 4, 1) layout.addWidget(self._secondDelayLabel, 5, 0) layout.addWidget(self._secondDelayEdit, 5, 1) layout.addWidget(self._secondPulseWidthLabel, 6, 0) layout.addWidget(self._secondPulseWidthEdit, 6, 1) layout.addWidget(self._pulseMode, 7, 0, 1, 2) layout.setRowStretch(8, 1.0) # layout.setSizeConstraint(QLayout.SetFixedSize) iClampPanel.setLayout(layout) return self._iClampCtrlBox
def init_eparlayout(self): ''' In commandLayout - Initialization of the experimental parameters section: Retarding potential, TOF length, wavelength, gas and first harmonic expected to see.''' gases = cts.GASLIST epar_box = QGroupBox(self) epar_box.setTitle("Experimental parameters") epar_box.setSizePolicy(0, 0) eparlayout = QGridLayout() eparlayout.setSpacing(10) self.retpot_le = QLineEdit(str(cts.cur_Vp), self) self.toflength_le = QLineEdit(str(cts.cur_L), self) self.wvlength_le = QLineEdit(str(cts.lambda_start), self) self.gas_combo = QComboBox(self) self.gas_combo.addItems(gases) self.firstharm_le = QLineEdit(str(cts.first_harm), self) self.retpot_le.returnPressed.connect(self.update_cts_fn) self.toflength_le.returnPressed.connect(self.update_cts_fn) self.wvlength_le.returnPressed.connect(self.update_cts_fn) self.firstharm_le.returnPressed.connect(self.update_cts_fn) self.gas_combo.currentIndexChanged.connect(self.gas_combo_lr) eparlayout.addWidget(QLabel("Ret. pot. (V)"), 0, 0) eparlayout.addWidget(self.retpot_le, 1, 0) eparlayout.addWidget(QLabel("TOF length (m)"), 0, 1) eparlayout.addWidget(self.toflength_le, 1, 1) eparlayout.addWidget(QLabel("lambda (nm)"), 0, 2) eparlayout.addWidget(self.wvlength_le, 1, 2) eparlayout.addWidget(QLabel("gas"), 0, 3) eparlayout.addWidget(self.gas_combo, 1, 3) eparlayout.addWidget(QLabel("1st harm."), 0, 4) eparlayout.addWidget(self.firstharm_le, 1, 4) epar_box.setLayout(eparlayout) for widget in epar_box.children(): if isinstance(widget, QLineEdit) or isinstance(widget, QComboBox): widget.setFixedSize(50, 20) widget.setSizePolicy(0, 0) self.commandlayout.addWidget(epar_box)
def _getIClampCtrlBox(self): iClampPanel = QGroupBox(self) self._iClampCtrlBox = iClampPanel self._baseCurrentLabel = QLabel("Base Current Level (uA)",iClampPanel) self._baseCurrentEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.baseI']),iClampPanel) self._firstPulseLabel = QLabel("First Pulse Current (uA)", iClampPanel) self._firstPulseEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstI']), iClampPanel) self._firstDelayLabel = QLabel("First Onset Delay (ms)", iClampPanel) self._firstDelayEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstD']),iClampPanel) self._firstPulseWidthLabel = QLabel("First Pulse Width (ms)", iClampPanel) self._firstPulseWidthEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstT']), iClampPanel) self._secondPulseLabel = QLabel("Second Pulse Current (uA)", iClampPanel) self._secondPulseEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondI']), iClampPanel) self._secondDelayLabel = QLabel("Second Onset Delay (ms)", iClampPanel) self._secondDelayEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondD']),iClampPanel) self._secondPulseWidthLabel = QLabel("Second Pulse Width (ms)", iClampPanel) self._secondPulseWidthEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondT']), iClampPanel) self._pulseMode = QComboBox(iClampPanel) self._pulseMode.addItem("Single Pulse") self._pulseMode.addItem("Pulse Train") for child in iClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(iClampPanel) layout.addWidget(self._baseCurrentLabel, 0, 0) layout.addWidget(self._baseCurrentEdit, 0, 1) layout.addWidget(self._firstPulseLabel, 1, 0) layout.addWidget(self._firstPulseEdit, 1, 1) layout.addWidget(self._firstDelayLabel, 2, 0) layout.addWidget(self._firstDelayEdit, 2, 1) layout.addWidget(self._firstPulseWidthLabel, 3, 0) layout.addWidget(self._firstPulseWidthEdit, 3, 1) layout.addWidget(self._secondPulseLabel, 4, 0) layout.addWidget(self._secondPulseEdit, 4, 1) layout.addWidget(self._secondDelayLabel, 5, 0) layout.addWidget(self._secondDelayEdit, 5, 1) layout.addWidget(self._secondPulseWidthLabel, 6, 0) layout.addWidget(self._secondPulseWidthEdit, 6, 1) layout.addWidget(self._pulseMode, 7, 0, 1, 2) layout.setRowStretch(8, 1.0) # layout.setSizeConstraint(QLayout.SetFixedSize) iClampPanel.setLayout(layout) return self._iClampCtrlBox
def init_exportlayout(self): ''' In commandLayout - Initialization of the "Export" section''' exportlayout = QGridLayout() export_box = QGroupBox("Export", self) export_box.setFixedSize(300, 60) self.exportrab_btn = QPushButton("RABBIT", self) self.exportrab_btn.clicked.connect(self.exportrab_lr) exportlayout.addWidget(self.exportrab_btn) export_box.setLayout(exportlayout) for widget in export_box.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) widget.setEnabled(False) self.commandLayout.addWidget(export_box)
def _getVClampCtrlBox(self): vClampPanel = QGroupBox(self) self._vClampCtrlBox = vClampPanel self._holdingVLabel = QLabel("Holding Voltage (mV)", vClampPanel) self._holdingVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.holdingV']), vClampPanel) self._holdingTimeLabel = QLabel("Holding Time (ms)", vClampPanel) self._holdingTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.holdingT']), vClampPanel) self._prePulseVLabel = QLabel("Pre-pulse Voltage (mV)", vClampPanel) self._prePulseVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.prepulseV']), vClampPanel) self._prePulseTimeLabel = QLabel("Pre-pulse Time (ms)", vClampPanel) self._prePulseTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.prepulseT']), vClampPanel) self._clampVLabel = QLabel("Clamp Voltage (mV)", vClampPanel) self._clampVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.clampV']), vClampPanel) self._clampTimeLabel = QLabel("Clamp Time (ms)", vClampPanel) self._clampTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.clampT']), vClampPanel) for child in vClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(vClampPanel) layout.addWidget(self._holdingVLabel, 0, 0) layout.addWidget(self._holdingVEdit, 0, 1) layout.addWidget(self._holdingTimeLabel, 1, 0) layout.addWidget(self._holdingTimeEdit, 1, 1) layout.addWidget(self._prePulseVLabel, 2, 0) layout.addWidget(self._prePulseVEdit, 2, 1) layout.addWidget(self._prePulseTimeLabel, 3, 0) layout.addWidget(self._prePulseTimeEdit, 3, 1) layout.addWidget(self._clampVLabel, 4, 0) layout.addWidget(self._clampVEdit, 4, 1) layout.addWidget(self._clampTimeLabel, 5, 0) layout.addWidget(self._clampTimeEdit, 5, 1) layout.setRowStretch(6, 1.0) vClampPanel.setLayout(layout) return self._vClampCtrlBox
def init_fitparlayout(self): ''' In commandLayout - Initialization of the fit parameter section with a, t0 and c. First line: guess values calculated from the experimental parameters. Second line: fitted values.''' fitpar_box = QGroupBox() fitpar_box.setTitle("Calibration parameters") fitpar_box.setSizePolicy(0, 0) fitparlayout = QGridLayout() fitparlayout.setSpacing(10) self.aguess_lb = QLabel(self) self.afit_lb = QLabel(self) self.t0guess_lb = QLabel(self) self.t0fit_lb = QLabel(self) self.cguess_lb = QLabel(self) self.cfit_lb = QLabel(self) fitparlayout.addWidget(QLabel("a"), 0, 0) fitparlayout.addWidget(self.aguess_lb, 1, 0) fitparlayout.addWidget(self.afit_lb, 2, 0) fitparlayout.addWidget(QLabel("t0"), 0, 1) fitparlayout.addWidget(self.t0guess_lb, 1, 1) fitparlayout.addWidget(self.t0fit_lb, 2, 1) fitparlayout.addWidget(QLabel("c"), 0, 2) fitparlayout.addWidget(self.cguess_lb, 1, 2) fitparlayout.addWidget(self.cfit_lb, 2, 2) fitparlayout.addWidget(QLabel("guess"), 1, 3) fitparlayout.addWidget(QLabel("calc"), 2, 3) text = "q=a*1/(t-t0)²+c" fitparlayout.addWidget(QLabel(text), 1, 4) fitpar_box.setLayout(fitparlayout) for widget in fitpar_box.children(): if isinstance(widget, QLabel) and widget.text() != text: widget.setSizePolicy(0, 0) widget.setFixedSize(55, 15) self.commandlayout.addWidget(fitpar_box)
def init_plotbtnlayout(self): ''' In commandLayout - Initialization of the "Plot" section''' plotbtnlayout = QGridLayout() plotbtn_box = QGroupBox("Plot", self) plotbtn_box.setFixedSize(300, 60) self.plotSBvsdelay_btn = QPushButton("SB vs delay", self) self.plotPulseInTime_btn = QPushButton("Pulse in time", self) self.plotSBvsdelay_btn.clicked.connect(self.plotSBvsdelay_lr) self.plotPulseInTime_btn.clicked.connect(self.plotPulseInTime_lr) plotbtnlayout.addWidget(self.plotSBvsdelay_btn, 0, 0) plotbtnlayout.addWidget(self.plotPulseInTime_btn, 0, 1) plotbtn_box.setLayout(plotbtnlayout) for widget in plotbtn_box.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) widget.setEnabled(False) self.commandLayout.addWidget(plotbtn_box)
def init_sigtreatmentlayout(self): ''' In commandLayout - Initialization of the "Signal Treatment" section''' sigtreatment_box = QGroupBox("Signal Treatment", self) sigtreatmentlayout = QGridLayout() sigtreatment_box.setFixedSize(300, 100) self.smooth_btn = QPushButton("smooth", self) self.smooth_le = QLineEdit("2", self) self.normalize_btn = QPushButton("normalize", self) self.subXUV_btn = QPushButton("substract XUV", self) self.selectbands_btn = QPushButton("select bands") self.bandsnb_le = QLineEdit(str(cts.bandsnb), self) self.smooth_btn.clicked.connect(self.smoothrab_lr) self.normalize_btn.clicked.connect(self.normalizerab_lr) self.selectbands_btn.clicked.connect(self.selectbands_lr) self.bandsnb_le.returnPressed.connect(self.bandsnb_lr) self.subXUV_btn.clicked.connect(self.subXUV_lr) sigtreatmentlayout.addWidget(self.smooth_btn, 0, 0) sigtreatmentlayout.addWidget(self.smooth_le, 1, 0) sigtreatmentlayout.addWidget(self.normalize_btn, 0, 1) sigtreatmentlayout.addWidget(self.subXUV_btn, 1, 1) sigtreatmentlayout.addWidget(self.selectbands_btn, 0, 2) sigtreatmentlayout.addWidget(self.bandsnb_le, 1, 2) sigtreatment_box.setLayout(sigtreatmentlayout) for widget in sigtreatment_box.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) widget.setEnabled(False) if isinstance(widget, QLineEdit): widget.setFixedSize(55, 20) self.commandLayout.addWidget(sigtreatment_box)
def init_importlayout(self): ''' In commandLayout - Initialization of the "Import" section''' Importlayout = QGridLayout() Importlayout.setSpacing(10) Import_box = QGroupBox(self) Import_box.setTitle("Import") Import_box.setFixedSize(300, 100) self.importcalib_btn = QPushButton("calib", self) self.importdata_btn = QPushButton("data", self) self.importXUV_btn = QPushButton("XUV only", self) self.importrabparam_btn = QPushButton("RABBIT param", self) self.importrab_btn = QPushButton("RABBIT", self) self.importcalib_btn.clicked.connect(self.importcalib_lr) self.importdata_btn.clicked.connect(self.importdata_lr) self.importXUV_btn.clicked.connect(self.importXUV_lr) self.importrabparam_btn.clicked.connect(self.importrabparam_lr) self.importrab_btn.clicked.connect(self.importrab_lr) Importlayout.addWidget(self.importcalib_btn, 0, 0) Importlayout.addWidget(self.importdata_btn, 1, 0) Importlayout.addWidget(self.importXUV_btn, 0, 1) Importlayout.addWidget(self.importrabparam_btn, 0, 2) Importlayout.addWidget(self.importrab_btn, 1, 2) Import_box.setLayout(Importlayout) self.commandLayout.addWidget(Import_box) for widget in Import_box.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) widget.setEnabled(False) self.importcalib_btn.setEnabled(True) self.importrabparam_btn.setEnabled(True)
class SquidGui( QMainWindow ): defaults = {} defaults.update(SquidAxon.defaults) defaults.update(ClampCircuit.defaults) defaults.update({'runtime': 50.0, 'simdt': 0.01, 'plotdt': 0.1, 'vclamp.holdingV': 0.0, 'vclamp.holdingT': 10.0, 'vclamp.prepulseV': 0.0, 'vclamp.prepulseT': 0.0, 'vclamp.clampV': 50.0, 'vclamp.clampT': 20.0, 'iclamp.baseI': 0.0, 'iclamp.firstI': 0.1, 'iclamp.firstT': 40.0, 'iclamp.firstD': 5.0, 'iclamp.secondI': 0.0, 'iclamp.secondT': 0.0, 'iclamp.secondD': 0.0 }) def __init__(self, *args): QMainWindow.__init__(self, *args) self.squid_setup = SquidSetup() self._plotdt = SquidGui.defaults['plotdt'] self._plot_dict = defaultdict(list) self.setWindowTitle('Squid Axon simulation') self.setDockNestingEnabled(True) self._createRunControl() self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._runControlDock) self._runControlDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self._createChannelControl() self._channelCtrlBox.setWindowTitle('Channel properties') self._channelControlDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._channelControlDock) self._createElectronicsControl() self._electronicsDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self._electronicsDock.setWindowTitle('Electronics') self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._electronicsDock) self._createPlotWidget() self.setCentralWidget(self._plotWidget) self._createStatePlotWidget() self._createHelpMessage() self._helpWindow.setVisible(False) self._statePlotWidget.setWindowFlags(QtCore.Qt.Window) self._statePlotWidget.setWindowTitle('State plot') self._initActions() self._createRunToolBar() self._createPlotToolBar() def getFloatInput(self, widget, name): try: return float(str(widget.text())) except ValueError: QMessageBox.critical(self, 'Invalid input', 'Please enter a valid number for {}'.format(name)) raise def _createPlotWidget(self): self._plotWidget = QWidget() self._plotFigure = Figure() self._plotCanvas = FigureCanvas(self._plotFigure) self._plotCanvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._plotCanvas.updateGeometry() self._plotCanvas.setParent(self._plotWidget) self._plotCanvas.mpl_connect('scroll_event', self._onScroll) self._plotFigure.set_canvas(self._plotCanvas) # Vm and command voltage go in the same subplot self._vm_axes = self._plotFigure.add_subplot(2,2,1, title='Membrane potential') self._vm_axes.set_ylim(-20.0, 120.0) # Channel conductances go to the same subplot self._g_axes = self._plotFigure.add_subplot(2,2,2, title='Channel conductance') self._g_axes.set_ylim(0.0, 0.5) # Injection current for Vclamp/Iclamp go to the same subplot self._im_axes = self._plotFigure.add_subplot(2,2,3, title='Injection current') self._im_axes.set_ylim(-0.5, 0.5) # Channel currents go to the same subplot self._i_axes = self._plotFigure.add_subplot(2,2,4, title='Channel current') self._i_axes.set_ylim(-10, 10) for axis in self._plotFigure.axes: axis.set_autoscale_on(False) layout = QVBoxLayout() layout.addWidget(self._plotCanvas) self._plotNavigator = NavigationToolbar(self._plotCanvas, self._plotWidget) layout.addWidget(self._plotNavigator) self._plotWidget.setLayout(layout) def _createStatePlotWidget(self): self._statePlotWidget = QWidget() self._statePlotFigure = Figure() self._statePlotCanvas = FigureCanvas(self._statePlotFigure) self._statePlotCanvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._statePlotCanvas.updateGeometry() self._statePlotCanvas.setParent(self._statePlotWidget) self._statePlotFigure.set_canvas(self._statePlotCanvas) self._statePlotFigure.subplots_adjust(hspace=0.5) self._statePlotAxes = self._statePlotFigure.add_subplot(2,1,1, title='State plot') self._state_plot, = self._statePlotAxes.plot([], [], label='state') self._activationParamAxes = self._statePlotFigure.add_subplot(2,1,2, title='H-H activation parameters vs time') self._activationParamAxes.set_xlabel('Time (ms)') #for axis in self._plotFigure.axes: # axis.autoscale(False) self._stateplot_xvar_label = QLabel('Variable on X-axis') self._stateplot_xvar_combo = QComboBox() self._stateplot_xvar_combo.addItems(['V', 'm', 'n', 'h']) self._stateplot_xvar_combo.setCurrentText('V') self._stateplot_xvar_combo.setEditable(False) self._stateplot_xvar_combo.currentIndexChanged[str].connect( self._statePlotXSlot ) self._stateplot_yvar_label = QLabel('Variable on Y-axis') self._stateplot_yvar_combo = QComboBox() self._stateplot_yvar_combo.addItems(['V', 'm', 'n', 'h']) self._stateplot_yvar_combo.setCurrentIndex(2) self._stateplot_yvar_combo.setEditable(False) self._stateplot_yvar_combo.currentIndexChanged[str].connect(self._statePlotYSlot) self._statePlotNavigator = NavigationToolbar(self._statePlotCanvas, self._statePlotWidget) frame = QFrame() frame.setFrameStyle(QFrame.StyledPanel + QFrame.Raised) layout = QHBoxLayout() layout.addWidget(self._stateplot_xvar_label) layout.addWidget(self._stateplot_xvar_combo) layout.addWidget(self._stateplot_yvar_label) layout.addWidget(self._stateplot_yvar_combo) frame.setLayout(layout) self._closeStatePlotAction = QAction('Close', self) self._closeStatePlotAction.triggered.connect(self._statePlotWidget.close) self._closeStatePlotButton = QToolButton() self._closeStatePlotButton.setDefaultAction(self._closeStatePlotAction) layout = QVBoxLayout() layout.addWidget(frame) layout.addWidget(self._statePlotCanvas) layout.addWidget(self._statePlotNavigator) layout.addWidget(self._closeStatePlotButton) self._statePlotWidget.setLayout(layout) # Setting the close event so that when the help window is # closed the ``State plot`` button becomes unchecked self._statePlotWidget.closeEvent = lambda event: self._showStatePlotAction.setChecked(False) def _createRunControl(self): self._runControlBox = QGroupBox(self) self._runControlBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self._runTimeLabel = QLabel("Run time (ms)", self._runControlBox) self._simTimeStepLabel = QLabel("Simulation time step (ms)", self._runControlBox) self._runTimeEdit = QLineEdit('%g' % (SquidGui.defaults['runtime']), self._runControlBox) set_default_line_edit_size(self._runTimeEdit) self._simTimeStepEdit = QLineEdit('%g' % (SquidGui.defaults['simdt']), self._runControlBox) set_default_line_edit_size(self._simTimeStepEdit) layout = QGridLayout() layout.addWidget(self._runTimeLabel, 0, 0) layout.addWidget(self._runTimeEdit, 0, 1) layout.addWidget(self._simTimeStepLabel, 1, 0) layout.addWidget(self._simTimeStepEdit, 1, 1) layout.setColumnStretch(2, 1.0) layout.setRowStretch(2, 1.0) self._runControlBox.setLayout(layout) self._runControlDock = QDockWidget('Simulation', self) self._runControlDock.setWidget(self._runControlBox) def _createChannelControl(self): self._channelControlDock = QDockWidget('Channels', self) self._channelCtrlBox = QGroupBox(self) self._naConductanceToggle = QCheckBox('Block Na+ channel', self._channelCtrlBox) self._naConductanceToggle.setToolTip('<html>%s</html>' % (tooltip_NaChan)) self._kConductanceToggle = QCheckBox('Block K+ channel', self._channelCtrlBox) self._kConductanceToggle.setToolTip('<html>%s</html>' % (tooltip_KChan)) self._kOutLabel = QLabel('[K+]out (mM)', self._channelCtrlBox) self._kOutEdit = QLineEdit('%g' % (self.squid_setup.squid_axon.K_out), self._channelCtrlBox) self._kOutLabel.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._kOutEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._kOutEdit) self._naOutLabel = QLabel('[Na+]out (mM)', self._channelCtrlBox) self._naOutEdit = QLineEdit('%g' % (self.squid_setup.squid_axon.Na_out), self._channelCtrlBox) self._naOutLabel.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._naOutEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._naOutEdit) self._kInLabel = QLabel('[K+]in (mM)', self._channelCtrlBox) self._kInEdit = QLineEdit('%g' % (self.squid_setup.squid_axon.K_in), self._channelCtrlBox) self._kInEdit.setToolTip(tooltip_Nernst) self._naInLabel = QLabel('[Na+]in (mM)', self._channelCtrlBox) self._naInEdit = QLineEdit('%g' % (self.squid_setup.squid_axon.Na_in), self._channelCtrlBox) self._naInEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._temperatureLabel = QLabel('Temperature (C)', self._channelCtrlBox) self._temperatureEdit = QLineEdit('%g' % (self.defaults['temperature'] - CELSIUS_TO_KELVIN), self._channelCtrlBox) self._temperatureEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._temperatureEdit) for child in self._channelCtrlBox.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(self._channelCtrlBox) layout.addWidget(self._naConductanceToggle, 0, 0) layout.addWidget(self._kConductanceToggle, 1, 0) layout.addWidget(self._naOutLabel, 2, 0) layout.addWidget(self._naOutEdit, 2, 1) layout.addWidget(self._naInLabel, 3, 0) layout.addWidget(self._naInEdit, 3, 1) layout.addWidget(self._kOutLabel, 4, 0) layout.addWidget(self._kOutEdit, 4, 1) layout.addWidget(self._kInLabel, 5, 0) layout.addWidget(self._kInEdit, 5, 1) layout.addWidget(self._temperatureLabel, 6, 0) layout.addWidget(self._temperatureEdit, 6, 1) layout.setRowStretch(7, 1.0) self._channelCtrlBox.setLayout(layout) self._channelControlDock.setWidget(self._channelCtrlBox) return self._channelCtrlBox def __get_stateplot_data(self, name): data = [] if name == 'V': data = self.squid_setup.vm_table.vector elif name == 'm': data = self.squid_setup.m_table.vector elif name == 'h': data = self.squid_setup.h_table.vector elif name == 'n': data = self.squid_setup.n_table.vector else: raise ValueError('Unrecognized selection: %s' % name ) return numpy.asarray(data) def _statePlotYSlot(self, selectedItem): ydata = self.__get_stateplot_data(str(selectedItem)) self._state_plot.set_ydata(ydata) self._statePlotAxes.set_ylabel(selectedItem) if str(selectedItem) == 'V': self._statePlotAxes.set_ylim(-20, 120) else: self._statePlotAxes.set_ylim(0, 1) self._statePlotCanvas.draw() def _statePlotXSlot(self, selectedItem): xdata = self.__get_stateplot_data(str(selectedItem)) self._state_plot.set_xdata(xdata) self._statePlotAxes.set_xlabel(selectedItem) if str(selectedItem) == 'V': self._statePlotAxes.set_xlim(-20, 120) else: self._statePlotAxes.set_xlim(0, 1) self._statePlotCanvas.draw() def _createElectronicsControl(self): """Creates a tabbed widget of voltage clamp and current clamp controls""" self._electronicsTab = QTabWidget(self) self._electronicsTab.addTab(self._getIClampCtrlBox(), 'Current clamp') self._electronicsTab.addTab(self._getVClampCtrlBox(), 'Voltage clamp') self._electronicsDock = QDockWidget(self) self._electronicsDock.setWidget(self._electronicsTab) def _getVClampCtrlBox(self): vClampPanel = QGroupBox(self) self._vClampCtrlBox = vClampPanel self._holdingVLabel = QLabel("Holding Voltage (mV)", vClampPanel) self._holdingVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.holdingV']), vClampPanel) self._holdingTimeLabel = QLabel("Holding Time (ms)", vClampPanel) self._holdingTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.holdingT']), vClampPanel) self._prePulseVLabel = QLabel("Pre-pulse Voltage (mV)", vClampPanel) self._prePulseVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.prepulseV']), vClampPanel) self._prePulseTimeLabel = QLabel("Pre-pulse Time (ms)", vClampPanel) self._prePulseTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.prepulseT']), vClampPanel) self._clampVLabel = QLabel("Clamp Voltage (mV)", vClampPanel) self._clampVEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.clampV']), vClampPanel) self._clampTimeLabel = QLabel("Clamp Time (ms)", vClampPanel) self._clampTimeEdit = QLineEdit('%g' % (SquidGui.defaults['vclamp.clampT']), vClampPanel) for child in vClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(vClampPanel) layout.addWidget(self._holdingVLabel, 0, 0) layout.addWidget(self._holdingVEdit, 0, 1) layout.addWidget(self._holdingTimeLabel, 1, 0) layout.addWidget(self._holdingTimeEdit, 1, 1) layout.addWidget(self._prePulseVLabel, 2, 0) layout.addWidget(self._prePulseVEdit, 2, 1) layout.addWidget(self._prePulseTimeLabel,3,0) layout.addWidget(self._prePulseTimeEdit, 3, 1) layout.addWidget(self._clampVLabel, 4, 0) layout.addWidget(self._clampVEdit, 4, 1) layout.addWidget(self._clampTimeLabel, 5, 0) layout.addWidget(self._clampTimeEdit, 5, 1) layout.setRowStretch(6, 1.0) vClampPanel.setLayout(layout) return self._vClampCtrlBox def _getIClampCtrlBox(self): iClampPanel = QGroupBox(self) self._iClampCtrlBox = iClampPanel self._baseCurrentLabel = QLabel("Base Current Level (uA)",iClampPanel) self._baseCurrentEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.baseI']),iClampPanel) self._firstPulseLabel = QLabel("First Pulse Current (uA)", iClampPanel) self._firstPulseEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstI']), iClampPanel) self._firstDelayLabel = QLabel("First Onset Delay (ms)", iClampPanel) self._firstDelayEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstD']),iClampPanel) self._firstPulseWidthLabel = QLabel("First Pulse Width (ms)", iClampPanel) self._firstPulseWidthEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.firstT']), iClampPanel) self._secondPulseLabel = QLabel("Second Pulse Current (uA)", iClampPanel) self._secondPulseEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondI']), iClampPanel) self._secondDelayLabel = QLabel("Second Onset Delay (ms)", iClampPanel) self._secondDelayEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondD']),iClampPanel) self._secondPulseWidthLabel = QLabel("Second Pulse Width (ms)", iClampPanel) self._secondPulseWidthEdit = QLineEdit('%g' % (SquidGui.defaults['iclamp.secondT']), iClampPanel) self._pulseMode = QComboBox(iClampPanel) self._pulseMode.addItem("Single Pulse") self._pulseMode.addItem("Pulse Train") for child in iClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(iClampPanel) layout.addWidget(self._baseCurrentLabel, 0, 0) layout.addWidget(self._baseCurrentEdit, 0, 1) layout.addWidget(self._firstPulseLabel, 1, 0) layout.addWidget(self._firstPulseEdit, 1, 1) layout.addWidget(self._firstDelayLabel, 2, 0) layout.addWidget(self._firstDelayEdit, 2, 1) layout.addWidget(self._firstPulseWidthLabel, 3, 0) layout.addWidget(self._firstPulseWidthEdit, 3, 1) layout.addWidget(self._secondPulseLabel, 4, 0) layout.addWidget(self._secondPulseEdit, 4, 1) layout.addWidget(self._secondDelayLabel, 5, 0) layout.addWidget(self._secondDelayEdit, 5, 1) layout.addWidget(self._secondPulseWidthLabel, 6, 0) layout.addWidget(self._secondPulseWidthEdit, 6, 1) layout.addWidget(self._pulseMode, 7, 0, 1, 2) layout.setRowStretch(8, 1.0) # layout.setSizeConstraint(QLayout.SetFixedSize) iClampPanel.setLayout(layout) return self._iClampCtrlBox def _overlayPlots(self, overlay): if not overlay: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): title = axis.get_title() axis.clear() axis.set_title(title) suffix = '' else: suffix = '_%d' % (len(self._plot_dict['vm'])) self._vm_axes.set_xlim(0.0, self._runtime) self._g_axes.set_xlim(0.0, self._runtime) self._im_axes.set_xlim(0.0, self._runtime) self._i_axes.set_xlim(0.0, self._runtime) self._vm_plot, = self._vm_axes.plot([], [], label='Vm%s'%(suffix)) self._plot_dict['vm'].append(self._vm_plot) self._command_plot, = self._vm_axes.plot([], [], label='command%s'%(suffix)) self._plot_dict['command'].append(self._command_plot) # Channel conductances go to the same subplot self._gna_plot, = self._g_axes.plot([], [], label='Na%s'%(suffix)) self._plot_dict['gna'].append(self._gna_plot) self._gk_plot, = self._g_axes.plot([], [], label='K%s'%(suffix)) self._plot_dict['gk'].append(self._gk_plot) # Injection current for Vclamp/Iclamp go to the same subplot self._iclamp_plot, = self._im_axes.plot([], [], label='Iclamp%s'%(suffix)) self._vclamp_plot, = self._im_axes.plot([], [], label='Vclamp%s'%(suffix)) self._plot_dict['iclamp'].append(self._iclamp_plot) self._plot_dict['vclamp'].append(self._vclamp_plot) # Channel currents go to the same subplot self._ina_plot, = self._i_axes.plot([], [], label='Na%s'%(suffix)) self._plot_dict['ina'].append(self._ina_plot) self._ik_plot, = self._i_axes.plot([], [], label='K%s'%(suffix)) self._plot_dict['ik'].append(self._ik_plot) # self._i_axes.legend() # State plots self._state_plot, = self._statePlotAxes.plot([], [], label='state%s'%(suffix)) self._plot_dict['state'].append(self._state_plot) self._m_plot, = self._activationParamAxes.plot([],[], label='m%s'%(suffix)) self._h_plot, = self._activationParamAxes.plot([], [], label='h%s'%(suffix)) self._n_plot, = self._activationParamAxes.plot([], [], label='n%s'%(suffix)) self._plot_dict['m'].append(self._m_plot) self._plot_dict['h'].append(self._h_plot) self._plot_dict['n'].append(self._n_plot) if self._showLegendAction.isChecked(): for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend() def _updateAllPlots(self): self._updatePlots() self._updateStatePlot() def _updatePlots(self): if len(self.squid_setup.vm_table.vector) <= 0: return vm = numpy.asarray(self.squid_setup.vm_table.vector) cmd = numpy.asarray(self.squid_setup.cmd_table.vector) ik = numpy.asarray(self.squid_setup.ik_table.vector) ina = numpy.asarray(self.squid_setup.ina_table.vector) iclamp = numpy.asarray(self.squid_setup.iclamp_table.vector) vclamp = numpy.asarray(self.squid_setup.vclamp_table.vector) gk = numpy.asarray(self.squid_setup.gk_table.vector) gna = numpy.asarray(self.squid_setup.gna_table.vector) time_series = numpy.linspace(0, self._plotdt * len(vm), len(vm)) self._vm_plot.set_data(time_series, vm) time_series = numpy.linspace(0, self._plotdt * len(cmd), len(cmd)) self._command_plot.set_data(time_series, cmd) time_series = numpy.linspace(0, self._plotdt * len(ik), len(ik)) self._ik_plot.set_data(time_series, ik) time_series = numpy.linspace(0, self._plotdt * len(ina), len(ina)) self._ina_plot.set_data(time_series, ina) time_series = numpy.linspace(0, self._plotdt * len(iclamp), len(iclamp)) self._iclamp_plot.set_data(time_series, iclamp) time_series = numpy.linspace(0, self._plotdt * len(vclamp), len(vclamp)) self._vclamp_plot.set_data(time_series, vclamp) time_series = numpy.linspace(0, self._plotdt * len(gk), len(gk)) self._gk_plot.set_data(time_series, gk) time_series = numpy.linspace(0, self._plotdt * len(gna), len(gna)) self._gna_plot.set_data(time_series, gna) # self._vm_axes.margins(y=0.1) # self._g_axes.margin(y=0.1) # self._im_axes.margins(y=0.1) # self._i_axes.margins(y=0.1) if self._autoscaleAction.isChecked(): for axis in self._plotFigure.axes: axis.relim() axis.margins(0.1, 0.1) axis.autoscale_view(tight=True) else: self._vm_axes.set_ylim(-20.0, 120.0) self._g_axes.set_ylim(0.0, 0.5) self._im_axes.set_ylim(-0.5, 0.5) self._i_axes.set_ylim(-10, 10) self._vm_axes.set_xlim(0.0, time_series[-1]) self._g_axes.set_xlim(0.0, time_series[-1]) self._im_axes.set_xlim(0.0, time_series[-1]) self._i_axes.set_xlim(0.0, time_series[-1]) self._plotCanvas.draw() def _updateStatePlot(self): if len(self.squid_setup.vm_table.vector) <= 0: return sx = str(self._stateplot_xvar_combo.currentText()) sy = str(self._stateplot_yvar_combo.currentText()) xdata = self.__get_stateplot_data(sx) ydata = self.__get_stateplot_data(sy) minlen = min(len(xdata), len(ydata)) self._state_plot.set_data(xdata[:minlen], ydata[:minlen]) self._statePlotAxes.set_xlabel(sx) self._statePlotAxes.set_ylabel(sy) if sx == 'V': self._statePlotAxes.set_xlim(-20, 120) else: self._statePlotAxes.set_xlim(0, 1) if sy == 'V': self._statePlotAxes.set_ylim(-20, 120) else: self._statePlotAxes.set_ylim(0, 1) self._activationParamAxes.set_xlim(0, self._runtime) m = self.__get_stateplot_data('m') n = self.__get_stateplot_data('n') h = self.__get_stateplot_data('h') time_series = numpy.linspace(0, self._plotdt*len(m), len(m)) self._m_plot.set_data(time_series, m) time_series = numpy.linspace(0, self._plotdt*len(h), len(h)) self._h_plot.set_data(time_series, h) time_series = numpy.linspace(0, self._plotdt*len(n), len(n)) self._n_plot.set_data(time_series, n) if self._autoscaleAction.isChecked(): for axis in self._statePlotFigure.axes: axis.relim() axis.set_autoscale_on(True) axis.autoscale_view(True) self._statePlotCanvas.draw() def _runSlot(self): if moose.isRunning(): print('Stopping simulation in progress ...') moose.stop() self._runtime = self.getFloatInput(self._runTimeEdit, self._runTimeLabel.text()) self._overlayPlots(self._overlayAction.isChecked()) self._simdt = self.getFloatInput(self._simTimeStepEdit, self._simTimeStepLabel.text()) clampMode = None singlePulse = True if self._electronicsTab.currentWidget() == self._vClampCtrlBox: clampMode = 'vclamp' baseLevel = self.getFloatInput(self._holdingVEdit, self._holdingVLabel.text()) firstDelay = self.getFloatInput(self._holdingTimeEdit, self._holdingTimeLabel.text()) firstWidth = self.getFloatInput(self._prePulseTimeEdit, self._prePulseTimeLabel.text()) firstLevel = self.getFloatInput(self._prePulseVEdit, self._prePulseVLabel.text()) secondDelay = firstWidth secondWidth = self.getFloatInput(self._clampTimeEdit, self._clampTimeLabel.text()) secondLevel = self.getFloatInput(self._clampVEdit, self._clampVLabel.text()) if not self._autoscaleAction.isChecked(): self._im_axes.set_ylim(-10.0, 10.0) else: clampMode = 'iclamp' baseLevel = self.getFloatInput(self._baseCurrentEdit, self._baseCurrentLabel.text()) firstDelay = self.getFloatInput(self._firstDelayEdit, self._firstDelayLabel.text()) firstWidth = self.getFloatInput(self._firstPulseWidthEdit, self._firstPulseWidthLabel.text()) firstLevel = self.getFloatInput(self._firstPulseEdit, self._firstPulseLabel.text()) secondDelay = self.getFloatInput(self._secondDelayEdit, self._secondDelayLabel.text()) secondLevel = self.getFloatInput(self._secondPulseEdit, self._secondPulseLabel.text()) secondWidth = self.getFloatInput(self._secondPulseWidthEdit, self._secondPulseWidthLabel.text()) singlePulse = (self._pulseMode.currentIndex() == 0) if not self._autoscaleAction.isChecked(): self._im_axes.set_ylim(-0.4, 0.4) self.squid_setup.clamp_ckt.configure_pulses(baseLevel=baseLevel, firstDelay=firstDelay, firstWidth=firstWidth, firstLevel=firstLevel, secondDelay=secondDelay, secondWidth=secondWidth, secondLevel=secondLevel, singlePulse=singlePulse) if self._kConductanceToggle.isChecked(): self.squid_setup.squid_axon.specific_gK = 0.0 else: self.squid_setup.squid_axon.specific_gK = SquidAxon.defaults['specific_gK'] if self._naConductanceToggle.isChecked(): self.squid_setup.squid_axon.specific_gNa = 0.0 else: self.squid_setup.squid_axon.specific_gNa = SquidAxon.defaults['specific_gNa'] self.squid_setup.squid_axon.celsius = self.getFloatInput(self._temperatureEdit, self._temperatureLabel.text()) self.squid_setup.squid_axon.K_out = self.getFloatInput(self._kOutEdit, self._kOutLabel.text()) self.squid_setup.squid_axon.Na_out = self.getFloatInput(self._naOutEdit, self._naOutLabel.text()) self.squid_setup.squid_axon.K_in = self.getFloatInput(self._kInEdit, self._kInLabel.text()) self.squid_setup.squid_axon.Na_in = self.getFloatInput(self._naInEdit, self._naInLabel.text()) self.squid_setup.squid_axon.updateEk() self.squid_setup.schedule(self._simdt, self._plotdt, clampMode) # The following line is for use with Qthread self.squid_setup.run(self._runtime) self._updateAllPlots() def _toggleDocking(self, on): self._channelControlDock.setFloating(on) self._electronicsDock.setFloating(on) self._runControlDock.setFloating(on) def _restoreDocks(self): self._channelControlDock.setVisible(True) self._electronicsDock.setVisible(True) self._runControlDock.setVisible(True) def _initActions(self): self._runAction = QAction(self.tr('Run'), self) self._runAction.setShortcut(self.tr('F5')) self._runAction.setToolTip('Run simulation (F5)') self._runAction.triggered.connect( self._runSlot) self._resetToDefaultsAction = QAction(self.tr('Restore defaults'), self) self._resetToDefaultsAction.setToolTip('Reset all settings to their default values') self._resetToDefaultsAction.triggered.connect( self._useDefaults) self._showLegendAction = QAction(self.tr('Display legend'), self) self._showLegendAction.setCheckable(True) self._showLegendAction.toggled.connect(self._showLegend) self._showStatePlotAction = QAction(self.tr('State plot'), self) self._showStatePlotAction.setCheckable(True) self._showStatePlotAction.setChecked(False) self._showStatePlotAction.toggled.connect(self._statePlotWidget.setVisible) self._autoscaleAction = QAction(self.tr('Auto-scale plots'), self) self._autoscaleAction.setCheckable(True) self._autoscaleAction.setChecked(False) self._autoscaleAction.toggled.connect(self._autoscale) self._overlayAction = QAction('Overlay plots', self) self._overlayAction.setCheckable(True) self._overlayAction.setChecked(False) self._dockAction = QAction('Undock all', self) self._dockAction.setCheckable(True) self._dockAction.setChecked(False) # self._dockAction.toggle.connect( self._toggleDocking) self._restoreDocksAction = QAction('Show all', self) self._restoreDocksAction.triggered.connect( self._restoreDocks) self._quitAction = QAction(self.tr('&Quit'), self) self._quitAction.setShortcut(self.tr('Ctrl+Q')) self._quitAction.triggered.connect(qApp.closeAllWindows) def _createRunToolBar(self): self._simToolBar = self.addToolBar(self.tr('Simulation control')) self._simToolBar.addAction(self._quitAction) self._simToolBar.addAction(self._runAction) self._simToolBar.addAction(self._resetToDefaultsAction) self._simToolBar.addAction(self._dockAction) self._simToolBar.addAction(self._restoreDocksAction) def _createPlotToolBar(self): self._plotToolBar = self.addToolBar(self.tr('Plotting control')) self._plotToolBar.addAction(self._showLegendAction) self._plotToolBar.addAction(self._autoscaleAction) self._plotToolBar.addAction(self._overlayAction) self._plotToolBar.addAction(self._showStatePlotAction) self._plotToolBar.addAction(self._helpAction) self._plotToolBar.addAction(self._helpBiophysicsAction) def _showLegend(self, on): if on: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend().set_visible(True) else: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend().set_visible(False) self._plotCanvas.draw() self._statePlotCanvas.draw() def _autoscale(self, on): if on: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.relim() axis.set_autoscale_on(True) axis.autoscale_view(True) else: for axis in self._plotFigure.axes: axis.set_autoscale_on(False) self._vm_axes.set_ylim(-20.0, 120.0) self._g_axes.set_ylim(0.0, 0.5) self._im_axes.set_ylim(-0.5, 0.5) self._i_axes.set_ylim(-10, 10) self._plotCanvas.draw() self._statePlotCanvas.draw() def _useDefaults(self): self._runTimeEdit.setText('%g' % (self.defaults['runtime'])) self._simTimeStepEdit.setText('%g' % (self.defaults['simdt'])) self._overlayAction.setChecked(False) self._naConductanceToggle.setChecked(False) self._kConductanceToggle.setChecked(False) self._kOutEdit.setText('%g' % (SquidGui.defaults['K_out'])) self._naOutEdit.setText('%g' % (SquidGui.defaults['Na_out'])) self._kInEdit.setText('%g' % (SquidGui.defaults['K_in'])) self._naInEdit.setText('%g' % (SquidGui.defaults['Na_in'])) self._temperatureEdit.setText('%g' % (SquidGui.defaults['temperature'] - CELSIUS_TO_KELVIN)) self._holdingVEdit.setText('%g' % (SquidGui.defaults['vclamp.holdingV'])) self._holdingTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.holdingT'])) self._prePulseVEdit.setText('%g' % (SquidGui.defaults['vclamp.prepulseV'])) self._prePulseTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.prepulseT'])) self._clampVEdit.setText('%g' % (SquidGui.defaults['vclamp.clampV'])) self._clampTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.clampT'])) self._baseCurrentEdit.setText('%g' % (SquidGui.defaults['iclamp.baseI'])) self._firstPulseEdit.setText('%g' % (SquidGui.defaults['iclamp.firstI'])) self._firstDelayEdit.setText('%g' % (SquidGui.defaults['iclamp.firstD'])) self._firstPulseWidthEdit.setText('%g' % (SquidGui.defaults['iclamp.firstT'])) self._secondPulseEdit.setText('%g' % (SquidGui.defaults['iclamp.secondI'])) self._secondDelayEdit.setText('%g' % (SquidGui.defaults['iclamp.secondD'])) self._secondPulseWidthEdit.setText('%g' % (SquidGui.defaults['iclamp.secondT'])) self._pulseMode.setCurrentIndex(0) def _onScroll(self, event): if event.inaxes is None: return axes = event.inaxes zoom = 0.0 if event.button == 'up': zoom = -1.0 elif event.button == 'down': zoom = 1.0 if zoom != 0.0: self._plotNavigator.push_current() axes.get_xaxis().zoom(zoom) axes.get_yaxis().zoom(zoom) self._plotCanvas.draw() def closeEvent(self, event): qApp.closeAllWindows() def _showBioPhysicsHelp(self): self._createHelpMessage() self._helpMessageText.setText('<html><p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p></html>' % (tooltip_Nernst, tooltip_Erest, tooltip_KChan, tooltip_NaChan, tooltip_Im)) self._helpWindow.setVisible(True) def _showRunningHelp(self): self._createHelpMessage() self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL)) self._helpWindow.setVisible(True) def _createHelpMessage(self): if hasattr(self, '_helpWindow'): return self._helpWindow = QWidget() self._helpWindow.setWindowFlags(QtCore.Qt.Window) layout = QVBoxLayout() self._helpWindow.setLayout(layout) self._helpMessageArea = QScrollArea() self._helpMessageText = QTextBrowser() self._helpMessageText.setOpenExternalLinks(True) self._helpMessageArea.setWidget(self._helpMessageText) layout.addWidget(self._helpMessageText) self._squidGuiPath = os.path.dirname(os.path.abspath(__file__)) self._helpBaseURL = os.path.join(self._squidGuiPath,'help.html') self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL)) self._helpMessageText.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._helpMessageArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._helpMessageText.setMinimumSize(800, 600) self._closeHelpAction = QAction('Close', self) self._closeHelpAction.triggered.connect(self._helpWindow.close) # Setting the close event so that the ``Help`` button is # unchecked when the help window is closed self._helpWindow.closeEvent = lambda event: self._helpAction.setChecked(False) self._helpTOCAction = QAction('Help running demo', self) self._helpTOCAction.triggered.connect( self._jumpToHelpTOC) # This panel is for putting two buttons using horizontal # layout panel = QFrame() panel.setFrameStyle(QFrame.StyledPanel + QFrame.Raised) layout.addWidget(panel) layout = QHBoxLayout() panel.setLayout(layout) self._helpAction = QAction('Help running', self) self._helpAction.triggered.connect(self._showRunningHelp) self._helpBiophysicsAction = QAction('Help biophysics', self) self._helpBiophysicsAction.triggered.connect(self._showBioPhysicsHelp) self._helpTOCButton = QToolButton() self._helpTOCButton.setDefaultAction(self._helpTOCAction) self._helpBiophysicsButton = QToolButton() self._helpBiophysicsButton.setDefaultAction(self._helpBiophysicsAction) layout.addWidget(self._helpTOCButton) layout.addWidget(self._helpBiophysicsButton) self._closeHelpButton = QToolButton() self._closeHelpButton.setDefaultAction(self._closeHelpAction) layout.addWidget(self._closeHelpButton) def _jumpToHelpTOC(self): self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL))
class CalibWin(QWidget, _calf.calib_functions_mixin, _ie.Imp_Exp_Mixin): """ Energy calibration window For the names of the children widgets, I tried to put suffixes that indicate clearly their types: *_btn -> QPushButton, *_le -> QLineEdit, *_lb -> QLabel, *layout -> QHBoxLayout, QVBoxLayout or QGridLayout, *_box -> QGroupBox, *_cb -> QCheckBox, *_rb -> QRadioButton The functions that are connected to a widget's event have the suffix _lr (for 'listener'). For example, a button named test_btn will be connected to a function test_lr. Some functions may be connected to widgets but without the suffix _lr in their names. It means that they are not only called when interacting with the widget. """ ################################################################################## ############################ Widget Initialization ############################### def __init__(self, parent=None): """Initialization of the window the main layout is called mainLayout. It is divided in two: - graphLayout: the left part, contains all the figures - commandLayout: the right part, contains all the buttons, fields, checkboxes... Both graphLayout and commandLayout are divided into sub-layouts. This function calls several functions to initialize each part of the window. The name of these functions contains 'init_*layout'. """ super(CalibWin, self).__init__(parent=parent) self.setWindowTitle("Energy Calibration") self.mainlayout = QHBoxLayout() self.graphlayout = QVBoxLayout() self.commandlayout = QVBoxLayout() self.commandlayout.setSpacing(10) # initialization of all the widgets/layouts self.init_btnlayout() self.init_tof2evlayout() self.init_eparlayout() self.init_fitparlayout() self.init_envectlayout() self.init_tofgraphlayout() self.init_graphauxlayout() # making the buttons not resizable for widget in self.children(): if isinstance(widget, QPushButton): widget.setSizePolicy(0, 0) self.mainlayout.addLayout(self.graphlayout) self.mainlayout.addLayout(self.commandlayout) self.setLayout(self.mainlayout) self.init_var() self.show() def init_var(self): ''' Initialization of instance attributes''' self.withsb_bool = False self.calibloaded = False self.dataloaded = False self.bgndremoved = False self.threshyBool = False self.threshxminBool = False self.threshxmaxBool = False self.showexppeaksBool = False self.calibBool = False self.peaksfound = False self.thxmin = 0 self.thy = 0 self.thxmax = 0 self.counts = [] self.gas_combo.setCurrentIndex(2) # Argon def init_btnlayout(self): ''' In commandLayout - Initialization of the top right part of the layout, with 6 buttons (see just below)''' btnlayout = QGridLayout() btnlayout.setSpacing(10) self.load_btn = QPushButton("Load data", self) self.rmbgnd_btn = QPushButton("Remove bgnd", self) self.findpeaks_btn = QPushButton("Find peaks", self) self.rmpeaks_btn = QPushButton("Remove peaks", self) self.exportcalib_btn = QPushButton("Export calib", self) self.importcalib_btn = QPushButton("Import calib", self) self.exportXUV_btn = QPushButton("Export XUV", self) self.rmbgnd_btn.setEnabled(False) self.findpeaks_btn.setEnabled(False) self.rmpeaks_btn.setEnabled(False) self.exportcalib_btn.setEnabled(False) self.importcalib_btn.setEnabled(False) self.exportXUV_btn.setEnabled(False) self.load_btn.clicked.connect(self.loadfile_lr) self.rmbgnd_btn.clicked.connect(self.rmbgnd_lr) self.findpeaks_btn.clicked.connect(self.findpeaks_lr) self.importcalib_btn.clicked.connect(self.importcalib_lr) self.rmpeaks_btn.clicked.connect(self.removepeaks_lr) self.exportXUV_btn.clicked.connect(self.exportXUV_lr) self.exportcalib_btn.clicked.connect(self.exportcalib_lr) btnlayout.addWidget(self.load_btn, 0, 0) btnlayout.addWidget(self.rmbgnd_btn, 0, 1) btnlayout.addWidget(self.findpeaks_btn, 1, 0) btnlayout.addWidget(self.rmpeaks_btn, 1, 1) btnlayout.addWidget(self.exportcalib_btn, 1, 3) btnlayout.addWidget(self.importcalib_btn, 1, 2) btnlayout.addWidget(self.exportXUV_btn, 0, 3) self.commandlayout.addLayout(btnlayout) def init_tof2evlayout(self): ''' In commandLayout - Initialization of the tof to eV section: parameters of the af.find_local_maxima function, 'TOF to energy' button and 'with sidebands checkbox' ''' tof2evlayout = QHBoxLayout() flmlayout = QGridLayout() flmlayout.setSpacing(10) self.flm_box = QGroupBox(self) self.flm_box.setTitle("Find local maxima parameters") self.sm1_le = QLineEdit("5", self) self.sm2_le = QLineEdit("100", self) self.mindt_le = QLineEdit("10", self) flmlayout.addWidget(QLabel("smooth1"), 0, 0) flmlayout.addWidget(self.sm1_le, 1, 0) flmlayout.addWidget(QLabel("smooth2"), 0, 1) flmlayout.addWidget(self.sm2_le, 1, 1) flmlayout.addWidget(QLabel("min dt"), 0, 2) flmlayout.addWidget(self.mindt_le, 1, 2) self.flm_box.setLayout(flmlayout) for widget in self.flm_box.children(): if isinstance(widget, QLineEdit): widget.setSizePolicy(0, 0) widget.setFixedSize(50, 20) self.tof2en_btn = QPushButton("TOF to energy", self) self.tof2en_btn.clicked.connect(self.tof2en_lr) self.tof2en_btn.setEnabled(False) self.withsb_cb = QCheckBox("With sidebands", self) self.withsb_cb.stateChanged.connect(self.withsb_fn) tof2evlayout.addWidget(self.flm_box) tof2evlayout.addWidget(self.withsb_cb) tof2evlayout.addWidget(self.tof2en_btn) self.commandlayout.addLayout(tof2evlayout) def init_eparlayout(self): ''' In commandLayout - Initialization of the experimental parameters section: Retarding potential, TOF length, wavelength, gas and first harmonic expected to see.''' gases = cts.GASLIST epar_box = QGroupBox(self) epar_box.setTitle("Experimental parameters") epar_box.setSizePolicy(0, 0) eparlayout = QGridLayout() eparlayout.setSpacing(10) self.retpot_le = QLineEdit(str(cts.cur_Vp), self) self.toflength_le = QLineEdit(str(cts.cur_L), self) self.wvlength_le = QLineEdit(str(cts.lambda_start), self) self.gas_combo = QComboBox(self) self.gas_combo.addItems(gases) self.firstharm_le = QLineEdit(str(cts.first_harm), self) self.retpot_le.returnPressed.connect(self.update_cts_fn) self.toflength_le.returnPressed.connect(self.update_cts_fn) self.wvlength_le.returnPressed.connect(self.update_cts_fn) self.firstharm_le.returnPressed.connect(self.update_cts_fn) self.gas_combo.currentIndexChanged.connect(self.gas_combo_lr) eparlayout.addWidget(QLabel("Ret. pot. (V)"), 0, 0) eparlayout.addWidget(self.retpot_le, 1, 0) eparlayout.addWidget(QLabel("TOF length (m)"), 0, 1) eparlayout.addWidget(self.toflength_le, 1, 1) eparlayout.addWidget(QLabel("lambda (nm)"), 0, 2) eparlayout.addWidget(self.wvlength_le, 1, 2) eparlayout.addWidget(QLabel("gas"), 0, 3) eparlayout.addWidget(self.gas_combo, 1, 3) eparlayout.addWidget(QLabel("1st harm."), 0, 4) eparlayout.addWidget(self.firstharm_le, 1, 4) epar_box.setLayout(eparlayout) for widget in epar_box.children(): if isinstance(widget, QLineEdit) or isinstance(widget, QComboBox): widget.setFixedSize(50, 20) widget.setSizePolicy(0, 0) self.commandlayout.addWidget(epar_box) def init_fitparlayout(self): ''' In commandLayout - Initialization of the fit parameter section with a, t0 and c. First line: guess values calculated from the experimental parameters. Second line: fitted values.''' fitpar_box = QGroupBox() fitpar_box.setTitle("Calibration parameters") fitpar_box.setSizePolicy(0, 0) fitparlayout = QGridLayout() fitparlayout.setSpacing(10) self.aguess_lb = QLabel(self) self.afit_lb = QLabel(self) self.t0guess_lb = QLabel(self) self.t0fit_lb = QLabel(self) self.cguess_lb = QLabel(self) self.cfit_lb = QLabel(self) fitparlayout.addWidget(QLabel("a"), 0, 0) fitparlayout.addWidget(self.aguess_lb, 1, 0) fitparlayout.addWidget(self.afit_lb, 2, 0) fitparlayout.addWidget(QLabel("t0"), 0, 1) fitparlayout.addWidget(self.t0guess_lb, 1, 1) fitparlayout.addWidget(self.t0fit_lb, 2, 1) fitparlayout.addWidget(QLabel("c"), 0, 2) fitparlayout.addWidget(self.cguess_lb, 1, 2) fitparlayout.addWidget(self.cfit_lb, 2, 2) fitparlayout.addWidget(QLabel("guess"), 1, 3) fitparlayout.addWidget(QLabel("calc"), 2, 3) text = "q=a*1/(t-t0)²+c" fitparlayout.addWidget(QLabel(text), 1, 4) fitpar_box.setLayout(fitparlayout) for widget in fitpar_box.children(): if isinstance(widget, QLabel) and widget.text() != text: widget.setSizePolicy(0, 0) widget.setFixedSize(55, 15) self.commandlayout.addWidget(fitpar_box) def init_envectlayout(self): ''' In commandLayout - Initialization of the resulting energy vector section, with elow, ehigh and dE''' envect_box = QGroupBox() envect_box.setTitle("Energy vector parameters") envect_box.setSizePolicy(0, 0) envectlayout = QGridLayout() envectlayout.setSpacing(10) self.elow_le = QLineEdit("{:.2f}".format(cts.elow), self) self.ehigh_le = QLineEdit("{:.2f}".format(cts.ehigh), self) self.dE_le = QLineEdit(str(cts.dE), self) self.elow_le.returnPressed.connect(self.update_envect_fn) self.ehigh_le.returnPressed.connect(self.update_envect_fn) self.dE_le.returnPressed.connect(self.update_envect_fn) envectlayout.addWidget(QLabel("E low (eV)"), 0, 0) envectlayout.addWidget(self.elow_le, 1, 0) envectlayout.addWidget(QLabel("E high (eV)"), 0, 1) envectlayout.addWidget(self.ehigh_le, 1, 1) envectlayout.addWidget(QLabel("dE (eV)"), 0, 2) envectlayout.addWidget(self.dE_le, 1, 2) envect_box.setLayout(envectlayout) for widget in envect_box.children(): if isinstance(widget, QLabel) or isinstance(widget, QLineEdit): widget.setSizePolicy(0, 0) widget.setFixedSize(55, 20) self.commandlayout.addWidget(envect_box) self.update_envect_fn() def init_tofgraphlayout(self): ''' In graphLayout - Initialization of the top figure on the window, where the time of flight is plotted''' tof_fig = Figure(figsize=(4, 3), dpi=100) self.tof_fc = FigureCanvas(tof_fig) self.tof_fc.mpl_connect('button_press_event', self.onclick) self.tof_ax = self.tof_fc.figure.add_subplot(111) self.tof_fc.draw() tof_nav = NavigationToolbar2QT(self.tof_fc, self) tof_nav.setStyleSheet("QToolBar { border: 0px }") tgparalayout = QGridLayout() self.threshtype_bgroup = QButtonGroup() rblayout = QGridLayout() self.setth_cb = QCheckBox("Set threshold", self) self.setth_cb.setEnabled(False) self.setth_cb.stateChanged.connect(self.setth_lr) self.addpeak_cb = QCheckBox("Add peak", self) self.addpeak_cb.setEnabled(False) self.addpeak_cb.stateChanged.connect(self.addpeak_lr) self.showexppeaks_cb = QCheckBox("Show expected peaks", self) self.showexppeaks_cb.stateChanged.connect(self.showexppeaks_lr) self.showexppeaks_cb.setEnabled(False) self.clear_btn = QPushButton("Clear", self) self.clear_btn.clicked.connect(self.clear_lr) self.clear_btn.setEnabled(False) self.Y_rb = QRadioButton("Y", self) self.Y_rb.value = "Y" self.Y_rb.toggled.connect(self.threshtype_lr) self.Y_rb.toggle() self.Y_rb.setEnabled(False) self.xmin_rb = QRadioButton("X min", self) self.xmin_rb.value = "Xm" self.xmin_rb.toggled.connect(self.threshtype_lr) self.xmin_rb.setEnabled(False) self.xmax_rb = QRadioButton("X max", self) self.xmax_rb.value = "XM" self.xmax_rb.toggled.connect(self.threshtype_lr) self.xmax_rb.setEnabled(False) self.minus_sign_cb = QCheckBox("*(-1)", self) self.minus_sign_cb.setEnabled(False) self.minus_sign_cb.stateChanged.connect(self.minus_sign_lr) self.threshtype_bgroup.addButton(self.Y_rb) self.threshtype_bgroup.addButton(self.xmin_rb) self.threshtype_bgroup.addButton(self.xmax_rb) rblayout.addWidget(self.Y_rb, 0, 0) rblayout.addWidget(self.xmin_rb, 0, 1) rblayout.addWidget(self.xmax_rb, 0, 2) tgparalayout.addWidget(self.setth_cb, 0, 0) tgparalayout.addWidget(self.addpeak_cb, 0, 1) tgparalayout.addWidget(self.showexppeaks_cb, 0, 2) tgparalayout.addWidget(self.clear_btn, 0, 3) tgparalayout.addLayout(rblayout, 1, 0, 1, 3) tgparalayout.addWidget(self.minus_sign_cb, 1, 3) self.graphlayout.addWidget(self.tof_fc) self.graphlayout.addWidget(tof_nav) self.graphlayout.addLayout(tgparalayout) def init_graphauxlayout(self): ''' In graphLayout - Initialization the two bottom figures on the window''' graphauxlayout = QHBoxLayout() ga1layout = QVBoxLayout() ga2layout = QVBoxLayout() fit_fig = Figure(figsize=(2, 2), dpi=100) self.fit_fc = FigureCanvas(fit_fig) self.fit_fc.setSizePolicy(1, 0) self.fit_ax = self.fit_fc.figure.add_subplot(111) self.fit_ax.tick_params(labelsize=8) ga1layout.addWidget(self.fit_fc) self.fit_fc.draw() en_fig = Figure(figsize=(2, 2), dpi=100) self.en_fc = FigureCanvas(en_fig) #self.en_fc.setSizePolicy(1, 0) self.en_ax = self.en_fc.figure.add_subplot(111) self.en_ax.tick_params(labelsize=8) self.en_fc.draw() en_nav = NavigationToolbar2QT(self.en_fc, self) en_nav.setStyleSheet("QToolBar { border: 0px }") ga2layout.addWidget(self.en_fc) ga2layout.addWidget(en_nav) graphauxlayout.addLayout(ga1layout) graphauxlayout.addLayout(ga2layout) self.graphlayout.addLayout(graphauxlayout) ################################################################################# ############################ Other methods ###################################### def update_fitpar_fn(self): ''' Updating the fit parameters on the window''' if (self.calibBool): self.afit_lb.setText("{:.3e}".format(cts.afit)) self.t0fit_lb.setText("{:.3e}".format(cts.t0fit)) self.cfit_lb.setText("{:.3f}".format(cts.cfit)) def update_envect_fn(self): ''' Updating th energy vector parameters with the values written in the associated QLineEdit objects''' cts.elow = float(self.elow_le.text()) cts.ehigh = float(self.ehigh_le.text()) cts.dE = float(self.dE_le.text()) self.elow_le.setText("{:.2f}".format(cts.elow)) self.ehigh_le.setText("{:.2f}".format(cts.ehigh)) self.window().updateglobvar_fn() def gas_combo_lr(self, i): ''' Gas QCombobox listener''' cts.cur_Ip = cts.IPLIST[i] cts.first_harm = cts.FIRST_HARMLIST[i] self.firstharm_le.setText(str(cts.first_harm)) cts.elow = (cts.first_harm - 1) * cts.HEV * cts.cur_nu self.elow_le.setText("{:.2f}".format(cts.elow)) self.update_cts_fn() def threshtype_lr(self): ''' Listener of the threshold radiobuttons: Y, Xmin or Xmax''' rb = self.sender() self.threshtype = rb.value def withsb_fn(self, state): ''' "with sidebands" checkbox listener ''' if state == Qt.Checked: self.withsb_bool = True else: self.withsb_bool = False def showexppeaks_lr(self, state): ''' "show expected peaks" checkbox listener ''' if state == Qt.Checked: self.showexppeaksBool = True else: self.showexppeaksBool = False self.refreshplot_fn() def setth_lr(self): ''' "set threshold" checkbox listener ''' if self.setth_cb.isChecked(): self.addpeak_cb.setCheckState(Qt.Unchecked) def addpeak_lr(self): ''' "add peak" checkbox listener ''' if self.addpeak_cb.isChecked(): self.setth_cb.setCheckState(Qt.Unchecked) def removepeaks_lr(self): ''' "remove peaks" button listener ''' rmp = rmPeaksDialog(self) # new class defined below def refreshplot_fn(self): ''' Updating the top left (TOF) graph''' xmin, xmax = self.tof_ax.get_xlim() ymin, ymax = self.tof_ax.get_ylim() self.tof_ax.cla() self.tof_ax.xaxis.set_major_formatter(FormatStrFormatter('%2.e')) self.tof_ax.yaxis.set_major_formatter(FormatStrFormatter('%1.e')) self.tof_ax.set_ylabel("counts (arb. units)") self.tof_ax.set_xlabel("TOF (s)") if self.dataloaded: self.tof_ax.plot(self.counts[:, 0], self.counts[:, 1]) if self.threshyBool: self.tof_ax.plot(self.threshyline[:, 0], self.threshyline[:, 1], 'k') if self.threshxminBool: self.tof_ax.plot(self.threshxminline[:, 0], self.threshxminline[:, 1], 'k') if self.threshxmaxBool: self.tof_ax.plot(self.threshxmaxline[:, 0], self.threshxmaxline[:, 1], 'k') if self.peaksfound: self.tof_ax.plot(self.maximaIndices, self.maximaIntensity, 'ro') self.tof_ax.plot(self.counts[:, 0], self.convolvedsignal) if self.showexppeaksBool: qq2 = np.arange(cts.first_harm, 35, 1) y = np.linspace(ymax - (ymax - ymin) * 0.2, ymax, 100) for i in range((len(qq2))): if qq2[i] % 2 == 0: c = 'r' else: c = 'k' try: xval = float( np.math.sqrt(0.5 * cts.ME * cts.cur_L**2 / cts.QE / (qq2[i] * cts.HEV * cts.cur_nu - cts.cur_Ip)) + 6e-8) # NB: cts.QE is used to convert the energy from eV to Joules. It's not the electron's charge x = np.full((100, 1), xval) except Exception: print(traceback.format_exception(*sys.exc_info())) self.tof_ax.plot(x, y, color=c, linewidth=1.0) if self.bgndremoved: self.bgndremoved = False #this means that when we remove bgnd, we don't keep the same scale else: self.tof_ax.set_ylim(ymin, ymax) self.tof_ax.set_xlim(xmin, xmax) self.tof_fc.draw() def onclick(self, event): ''' called when double-clicking on the TOF graph''' if self.dataloaded and self.addpeak_cb.isChecked( ): #add a peak on clicking on the figure i = 0 ifound = False while (i < len(self.maximaIndices) and ifound == False): if (self.maximaIndices[i] < event.xdata): i += 1 else: ifound = True self.maximaIndices.insert(i, event.xdata) self.maximaIntensity.insert(i, event.ydata) self.refreshplot_fn() if self.dataloaded and self.setth_cb.isChecked(): if self.Y_rb.isChecked(): self.thy = event.ydata self.threshyline = np.full((len(self.counts), 2), self.thy) self.threshyline[:, 0] = self.counts[:, 0] self.threshyBool = True elif self.xmin_rb.isChecked(): self.thxmin = event.xdata self.threshxminline = np.full((len(self.counts), 2), self.thxmin) self.threshxminline[:, 1] = self.counts[:, 1] self.threshxminBool = True elif self.xmax_rb.isChecked(): self.thxmax = event.xdata self.threshxmaxline = np.full((len(self.counts), 2), self.thxmax) self.threshxmaxline[:, 1] = self.counts[:, 1] self.threshxmaxBool = True self.refreshplot_fn() def exportXUV_lr(self): ''' "Export XUV" button listener. Saving the energy vector and the energy-converted TOF signal''' filename = QFileDialog.getSaveFileName(self, 'Save XUV') fname = filename[0] if fname: XUV_array = np.vstack((self.Eevlin, self.signal)).T np.savetxt(fname, XUV_array, delimiter='\t') def clear_lr(self): ''' "Clear" button listener. Resets all the objects, but not the global variables''' self.tof_ax.cla() self.fit_ax.cla() self.en_ax.cla() self.dataloaded = False self.threshyBool = False self.threshxminBool = False self.threshxmaxBool = False self.calibloaded = False self.calibBool = False self.Y_rb.setEnabled(False) self.xmin_rb.setEnabled(False) self.xmax_rb.setEnabled(False) self.minus_sign_cb.setEnabled(False) for w in self.children(): if isinstance(w, QPushButton) or isinstance(w, QCheckBox): w.setEnabled(False) self.load_btn.setEnabled(True) self.importcalib_btn.setEnabled(False) self.minus_sign_cb.setEnabled(False) self.tof_fc.draw() self.fit_fc.draw() self.en_fc.draw() cts.clear_varlist() self.window().updateglobvar_fn()
class SquidGui(QMainWindow): defaults = {} defaults.update(SquidAxon.defaults) defaults.update(ClampCircuit.defaults) defaults.update({ 'runtime': 50.0, 'simdt': 0.01, 'plotdt': 0.1, 'vclamp.holdingV': 0.0, 'vclamp.holdingT': 10.0, 'vclamp.prepulseV': 0.0, 'vclamp.prepulseT': 0.0, 'vclamp.clampV': 50.0, 'vclamp.clampT': 20.0, 'iclamp.baseI': 0.0, 'iclamp.firstI': 0.1, 'iclamp.firstT': 40.0, 'iclamp.firstD': 5.0, 'iclamp.secondI': 0.0, 'iclamp.secondT': 0.0, 'iclamp.secondD': 0.0 }) def __init__(self, *args): QMainWindow.__init__(self, *args) self.squid_setup = SquidSetup() self._plotdt = SquidGui.defaults['plotdt'] self._plot_dict = defaultdict(list) self.setWindowTitle('Squid Axon simulation') self.setDockNestingEnabled(True) self._createRunControl() self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._runControlDock) self._runControlDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self._createChannelControl() self._channelCtrlBox.setWindowTitle('Channel properties') self._channelControlDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._channelControlDock) self._createElectronicsControl() self._electronicsDock.setFeatures(QDockWidget.AllDockWidgetFeatures) self._electronicsDock.setWindowTitle('Electronics') self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self._electronicsDock) self._createPlotWidget() self.setCentralWidget(self._plotWidget) self._createStatePlotWidget() self._createHelpMessage() self._helpWindow.setVisible(False) self._statePlotWidget.setWindowFlags(QtCore.Qt.Window) self._statePlotWidget.setWindowTitle('State plot') self._initActions() self._createRunToolBar() self._createPlotToolBar() def getFloatInput(self, widget, name): try: return float(str(widget.text())) except ValueError: QMessageBox.critical( self, 'Invalid input', 'Please enter a valid number for {}'.format(name)) raise def _createPlotWidget(self): self._plotWidget = QWidget() self._plotFigure = Figure() self._plotCanvas = FigureCanvas(self._plotFigure) self._plotCanvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._plotCanvas.updateGeometry() self._plotCanvas.setParent(self._plotWidget) self._plotCanvas.mpl_connect('scroll_event', self._onScroll) self._plotFigure.set_canvas(self._plotCanvas) # Vm and command voltage go in the same subplot self._vm_axes = self._plotFigure.add_subplot( 2, 2, 1, title='Membrane potential') self._vm_axes.set_ylim(-20.0, 120.0) # Channel conductances go to the same subplot self._g_axes = self._plotFigure.add_subplot( 2, 2, 2, title='Channel conductance') self._g_axes.set_ylim(0.0, 0.5) # Injection current for Vclamp/Iclamp go to the same subplot self._im_axes = self._plotFigure.add_subplot(2, 2, 3, title='Injection current') self._im_axes.set_ylim(-0.5, 0.5) # Channel currents go to the same subplot self._i_axes = self._plotFigure.add_subplot(2, 2, 4, title='Channel current') self._i_axes.set_ylim(-10, 10) for axis in self._plotFigure.axes: axis.set_autoscale_on(False) layout = QVBoxLayout() layout.addWidget(self._plotCanvas) self._plotNavigator = NavigationToolbar(self._plotCanvas, self._plotWidget) layout.addWidget(self._plotNavigator) self._plotWidget.setLayout(layout) def _createStatePlotWidget(self): self._statePlotWidget = QWidget() self._statePlotFigure = Figure() self._statePlotCanvas = FigureCanvas(self._statePlotFigure) self._statePlotCanvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._statePlotCanvas.updateGeometry() self._statePlotCanvas.setParent(self._statePlotWidget) self._statePlotFigure.set_canvas(self._statePlotCanvas) self._statePlotFigure.subplots_adjust(hspace=0.5) self._statePlotAxes = self._statePlotFigure.add_subplot( 2, 1, 1, title='State plot') self._state_plot, = self._statePlotAxes.plot([], [], label='state') self._activationParamAxes = self._statePlotFigure.add_subplot( 2, 1, 2, title='H-H activation parameters vs time') self._activationParamAxes.set_xlabel('Time (ms)') #for axis in self._plotFigure.axes: # axis.autoscale(False) self._stateplot_xvar_label = QLabel('Variable on X-axis') self._stateplot_xvar_combo = QComboBox() self._stateplot_xvar_combo.addItems(['V', 'm', 'n', 'h']) self._stateplot_xvar_combo.setCurrentText('V') self._stateplot_xvar_combo.setEditable(False) self._stateplot_xvar_combo.currentIndexChanged[str].connect( self._statePlotXSlot) self._stateplot_yvar_label = QLabel('Variable on Y-axis') self._stateplot_yvar_combo = QComboBox() self._stateplot_yvar_combo.addItems(['V', 'm', 'n', 'h']) self._stateplot_yvar_combo.setCurrentIndex(2) self._stateplot_yvar_combo.setEditable(False) self._stateplot_yvar_combo.currentIndexChanged[str].connect( self._statePlotYSlot) self._statePlotNavigator = NavigationToolbar(self._statePlotCanvas, self._statePlotWidget) frame = QFrame() frame.setFrameStyle(QFrame.StyledPanel + QFrame.Raised) layout = QHBoxLayout() layout.addWidget(self._stateplot_xvar_label) layout.addWidget(self._stateplot_xvar_combo) layout.addWidget(self._stateplot_yvar_label) layout.addWidget(self._stateplot_yvar_combo) frame.setLayout(layout) self._closeStatePlotAction = QAction('Close', self) self._closeStatePlotAction.triggered.connect( self._statePlotWidget.close) self._closeStatePlotButton = QToolButton() self._closeStatePlotButton.setDefaultAction(self._closeStatePlotAction) layout = QVBoxLayout() layout.addWidget(frame) layout.addWidget(self._statePlotCanvas) layout.addWidget(self._statePlotNavigator) layout.addWidget(self._closeStatePlotButton) self._statePlotWidget.setLayout(layout) # Setting the close event so that when the help window is # closed the ``State plot`` button becomes unchecked self._statePlotWidget.closeEvent = lambda event: self._showStatePlotAction.setChecked( False) def _createRunControl(self): self._runControlBox = QGroupBox(self) self._runControlBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self._runTimeLabel = QLabel("Run time (ms)", self._runControlBox) self._simTimeStepLabel = QLabel("Simulation time step (ms)", self._runControlBox) self._runTimeEdit = QLineEdit('%g' % (SquidGui.defaults['runtime']), self._runControlBox) set_default_line_edit_size(self._runTimeEdit) self._simTimeStepEdit = QLineEdit('%g' % (SquidGui.defaults['simdt']), self._runControlBox) set_default_line_edit_size(self._simTimeStepEdit) layout = QGridLayout() layout.addWidget(self._runTimeLabel, 0, 0) layout.addWidget(self._runTimeEdit, 0, 1) layout.addWidget(self._simTimeStepLabel, 1, 0) layout.addWidget(self._simTimeStepEdit, 1, 1) layout.setColumnStretch(2, 1.0) layout.setRowStretch(2, 1.0) self._runControlBox.setLayout(layout) self._runControlDock = QDockWidget('Simulation', self) self._runControlDock.setWidget(self._runControlBox) def _createChannelControl(self): self._channelControlDock = QDockWidget('Channels', self) self._channelCtrlBox = QGroupBox(self) self._naConductanceToggle = QCheckBox('Block Na+ channel', self._channelCtrlBox) self._naConductanceToggle.setToolTip('<html>%s</html>' % (tooltip_NaChan)) self._kConductanceToggle = QCheckBox('Block K+ channel', self._channelCtrlBox) self._kConductanceToggle.setToolTip('<html>%s</html>' % (tooltip_KChan)) self._kOutLabel = QLabel('[K+]out (mM)', self._channelCtrlBox) self._kOutEdit = QLineEdit('%g' % (self.squid_setup.axon.K_out), self._channelCtrlBox) self._kOutLabel.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._kOutEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._kOutEdit) self._naOutLabel = QLabel('[Na+]out (mM)', self._channelCtrlBox) self._naOutEdit = QLineEdit('%g' % (self.squid_setup.axon.Na_out), self._channelCtrlBox) self._naOutLabel.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._naOutEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._naOutEdit) self._kInLabel = QLabel('[K+]in (mM)', self._channelCtrlBox) self._kInEdit = QLineEdit('%g' % (self.squid_setup.axon.K_in), self._channelCtrlBox) self._kInEdit.setToolTip(tooltip_Nernst) self._naInLabel = QLabel('[Na+]in (mM)', self._channelCtrlBox) self._naInEdit = QLineEdit('%g' % (self.squid_setup.axon.Na_in), self._channelCtrlBox) self._naInEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) self._temperatureLabel = QLabel('Temperature (C)', self._channelCtrlBox) self._temperatureEdit = QLineEdit( '%g' % (self.defaults['temperature'] - CELSIUS_TO_KELVIN), self._channelCtrlBox) self._temperatureEdit.setToolTip('<html>%s</html>' % (tooltip_Nernst)) set_default_line_edit_size(self._temperatureEdit) for child in self._channelCtrlBox.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(self._channelCtrlBox) layout.addWidget(self._naConductanceToggle, 0, 0) layout.addWidget(self._kConductanceToggle, 1, 0) layout.addWidget(self._naOutLabel, 2, 0) layout.addWidget(self._naOutEdit, 2, 1) layout.addWidget(self._naInLabel, 3, 0) layout.addWidget(self._naInEdit, 3, 1) layout.addWidget(self._kOutLabel, 4, 0) layout.addWidget(self._kOutEdit, 4, 1) layout.addWidget(self._kInLabel, 5, 0) layout.addWidget(self._kInEdit, 5, 1) layout.addWidget(self._temperatureLabel, 6, 0) layout.addWidget(self._temperatureEdit, 6, 1) layout.setRowStretch(7, 1.0) self._channelCtrlBox.setLayout(layout) self._channelControlDock.setWidget(self._channelCtrlBox) return self._channelCtrlBox def __get_stateplot_data(self, name): data = [] if name == 'V': data = self.squid_setup.vm_table.vector elif name == 'm': data = self.squid_setup.m_table.vector elif name == 'h': data = self.squid_setup.h_table.vector elif name == 'n': data = self.squid_setup.n_table.vector else: raise ValueError('Unrecognized selection: %s' % name) return numpy.asarray(data) def _statePlotYSlot(self, selectedItem): ydata = self.__get_stateplot_data(str(selectedItem)) self._state_plot.set_ydata(ydata) self._statePlotAxes.set_ylabel(selectedItem) if str(selectedItem) == 'V': self._statePlotAxes.set_ylim(-20, 120) else: self._statePlotAxes.set_ylim(0, 1) self._statePlotCanvas.draw() def _statePlotXSlot(self, selectedItem): xdata = self.__get_stateplot_data(str(selectedItem)) self._state_plot.set_xdata(xdata) self._statePlotAxes.set_xlabel(selectedItem) if str(selectedItem) == 'V': self._statePlotAxes.set_xlim(-20, 120) else: self._statePlotAxes.set_xlim(0, 1) self._statePlotCanvas.draw() def _createElectronicsControl(self): """Creates a tabbed widget of voltage clamp and current clamp controls""" self._electronicsTab = QTabWidget(self) self._electronicsTab.addTab(self._getIClampCtrlBox(), 'Current clamp') self._electronicsTab.addTab(self._getVClampCtrlBox(), 'Voltage clamp') self._electronicsDock = QDockWidget(self) self._electronicsDock.setWidget(self._electronicsTab) def _getVClampCtrlBox(self): vClampPanel = QGroupBox(self) self._vClampCtrlBox = vClampPanel self._holdingVLabel = QLabel("Holding Voltage (mV)", vClampPanel) self._holdingVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.holdingV']), vClampPanel) self._holdingTimeLabel = QLabel("Holding Time (ms)", vClampPanel) self._holdingTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.holdingT']), vClampPanel) self._prePulseVLabel = QLabel("Pre-pulse Voltage (mV)", vClampPanel) self._prePulseVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.prepulseV']), vClampPanel) self._prePulseTimeLabel = QLabel("Pre-pulse Time (ms)", vClampPanel) self._prePulseTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.prepulseT']), vClampPanel) self._clampVLabel = QLabel("Clamp Voltage (mV)", vClampPanel) self._clampVEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.clampV']), vClampPanel) self._clampTimeLabel = QLabel("Clamp Time (ms)", vClampPanel) self._clampTimeEdit = QLineEdit( '%g' % (SquidGui.defaults['vclamp.clampT']), vClampPanel) for child in vClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(vClampPanel) layout.addWidget(self._holdingVLabel, 0, 0) layout.addWidget(self._holdingVEdit, 0, 1) layout.addWidget(self._holdingTimeLabel, 1, 0) layout.addWidget(self._holdingTimeEdit, 1, 1) layout.addWidget(self._prePulseVLabel, 2, 0) layout.addWidget(self._prePulseVEdit, 2, 1) layout.addWidget(self._prePulseTimeLabel, 3, 0) layout.addWidget(self._prePulseTimeEdit, 3, 1) layout.addWidget(self._clampVLabel, 4, 0) layout.addWidget(self._clampVEdit, 4, 1) layout.addWidget(self._clampTimeLabel, 5, 0) layout.addWidget(self._clampTimeEdit, 5, 1) layout.setRowStretch(6, 1.0) vClampPanel.setLayout(layout) return self._vClampCtrlBox def _getIClampCtrlBox(self): iClampPanel = QGroupBox(self) self._iClampCtrlBox = iClampPanel self._baseCurrentLabel = QLabel("Base Current Level (uA)", iClampPanel) self._baseCurrentEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.baseI']), iClampPanel) self._firstPulseLabel = QLabel("First Pulse Current (uA)", iClampPanel) self._firstPulseEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstI']), iClampPanel) self._firstDelayLabel = QLabel("First Onset Delay (ms)", iClampPanel) self._firstDelayEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstD']), iClampPanel) self._firstPulseWidthLabel = QLabel("First Pulse Width (ms)", iClampPanel) self._firstPulseWidthEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.firstT']), iClampPanel) self._secondPulseLabel = QLabel("Second Pulse Current (uA)", iClampPanel) self._secondPulseEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondI']), iClampPanel) self._secondDelayLabel = QLabel("Second Onset Delay (ms)", iClampPanel) self._secondDelayEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondD']), iClampPanel) self._secondPulseWidthLabel = QLabel("Second Pulse Width (ms)", iClampPanel) self._secondPulseWidthEdit = QLineEdit( '%g' % (SquidGui.defaults['iclamp.secondT']), iClampPanel) self._pulseMode = QComboBox(iClampPanel) self._pulseMode.addItem("Single Pulse") self._pulseMode.addItem("Pulse Train") for child in iClampPanel.children(): if isinstance(child, QLineEdit): set_default_line_edit_size(child) layout = QGridLayout(iClampPanel) layout.addWidget(self._baseCurrentLabel, 0, 0) layout.addWidget(self._baseCurrentEdit, 0, 1) layout.addWidget(self._firstPulseLabel, 1, 0) layout.addWidget(self._firstPulseEdit, 1, 1) layout.addWidget(self._firstDelayLabel, 2, 0) layout.addWidget(self._firstDelayEdit, 2, 1) layout.addWidget(self._firstPulseWidthLabel, 3, 0) layout.addWidget(self._firstPulseWidthEdit, 3, 1) layout.addWidget(self._secondPulseLabel, 4, 0) layout.addWidget(self._secondPulseEdit, 4, 1) layout.addWidget(self._secondDelayLabel, 5, 0) layout.addWidget(self._secondDelayEdit, 5, 1) layout.addWidget(self._secondPulseWidthLabel, 6, 0) layout.addWidget(self._secondPulseWidthEdit, 6, 1) layout.addWidget(self._pulseMode, 7, 0, 1, 2) layout.setRowStretch(8, 1.0) # layout.setSizeConstraint(QLayout.SetFixedSize) iClampPanel.setLayout(layout) return self._iClampCtrlBox def _overlayPlots(self, overlay): if not overlay: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): title = axis.get_title() axis.clear() axis.set_title(title) suffix = '' else: suffix = '_%d' % (len(self._plot_dict['vm'])) self._vm_axes.set_xlim(0.0, self._runtime) self._g_axes.set_xlim(0.0, self._runtime) self._im_axes.set_xlim(0.0, self._runtime) self._i_axes.set_xlim(0.0, self._runtime) self._vm_plot, = self._vm_axes.plot([], [], label='Vm%s' % (suffix)) self._plot_dict['vm'].append(self._vm_plot) self._command_plot, = self._vm_axes.plot([], [], label='command%s' % (suffix)) self._plot_dict['command'].append(self._command_plot) # Channel conductances go to the same subplot self._gna_plot, = self._g_axes.plot([], [], label='Na%s' % (suffix)) self._plot_dict['gna'].append(self._gna_plot) self._gk_plot, = self._g_axes.plot([], [], label='K%s' % (suffix)) self._plot_dict['gk'].append(self._gk_plot) # Injection current for Vclamp/Iclamp go to the same subplot self._iclamp_plot, = self._im_axes.plot([], [], label='Iclamp%s' % (suffix)) self._vclamp_plot, = self._im_axes.plot([], [], label='Vclamp%s' % (suffix)) self._plot_dict['iclamp'].append(self._iclamp_plot) self._plot_dict['vclamp'].append(self._vclamp_plot) # Channel currents go to the same subplot self._ina_plot, = self._i_axes.plot([], [], label='Na%s' % (suffix)) self._plot_dict['ina'].append(self._ina_plot) self._ik_plot, = self._i_axes.plot([], [], label='K%s' % (suffix)) self._plot_dict['ik'].append(self._ik_plot) # self._i_axes.legend() # State plots self._state_plot, = self._statePlotAxes.plot([], [], label='state%s' % (suffix)) self._plot_dict['state'].append(self._state_plot) self._m_plot, = self._activationParamAxes.plot([], [], label='m%s' % (suffix)) self._h_plot, = self._activationParamAxes.plot([], [], label='h%s' % (suffix)) self._n_plot, = self._activationParamAxes.plot([], [], label='n%s' % (suffix)) self._plot_dict['m'].append(self._m_plot) self._plot_dict['h'].append(self._h_plot) self._plot_dict['n'].append(self._n_plot) if self._showLegendAction.isChecked(): for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend() def _updateAllPlots(self): self._updatePlots() self._updateStatePlot() def _updatePlots(self): if len(self.squid_setup.vm_table.vector) <= 0: return vm = numpy.asarray(self.squid_setup.vm_table.vector) cmd = numpy.asarray(self.squid_setup.cmd_table.vector) ik = numpy.asarray(self.squid_setup.ik_table.vector) # tab = self.squid_setup.ina_table # print(moose.showmsg(tab)) ina = numpy.asarray(self.squid_setup.ina_table.vector) iclamp = numpy.asarray(self.squid_setup.iclamp_table.vector) vclamp = numpy.asarray(self.squid_setup.vclamp_table.vector) gk = numpy.asarray(self.squid_setup.gk_table.vector) gna = numpy.asarray(self.squid_setup.gna_table.vector) time_series = numpy.linspace(0, self._plotdt * len(vm), len(vm)) self._vm_plot.set_data(time_series, vm) time_series = numpy.linspace(0, self._plotdt * len(cmd), len(cmd)) self._command_plot.set_data(time_series, cmd) time_series = numpy.linspace(0, self._plotdt * len(ik), len(ik)) self._ik_plot.set_data(time_series, ik) time_series = numpy.linspace(0, self._plotdt * len(ina), len(ina)) self._ina_plot.set_data(time_series, ina) time_series = numpy.linspace(0, self._plotdt * len(iclamp), len(iclamp)) self._iclamp_plot.set_data(time_series, iclamp) time_series = numpy.linspace(0, self._plotdt * len(vclamp), len(vclamp)) self._vclamp_plot.set_data(time_series, vclamp) time_series = numpy.linspace(0, self._plotdt * len(gk), len(gk)) self._gk_plot.set_data(time_series, gk) time_series = numpy.linspace(0, self._plotdt * len(gna), len(gna)) self._gna_plot.set_data(time_series, gna) # self._vm_axes.margins(y=0.1) # self._g_axes.margin(y=0.1) # self._im_axes.margins(y=0.1) # self._i_axes.margins(y=0.1) if self._autoscaleAction.isChecked(): for axis in self._plotFigure.axes: axis.relim() axis.margins(0.1, 0.1) axis.autoscale_view(tight=True) else: self._vm_axes.set_ylim(-20.0, 120.0) self._g_axes.set_ylim(0.0, 0.5) self._im_axes.set_ylim(-0.5, 0.5) self._i_axes.set_ylim(-10, 10) self._vm_axes.set_xlim(0.0, time_series[-1]) self._g_axes.set_xlim(0.0, time_series[-1]) self._im_axes.set_xlim(0.0, time_series[-1]) self._i_axes.set_xlim(0.0, time_series[-1]) self._plotCanvas.draw() def _updateStatePlot(self): if len(self.squid_setup.vm_table.vector) <= 0: return sx = str(self._stateplot_xvar_combo.currentText()) sy = str(self._stateplot_yvar_combo.currentText()) xdata = self.__get_stateplot_data(sx) ydata = self.__get_stateplot_data(sy) minlen = min(len(xdata), len(ydata)) self._state_plot.set_data(xdata[:minlen], ydata[:minlen]) self._statePlotAxes.set_xlabel(sx) self._statePlotAxes.set_ylabel(sy) if sx == 'V': self._statePlotAxes.set_xlim(-20, 120) else: self._statePlotAxes.set_xlim(0, 1) if sy == 'V': self._statePlotAxes.set_ylim(-20, 120) else: self._statePlotAxes.set_ylim(0, 1) self._activationParamAxes.set_xlim(0, self._runtime) m = self.__get_stateplot_data('m') n = self.__get_stateplot_data('n') h = self.__get_stateplot_data('h') time_series = numpy.linspace(0, self._plotdt * len(m), len(m)) self._m_plot.set_data(time_series, m) time_series = numpy.linspace(0, self._plotdt * len(h), len(h)) self._h_plot.set_data(time_series, h) time_series = numpy.linspace(0, self._plotdt * len(n), len(n)) self._n_plot.set_data(time_series, n) if self._autoscaleAction.isChecked(): for axis in self._statePlotFigure.axes: axis.relim() axis.set_autoscale_on(True) axis.autoscale_view(True) self._statePlotCanvas.draw() def _runSlot(self): if moose.isRunning(): print('Stopping simulation in progress ...') moose.stop() self._runtime = self.getFloatInput(self._runTimeEdit, self._runTimeLabel.text()) self._overlayPlots(self._overlayAction.isChecked()) self._simdt = self.getFloatInput(self._simTimeStepEdit, self._simTimeStepLabel.text()) clampMode = None singlePulse = True if self._electronicsTab.currentWidget() == self._vClampCtrlBox: clampMode = 'vclamp' baseLevel = self.getFloatInput(self._holdingVEdit, self._holdingVLabel.text()) firstDelay = self.getFloatInput(self._holdingTimeEdit, self._holdingTimeLabel.text()) firstWidth = self.getFloatInput(self._prePulseTimeEdit, self._prePulseTimeLabel.text()) firstLevel = self.getFloatInput(self._prePulseVEdit, self._prePulseVLabel.text()) secondDelay = firstWidth secondWidth = self.getFloatInput(self._clampTimeEdit, self._clampTimeLabel.text()) secondLevel = self.getFloatInput(self._clampVEdit, self._clampVLabel.text()) if not self._autoscaleAction.isChecked(): self._im_axes.set_ylim(-10.0, 10.0) else: clampMode = 'iclamp' baseLevel = self.getFloatInput(self._baseCurrentEdit, self._baseCurrentLabel.text()) firstDelay = self.getFloatInput(self._firstDelayEdit, self._firstDelayLabel.text()) firstWidth = self.getFloatInput(self._firstPulseWidthEdit, self._firstPulseWidthLabel.text()) firstLevel = self.getFloatInput(self._firstPulseEdit, self._firstPulseLabel.text()) secondDelay = self.getFloatInput(self._secondDelayEdit, self._secondDelayLabel.text()) secondLevel = self.getFloatInput(self._secondPulseEdit, self._secondPulseLabel.text()) secondWidth = self.getFloatInput( self._secondPulseWidthEdit, self._secondPulseWidthLabel.text()) singlePulse = (self._pulseMode.currentIndex() == 0) if not self._autoscaleAction.isChecked(): self._im_axes.set_ylim(-0.4, 0.4) self.squid_setup.clamp_ckt.configure_pulses(baseLevel=baseLevel, firstDelay=firstDelay, firstWidth=firstWidth, firstLevel=firstLevel, secondDelay=secondDelay, secondWidth=secondWidth, secondLevel=secondLevel, singlePulse=singlePulse) if self._kConductanceToggle.isChecked(): self.squid_setup.axon.specific_gK = 0.0 else: self.squid_setup.axon.specific_gK = SquidAxon.defaults[ 'specific_gK'] if self._naConductanceToggle.isChecked(): self.squid_setup.axon.specific_gNa = 0.0 else: self.squid_setup.axon.specific_gNa = SquidAxon.defaults[ 'specific_gNa'] self.squid_setup.axon.celsius = self.getFloatInput( self._temperatureEdit, self._temperatureLabel.text()) self.squid_setup.axon.K_out = self.getFloatInput( self._kOutEdit, self._kOutLabel.text()) self.squid_setup.axon.Na_out = self.getFloatInput( self._naOutEdit, self._naOutLabel.text()) self.squid_setup.axon.K_in = self.getFloatInput( self._kInEdit, self._kInLabel.text()) self.squid_setup.axon.Na_in = self.getFloatInput( self._naInEdit, self._naInLabel.text()) self.squid_setup.axon.updateEk() self.squid_setup.schedule(self._simdt, self._plotdt, clampMode) # The following line is for use with Qthread self.squid_setup.run(self._runtime) self._updateAllPlots() def _toggleDocking(self, on): self._channelControlDock.setFloating(on) self._electronicsDock.setFloating(on) self._runControlDock.setFloating(on) def _restoreDocks(self): self._channelControlDock.setVisible(True) self._electronicsDock.setVisible(True) self._runControlDock.setVisible(True) def _initActions(self): self._runAction = QAction(self.tr('Run'), self) self._runAction.setShortcut(self.tr('F5')) self._runAction.setToolTip('Run simulation (F5)') self._runAction.triggered.connect(self._runSlot) self._resetToDefaultsAction = QAction(self.tr('Restore defaults'), self) self._resetToDefaultsAction.setToolTip( 'Reset all settings to their default values') self._resetToDefaultsAction.triggered.connect(self._useDefaults) self._showLegendAction = QAction(self.tr('Display legend'), self) self._showLegendAction.setCheckable(True) self._showLegendAction.toggled.connect(self._showLegend) self._showStatePlotAction = QAction(self.tr('State plot'), self) self._showStatePlotAction.setCheckable(True) self._showStatePlotAction.setChecked(False) self._showStatePlotAction.toggled.connect( self._statePlotWidget.setVisible) self._autoscaleAction = QAction(self.tr('Auto-scale plots'), self) self._autoscaleAction.setCheckable(True) self._autoscaleAction.setChecked(False) self._autoscaleAction.toggled.connect(self._autoscale) self._overlayAction = QAction('Overlay plots', self) self._overlayAction.setCheckable(True) self._overlayAction.setChecked(False) self._dockAction = QAction('Undock all', self) self._dockAction.setCheckable(True) self._dockAction.setChecked(False) # self._dockAction.toggle.connect( self._toggleDocking) self._restoreDocksAction = QAction('Show all', self) self._restoreDocksAction.triggered.connect(self._restoreDocks) self._quitAction = QAction(self.tr('&Quit'), self) self._quitAction.setShortcut(self.tr('Ctrl+Q')) self._quitAction.triggered.connect(qApp.closeAllWindows) def _createRunToolBar(self): self._simToolBar = self.addToolBar(self.tr('Simulation control')) self._simToolBar.addAction(self._quitAction) self._simToolBar.addAction(self._runAction) self._simToolBar.addAction(self._resetToDefaultsAction) self._simToolBar.addAction(self._dockAction) self._simToolBar.addAction(self._restoreDocksAction) def _createPlotToolBar(self): self._plotToolBar = self.addToolBar(self.tr('Plotting control')) self._plotToolBar.addAction(self._showLegendAction) self._plotToolBar.addAction(self._autoscaleAction) self._plotToolBar.addAction(self._overlayAction) self._plotToolBar.addAction(self._showStatePlotAction) self._plotToolBar.addAction(self._helpAction) self._plotToolBar.addAction(self._helpBiophysicsAction) def _showLegend(self, on): if on: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend().set_visible(True) else: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.legend().set_visible(False) self._plotCanvas.draw() self._statePlotCanvas.draw() def _autoscale(self, on): if on: for axis in (self._plotFigure.axes + self._statePlotFigure.axes): axis.relim() axis.set_autoscale_on(True) axis.autoscale_view(True) else: for axis in self._plotFigure.axes: axis.set_autoscale_on(False) self._vm_axes.set_ylim(-20.0, 120.0) self._g_axes.set_ylim(0.0, 0.5) self._im_axes.set_ylim(-0.5, 0.5) self._i_axes.set_ylim(-10, 10) self._plotCanvas.draw() self._statePlotCanvas.draw() def _useDefaults(self): self._runTimeEdit.setText('%g' % (self.defaults['runtime'])) self._simTimeStepEdit.setText('%g' % (self.defaults['simdt'])) self._overlayAction.setChecked(False) self._naConductanceToggle.setChecked(False) self._kConductanceToggle.setChecked(False) self._kOutEdit.setText('%g' % (SquidGui.defaults['K_out'])) self._naOutEdit.setText('%g' % (SquidGui.defaults['Na_out'])) self._kInEdit.setText('%g' % (SquidGui.defaults['K_in'])) self._naInEdit.setText('%g' % (SquidGui.defaults['Na_in'])) self._temperatureEdit.setText( '%g' % (SquidGui.defaults['temperature'] - CELSIUS_TO_KELVIN)) self._holdingVEdit.setText('%g' % (SquidGui.defaults['vclamp.holdingV'])) self._holdingTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.holdingT'])) self._prePulseVEdit.setText('%g' % (SquidGui.defaults['vclamp.prepulseV'])) self._prePulseTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.prepulseT'])) self._clampVEdit.setText('%g' % (SquidGui.defaults['vclamp.clampV'])) self._clampTimeEdit.setText('%g' % (SquidGui.defaults['vclamp.clampT'])) self._baseCurrentEdit.setText('%g' % (SquidGui.defaults['iclamp.baseI'])) self._firstPulseEdit.setText('%g' % (SquidGui.defaults['iclamp.firstI'])) self._firstDelayEdit.setText('%g' % (SquidGui.defaults['iclamp.firstD'])) self._firstPulseWidthEdit.setText('%g' % (SquidGui.defaults['iclamp.firstT'])) self._secondPulseEdit.setText('%g' % (SquidGui.defaults['iclamp.secondI'])) self._secondDelayEdit.setText('%g' % (SquidGui.defaults['iclamp.secondD'])) self._secondPulseWidthEdit.setText( '%g' % (SquidGui.defaults['iclamp.secondT'])) self._pulseMode.setCurrentIndex(0) def _onScroll(self, event): if event.inaxes is None: return axes = event.inaxes zoom = 0.0 if event.button == 'up': zoom = -1.0 elif event.button == 'down': zoom = 1.0 if zoom != 0.0: self._plotNavigator.push_current() axes.get_xaxis().zoom(zoom) axes.get_yaxis().zoom(zoom) self._plotCanvas.draw() def closeEvent(self, event): qApp.closeAllWindows() def _showBioPhysicsHelp(self): self._createHelpMessage() self._helpMessageText.setText( '<html><p>%s</p><p>%s</p><p>%s</p><p>%s</p><p>%s</p></html>' % (tooltip_Nernst, tooltip_Erest, tooltip_KChan, tooltip_NaChan, tooltip_Im)) self._helpWindow.setVisible(True) def _showRunningHelp(self): self._createHelpMessage() self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL)) self._helpWindow.setVisible(True) def _createHelpMessage(self): if hasattr(self, '_helpWindow'): return self._helpWindow = QWidget() self._helpWindow.setWindowFlags(QtCore.Qt.Window) layout = QVBoxLayout() self._helpWindow.setLayout(layout) self._helpMessageArea = QScrollArea() self._helpMessageText = QTextBrowser() self._helpMessageText.setOpenExternalLinks(True) self._helpMessageArea.setWidget(self._helpMessageText) layout.addWidget(self._helpMessageText) self._squidGuiPath = os.path.dirname(os.path.abspath(__file__)) self._helpBaseURL = os.path.join(self._squidGuiPath, 'help.html') self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL)) self._helpMessageText.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._helpMessageArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._helpMessageText.setMinimumSize(800, 600) self._closeHelpAction = QAction('Close', self) self._closeHelpAction.triggered.connect(self._helpWindow.close) # Setting the close event so that the ``Help`` button is # unchecked when the help window is closed self._helpWindow.closeEvent = lambda event: self._helpAction.setChecked( False) self._helpTOCAction = QAction('Help running demo', self) self._helpTOCAction.triggered.connect(self._jumpToHelpTOC) # This panel is for putting two buttons using horizontal # layout panel = QFrame() panel.setFrameStyle(QFrame.StyledPanel + QFrame.Raised) layout.addWidget(panel) layout = QHBoxLayout() panel.setLayout(layout) self._helpAction = QAction('Help running', self) self._helpAction.triggered.connect(self._showRunningHelp) self._helpBiophysicsAction = QAction('Help biophysics', self) self._helpBiophysicsAction.triggered.connect(self._showBioPhysicsHelp) self._helpTOCButton = QToolButton() self._helpTOCButton.setDefaultAction(self._helpTOCAction) self._helpBiophysicsButton = QToolButton() self._helpBiophysicsButton.setDefaultAction(self._helpBiophysicsAction) layout.addWidget(self._helpTOCButton) layout.addWidget(self._helpBiophysicsButton) self._closeHelpButton = QToolButton() self._closeHelpButton.setDefaultAction(self._closeHelpAction) layout.addWidget(self._closeHelpButton) def _jumpToHelpTOC(self): self._helpMessageText.setSource(QtCore.QUrl(self._helpBaseURL))
def GetTextFromGroup(group: QtWidgets.QGroupBox) -> QtWidgets.QPlainTextEdit: for child in group.children(): if hasattr(child, "toPlainText"): return child
class App(QWidget): def __init__(self): super().__init__() self.title = 'Personality Grapher' self.left = 100 self.right = 100 self.top = 10 self.width = 500 self.height = 500 self.init_ui() def init_ui(self): # set window title, size and position self.setWindowTitle(self.title) self.setGeometry(self.left, self.right, self.width, self.height) # create the main layout self.main_layout = QVBoxLayout() self.error_message = QErrorMessage() # add a form with spin boxes self.create_formbox() self.main_layout.addWidget(self.form_groupbox) # add an options box for the graph self.create_optionsbox() self.main_layout.addWidget(self.options_groupbox) # add render button render_button = QPushButton('Render Graph', self) render_button.setToolTip('Render the personality graph and display it') render_button.clicked.connect(self.render_click) self.main_layout.addWidget(render_button) # finally, display the window self.setLayout(self.main_layout) self.show() def create_formbox(self): self.form_groupbox = QGroupBox('Personality Traits') self.form_layout = QFormLayout() addrow_button = QPushButton('Add Trait', self) addrow_button.setToolTip('Add a new section to the graph') addrow_button.clicked.connect(self.addrow_click) self.form_layout.addRow(QLabel('Trait'), QLabel('Value')) self.form_layout.addRow(QLineEdit(), QDoubleSpinBox(decimals=5, maximum=10)) self.form_layout.addRow(addrow_button) self.form_groupbox.setLayout(self.form_layout) def create_optionsbox(self): self.options_groupbox = QGroupBox('Graph Options') self.options_layout = QFormLayout() self.ceiling_text = QDoubleSpinBox(decimals=5, value=10.00) self.ceiling_text.valueChanged.connect(self.ceiling_changed) self.options_layout.addRow(QLabel('Graph Name'), QLineEdit()) self.options_layout.addRow(QLabel('Max Value'), self.ceiling_text) self.options_layout.addRow(QLabel('Height/Width'), QSpinBox(value=6, minimum=1)) self.options_layout.addRow(QLabel('Padding'), QSpinBox(value=4)) self.options_layout.addRow(QLabel('Font Size'), QSpinBox(value=12, minimum=1)) self.options_groupbox.setLayout(self.options_layout) # calls the render_graph function to render and then display the graph @pyqtSlot() def render_click(self): input_values = [] input_names = [] name = self.options_groupbox.children()[2].text().strip() padding = self.options_groupbox.children()[8].value() size = self.options_groupbox.children()[6].value() font = self.options_groupbox.children()[10].value() max = self.options_groupbox.children()[4].value() if len(self.form_groupbox.children()) == 3: # if the user has not added any sections, warn them self.error_message.showMessage('Cannot graph with no sections!') elif size < 1: self.error_message.showMessage( 'Height/Width must be greater than 0!') else: # get values from input boxes and pass them into graphing function children = self.form_groupbox.findChildren(QLineEdit) count = 0 while count < len(children): input_names.append(children[count].text()) count += 1 input_values.append(float(children[count].text())) count += 1 result = polar_bar.render_graph(name=name, section_names=input_names, value_list=input_values, padding=padding, max=max, size=size, font=font) if result is False: self.error_message.showMessage( 'Padding too big for Height/Width!') @pyqtSlot() def addrow_click(self): self.form_layout.insertRow( self.form_layout.rowCount() - 1, QLineEdit(), QDoubleSpinBox(decimals=5, maximum=float(self.ceiling_text.value()))) @pyqtSlot() def ceiling_changed(self): for i in self.form_groupbox.findChildren(QDoubleSpinBox): i.setMaximum(float(self.ceiling_text.value()))
class SongList(QWidget): def __init__(self, parent=None): super(SongList, self).__init__(parent) os.chdir(os.path.dirname(os.path.abspath(__file__))) resourcesPath = os.getcwd() resourcesPath = os.path.join(resourcesPath, "resources") self.PLAY_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "play.png"))) self.PAUSE_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "pause.png"))) self.STOP_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "stop.png"))) self.DELETE_ICON = QIcon(QPixmap(os.path.join(resourcesPath, "delete.png"))) self.setupMediaPlayer() self.setupUi() def setupMediaPlayer(self): self.mediaPlayer = QMediaPlayer() self.mediaPlayer.setNotifyInterval(1) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.durationChanged.connect(self.durationChanged) def setupUi(self): self.setWindowTitle("List of songs") mainLayout = QHBoxLayout(self) verticalListLayout = QVBoxLayout() self.songsListWidget = QListWidget() self.songsListWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.songsListWidget.customContextMenuRequested.connect(self.listWidgetRightClick) verticalListLayout.addWidget(self.songsListWidget) miniHorizontalLayout = QHBoxLayout() locatorLine = QLineEdit() locatorLine.setPlaceholderText("Locator") locatorBox = QComboBox() items = ["Title", "Status", "Description", "Style", "All"] locatorBox.addItems(items) locatorBox.setCurrentIndex(len(items)-1) miniHorizontalLayout.addWidget(locatorLine) miniHorizontalLayout.addWidget(locatorBox) locatorLine.textChanged.connect(lambda:self.populateList(locatorLine.text(), locatorBox.currentText())) verticalListLayout.addLayout(miniHorizontalLayout) self.mainForm = QGroupBox() self.mainForm.setTitle("Details") mainLayout.addLayout(verticalListLayout) mainLayout.addWidget(self.mainForm) self.populateList() self.mainFormSetupUi() #self.show() self.songsListWidget.currentRowChanged.connect(self.changePage) def mainFormSetupUi(self): """title, status style, duration, descriptin, location, project, variation_another_song, timestamp""" mainLayout = QVBoxLayout(self.mainForm) #Horizontal Layout 1 horizontalLayout1 = QHBoxLayout() titleLabel = QLabel("Song name:") self.titleEdit = QLineEdit() self.titleEdit.editingFinished.connect(self.checkSong) self.titleEdit.textChanged.connect(self.validateSong) horizontalLayout1.addWidget(titleLabel) horizontalLayout1.addWidget(self.titleEdit) #Horizontal Layout 2 horizontalLayout2 = QHBoxLayout() statusLabel = QLabel("Status:") self.statusBox = QComboBox() dateLabel = QLabel("Date:") self.dateEdit = QDateTimeEdit() self.dateEdit.setCalendarPopup(True) horizontalLayout2.addWidget(statusLabel) horizontalLayout2.addWidget(self.statusBox) horizontalLayout2.addStretch(1) horizontalLayout2.addWidget(dateLabel) horizontalLayout2.addWidget(self.dateEdit) #Style Groupbox, widgets added automatically self.styleGroupBox = QGroupBox() self.styleGroupBox.setTitle("Style:") self.styleLayout = QGridLayout(self.styleGroupBox) horizontalLayout3 = QHBoxLayout() durationLabel = QLabel("Duration:") self.durationLine = QTimeEdit() self.durationLine.setDisplayFormat("mm:ss") projectLabel = QLabel("Project") self.projectComboBox = QComboBox() self.projectComboBox.setEditable(True) horizontalLayout3.addWidget(durationLabel) horizontalLayout3.addWidget(self.durationLine) horizontalLayout3.addWidget(projectLabel) horizontalLayout3.addWidget(self.projectComboBox) horizontalLayout4 = QHBoxLayout() descriptionLabel = QLabel("Description:") variationLabel = QLabel("Variation from another song: ") self.variationLine = QLineEdit() horizontalLayout4.addWidget(descriptionLabel) horizontalLayout4.addStretch(1) horizontalLayout4.addWidget(variationLabel) horizontalLayout4.addWidget(self.variationLine) self.descriptionTextEdit = QTextEdit() horizontalLayout5 = QHBoxLayout() locationLabel = QLabel("Location:") self.locationLine = QLineEdit() self.locationButton = QPushButton("...") self.locationButton.clicked.connect(self.locateFile) horizontalLayout5.addWidget(locationLabel) horizontalLayout5.addWidget(self.locationLine) horizontalLayout5.addWidget(self.locationButton) horizontalLayout6 = QHBoxLayout() self.slider = QSlider(Qt.Horizontal) self.slider.sliderReleased.connect(self.playSlider) self.slider.setStyleSheet("QSlider::handle:horizontal { border: 1px solid #777; background:#b55858;}") horizontalLayout6.addWidget(self.slider) horizontalLayout7 = QHBoxLayout() self.playButton = QPushButton() self.stopButton = QPushButton() self.playButton.setIcon(self.PLAY_ICON) self.stopButton.setIcon(self.STOP_ICON) self.playButton.clicked.connect(self.playSong) self.stopButton.clicked.connect(self.stopSong) horizontalLayout7.addStretch(1) horizontalLayout7.addWidget(self.playButton) horizontalLayout7.addWidget(self.stopButton) horizontalLayout7.addStretch(1) horizontalLayout8 = QHBoxLayout() self.saveButton = QPushButton() self.saveButton.setText("Save") self.saveButton.clicked.connect(self.saveSong) horizontalLayout8.addStretch(1) horizontalLayout8.addWidget(self.saveButton) mainLayout.addLayout(horizontalLayout1) mainLayout.addLayout(horizontalLayout2) mainLayout.addWidget(self.styleGroupBox) mainLayout.addLayout(horizontalLayout3) mainLayout.addLayout(horizontalLayout4) mainLayout.addWidget(self.descriptionTextEdit) mainLayout.addLayout(horizontalLayout5) mainLayout.addLayout(horizontalLayout6) mainLayout.addLayout(horizontalLayout7) mainLayout.addLayout(horizontalLayout8) def clearForm(self): self.titleEdit.clear() self.statusBox.clear() for widget in self.styleGroupBox.children(): if not isinstance(widget, QGridLayout): widget.deleteLater() self.durationLine.clear() self.projectComboBox.clear() self.variationLine.clear() self.descriptionTextEdit.clear() self.locationLine.clear() def changePage(self, index): title = self.songsListWidget.item(index).data(Qt.UserRole) self.clearForm() self.populateForm(title) self.slider.setValue(0) def populateForm(self, title): #title is the primary key listArray = queries("""SELECT title, status, style, duration, description, location, project, variation_another_song, timestamp from songs WHERE title = ?""", (title,)) print(listArray) if len(listArray) != 0: title = listArray[0][0] status = listArray[0][1] styles = [] styleArray = listArray[0][2] if styleArray != None: if "," in styleArray: styles = styleArray.split(",") else: styles.append(styleArray) duration = listArray[0][3] description = listArray[0][4] location = listArray[0][5] project = listArray[0][6] variation_another_song = listArray[0][7] timestamp = listArray[0][8] else: title = None status = None styles = None duration = None description = None location = None project = None variation_another_song = None timestamp = None if title != None: self.titleEdit.setText(title) self.statusBox.addItems(["Select...", "Demo", "WIP", "Idea", "Unfinished song", "EQ", "Master", "Finished"]) if status != None: self.statusBox.setCurrentText(status) if timestamp != None: self.dateEdit.setDateTime(datetime.strptime(timestamp, '%d/%m/%Y %H:%M')) else: self.dateEdit.setDateTime(datetime.now())#default styleArray = queries("select style from songs where style is not null") """ print(styleArray) if styleArray != None: styleArray = styleArray[0][0] if "," in styleArray: styles = styleArray.split(",") else: styles.append(styleArray)""" stylesArray = [] query = queries("select style from songs where style is not null") if len(query) != 0: for style in query: stylesMiniArray = style[0].split(",") stylesMiniArray = list(filter(None, stylesMiniArray)) for item in stylesMiniArray: if item not in stylesArray: if item != '': stylesArray.append(item) self.x = 0 self.y = 0 if len(stylesArray) != 0: for style in stylesArray: print("style", style) checkBox = QCheckBox(style) self.styleLayout.addWidget(checkBox, self.x, self.y) self.checkBoxPositionAsignment() self.addStyle() if styles!= None: if len(styles) != 0: for style in styles: for checkbox in self.styleGroupBox.children(): if isinstance(checkbox, QCheckBox): if checkbox.text() == style: checkbox.setChecked(True) if duration != None: time = QTime(0,0,0) self.durationLine.setTime(time.addSecs(duration)) projectsArray = ["Select..."] projectsArrayQuery = queries("SELECT project from songs") if len(projectsArrayQuery) != 0: for project in projectsArrayQuery[0]: if project not in projectsArray: projectsArray.append(project) if project != None: self.projectComboBox.setCurrentText(project) if variation_another_song != None: self.variationLine.setText(variation_another_song) if description != None: self.descriptionTextEdit.setText(description) available = False if location != None: self.locationLine.setText(location) if len(self.locationLine.text()) != 0: try: self.playlist = QMediaPlaylist() self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(location))) self.mediaPlayer.setPlaylist(self.playlist) except: pass available = True#I know this is stupid but just in case self.slider.setVisible(available) self.playButton.setVisible(available) self.stopButton.setVisible(available) def populateList(self, locatorItem=None, locatorColumn=None): print(locatorItem, locatorColumn) self.songsListWidget.blockSignals(True) self.songsListWidget.clear() if locatorItem == None or locatorItem == "": listArray = queries("""SELECT title, status, timestamp from songs """) print(listArray) else: if locatorColumn != "All": #No strings concatenation, no security holes if locatorColumn == "Title": sql = """SELECT title, status, timestamp from songs where title LIKE ?""" elif locatorColumn == "Status": sql = """SELECT title, status, timestamp from songs where status LIKE ?""" elif locatorColumn == "Description": sql = """SELECT title, status, timestamp from songs where description LIKE ?""" elif locatorColumn == "Style": sql = """SELECT title, status, timestamp from songs where style LIKE ?""" locatorItem = "%" + locatorItem + "%" listArray = queries(sql, (locatorItem,)) else: locatorItem = "%" + locatorItem + "%" variables = [locatorItem, locatorItem, locatorItem, locatorItem, locatorItem] listArray = queries("""SELECT title, status, timestamp from songs where title LIKE ? OR type LIKE ? OR original_song LIKE ? OR link LIKE ? OR description LIKE ?""", variables) for item in listArray: title = item[0] status = item[1] timestamp = item[2] try: timestamp = datetime.strptime(timestamp, "%d/%m/%Y %H:%M") timestamp = timestamp.strftime("%d/%m/%Y") except: timestamp = "" text = "%s %s %s" % (title, status, timestamp) qItem = QListWidgetItem(text) qItem.setData(Qt.UserRole, title) self.songsListWidget.addItem(qItem) #new idea qItem = QListWidgetItem("New song...") qItem.setData(Qt.UserRole, "New song...") #otherwise that would be an error self.songsListWidget.addItem(qItem) self.songsListWidget.blockSignals(False) def listWidgetRightClick(self, position): widgetItem = self.songsListWidget.itemAt(position) if widgetItem != None: #quick lazy text fix if widgetItem.text() != "New song...": print(widgetItem.text()) menu = QMenu() deleteAction = QAction(self.DELETE_ICON, "Delete song") menu.addAction(deleteAction) action = menu.exec(self.mapToGlobal(position)) if action == deleteAction: msg = QMessageBox.question(None, "Delete?", "Are you sure you want to delete this entry?") if msg == QMessageBox.Yes: title = widgetItem.data(Qt.UserRole) queries("DELETE from songs where title = ?", (title,)) self.populateList() self.songsListWidget.setCurrentRow(0) def songVariations(self): sql = "SELECT title from songs" songArray = [] for song in queries(sql)[0]: songArray.append(song) return songArray def checkBoxPositionAsignment(self): self.y += 1 if self.y == 4: self.y = 0 self.x += 1 def addStyle(self, text=""): "text = "" if comes from outside" self.styleEdit = QLineEdit() self.styleEdit.setPlaceholderText("Style") self.styleEdit.textChanged.connect(self.validateStyle) self.styleEdit.returnPressed.connect(lambda: self.addStyle(self.styleEdit.text())) if text != "": self.styleLayout.takeAt(self.styleLayout.count()-1).widget().deleteLater() styleCheckBox = QCheckBox() styleCheckBox.setText(text) print(text) self.styleLayout.addWidget(styleCheckBox, self.x, self.y) self.checkBoxPositionAsignment() print(self.durationLine.text()) self.styleLayout.addWidget(self.styleEdit) def checkSong(self): text = self.titleEdit.text() sql = "SELECT title from songs where title = ?" if len(queries(sql, (text,))) != 0: self.titleEdit.setText("") def validateSong(self): pass #VALIDATE REG EXP def validateStyle(self, text): if "," in text: self.styleEdit.undo() def playSong(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon(self.PAUSE_ICON) else: self.playButton.setIcon(self.PLAY_ICON) def positionChanged(self, position): if position != self.mediaPlayer.duration(): self.slider.setValue(position) def durationChanged(self, duration): if duration != self.mediaPlayer.position(): print("duration chagned") self.slider.setRange(0, duration) def playSlider(self): self.mediaPlayer.setPosition(self.slider.value()) def stopSong(self): self.mediaPlayer.stop() def locateFile(self): self.fileSystem = QFileDialog(filter="Sound files (*.wav *.mp3 *.flac)") self.fileSystem.show() self.fileSystem.fileSelected.connect(self.fileLoaded) def fileLoaded(self, path): self.locationLine.setText(path) try: self.playlist = QMediaPlaylist() self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path))) self.mediaPlayer.setPlaylist(self.playlist) except: print("fail") self.slider.setVisible(True) self.playButton.setVisible(True) self.stopButton.setVisible(True) def saveSong(self): title = self.titleEdit.text() status = self.statusBox.currentText() date = self.dateEdit.text() style = "" print(status, style) x = 0 for checkBox in self.styleGroupBox.children(): if isinstance(checkBox, QCheckBox): if checkBox.isChecked(): style += (checkBox.text()) + "," x+=1 if x != 0: style = style.rstrip(",") else: style = None duration = self.durationLine.time() duration = QTime(0, 0).secsTo(duration) project = self.projectComboBox.currentText() variation = self.variationLine.text() description = self.descriptionTextEdit.toPlainText() location = self.locationLine.text() variables = [title, status, description, location, project,\ variation, date, style, duration] print("---------", variables) sql = """INSERT OR REPLACE into songs (title, status, description, location, project, variation_another_song, timestamp, style, duration) values (?, ?, ?, ?, ?, ?, ?, ?, ?)""" queries(sql, variables) self.populateList()
class Multidialog(QDialog): def __init__(self, screen, title, form_set, buttons=None, button_pressed=lambda:None): if screen in window.screens: self.screen = screen super(Multidialog, self).__init__(window.screens[screen]) else: self.screen = None self.form_set = form_set self.elements = {} self.title = title self.setWindowTitle(self.title) self.main_layout = QVBoxLayout() self.group_box = QGroupBox() self.first_launch = True layout = QFormLayout() for f in self.form_set: if not 'name' in f: continue else: self.elements[f['name']] = {k:f[k] for k in f} e, l = self.get_element(etype=f['type'], settings=f['settings'] if 'settings' in f else None, data=f['data'] if 'data' in f else None, label=f['label'] if 'label' in f else None) self.elements[f['name']]['object'], self.elements[f['name']]['label'] = e, l self.elements[f['name']]['callback'] = False if 'settings' in f: self.elements[f['name']]['settings'] = f['settings'] layout.addRow(self.elements[f['name']]['label'], self.elements[f['name']]['object']) self.initialize_settings() if not buttons: self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.button_box.accepted.connect(self.accept) self.button_box.rejected.connect(self.reject) else: self.button_box = QDialogButtonBox(QDialogButtonBox.Reset | QDialogButtonBox.Ok | QDialogButtonBox.Cancel) #this needs some work self.group_box.setLayout(layout) self.main_layout.addWidget(self.group_box) self.main_layout.addWidget(self.button_box) self.setLayout(self.main_layout) self.setFixedSize(self.main_layout.sizeHint()) def get_element(self, etype, settings, data, label): widget = None label = QLabel(label) if etype.lower() == "input": le = QLineEdit() if data and isinstance(data, str): le.setText(data) if settings and 'mode' in settings: if settings["mode"] == "password": le.setEchoMode(QLineEdit.Password) widget = le elif etype.lower() == "dropdown": cb = QComboBox() cb.setFocusPolicy(Qt.StrongFocus) # bugged on enter activate; to-do # # QShortcut(QKeySequence(Qt.Key_Return), cb) # cb.enterPressed.activated.connect(lambda: cb.showPopup) if data: if isinstance(data, list): cb.addItems(data) elif isinstance(data, str): cb.addItem(data) widget = cb elif etype.lower() == "color": cb = ColorButton() if settings and 'pref' in settings: cb.pref = settings['pref'] cb.color = prefs.get_pref(cb.pref) elif 'color' in settings: cb.color = settings['color'] cb.updateColor(cb.color) widget = cb return widget, label def initialize_settings(self): for el in self.elements: e = self.elements[el] if 'settings' in e: settings = e['settings'] if 'conditional_show' in settings: if 'element' in settings['conditional_show']: if settings['conditional_show']['element'] in self.elements: ref_e = settings['conditional_show']['element'] if 'callback' in self.elements[ref_e] and not self.elements[ref_e]['callback']: if 'object' in self.elements[ref_e] and isinstance(self.elements[ref_e]['object'], QComboBox): self.elements[ref_e]['object'].currentIndexChanged.connect(self.initialize_settings) self.elements[ref_e]['callback'] = True if 'object' in self.elements[ref_e]: if 'not_state' in settings['conditional_show']: sv = not (self.elements[ref_e]['object'].currentText() in settings['conditional_show']['not_state']) e['object'].setVisible(sv) e['label'].setVisible(sv) elif 'state' in settings['conditional_show']: sv = (self.elements[ref_e]['object'].currentText() in settings['conditional_show']['state']) e['object'].setVisible(sv) e['label'].setVisible(sv) def refresh(self): for w in self.group_box.children(): if isinstance(w, QLineEdit): w.setText("") elif isinstance(w, QComboBox): if w.count() > 0: w.setCurrentIndex(0)