def __init__(self, ): super(ChangeLog, self).__init__() self.setWindowTitle('pyIMD :: What\'s new in pyIMD') self.setFixedSize(1100, 500) self.setWindowIcon( QtGui.QIcon( resource_path( os.path.join( os.path.join("ui", "icons", "pyIMD_logo_icon.ico"))))) self.setWindowFlags(Qt.WindowCloseButtonHint) h_box = QVBoxLayout() v_box = QHBoxLayout() grid_h = QGridLayout() grid_v = QGridLayout() self.change_log_text = QTextBrowser() self.cancel_btn = QPushButton('Close', self) self.cancel_btn.clicked.connect(self.close_window) grid_v.addWidget(self.cancel_btn, 1, 1) v_box.addStretch() v_box.addLayout(grid_v) grid_h.addWidget(self.change_log_text) h_box.addLayout(grid_h) h_box.addLayout(v_box) self.setLayout(h_box) change_log = open(resource_path('change_log.txt')).read() self.change_log_text.setText(change_log)
def testReadPyimdProject(self): settings = Settings() # Change a settings field settings.figure_format = 'pdf' # Save changes settings.write_pyimd_project(resource_path('TestConfig.xml')) # Change back to default settings.figure_format = 'png' # Observe change back to what was saved to file settings.read_pyimd_project(resource_path('TestConfig.xml')) expected_result = { '_figure_units': 'cm', '_figure_name_pre_start_with_cell': 'FitWithCellData', '_calculation_mode': 'PLL', '_figure_name_measured_data': 'CalculatedCellMass', '_pre_start_with_cell_path': '', '_measurements_path': '', '_upper_parameter_bounds': [100.0, 5.0, 3, 3], '_conversion_factor_deg_to_rad': -57.3, '_initial_parameter_guess': [70.0, 2.0, 0.0, 0.0], '_lower_parameter_bounds': [10.0, 1.0, -3, -3], '_cantilever_length': 100, '_rolling_window_size': 1000, '_figure_width': 56.44, '_conversion_factor_hz_to_khz': 1000.0, '_figure_plot_every_nth_point': 1, '_project_folder_path': '', '_selected_files': [], '_figure_resolution_dpi': 72, '_correct_for_frequency_offset': False, '_frequency_offset_mode': 'Auto', '_frequency_offset_n_measurements_used': 1, '_frequency_offset': 0, '_pre_start_no_cell_path': '', '_read_text_data_from_line': 23, '_figure_name_pre_start_no_cell': 'FitNoCellData', '_text_data_delimiter': '\t', '_spring_constant': 4.0, '_figure_format': 'pdf', '_cell_position': 5, '_figure_height': 45.16 } self.assertEqual(settings.__dict__, expected_result)
def __init__(self, parent=None): super(QuickInstructions, self).__init__(parent) # self.setAttribute(Qt.WA_DeleteOnClose) # Deletes instance on window close self.setWindowTitle('pyIMD :: Quick instructions') self.display_on_startup = 2 self.resize(400, 370) self.setWindowIcon( QtGui.QIcon( resource_path( os.path.join( os.path.join("ui", "icons", "pyIMD_logo_icon.ico"))))) grid = QGridLayout() grid.setContentsMargins(5, 5, 5, 5) ok = QPushButton('Ok') ok.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogApplyButton)) ok.setMaximumWidth(100) ok.clicked.connect(self.close) self.chk = QCheckBox('Display quick instructions at startup') self.chk.setFont(QFont('Arial', 9, QFont.Bold)) self.chk.setChecked(1) self.chk.clicked.connect(self.on_display_qi) self.quick_instructions = QTextEdit(self) self.quick_instructions.setText( '<h3>Quick Instructions</h3> ' 'Edit the settings first according to your experimental setup. Second select ' 'the directory containing your experimental data and determine the file name ' 'to measurement relationship as well as the measurement mode the data was ' 'recorded with.' '<br><br>Controls:' '<ul>' '<li><b>Ctrl+N (Cmd+N):</b> Create a new pyIMD project.' '</li>' '<li><b>Ctrl+O (Cmd+O):</b> Open an existing pyIMD project' '</li>' '<li><b>Ctrl+S (Cmd+S):</b> Save the current pyIMD project' '</li>' '<li><b>Ctrl+P (Cmd+P):</b> Open the settings dialog to change the project ' 'parameters</li></ul>' 'Hints:' '<ul><li>Create a pyIMD project for all your experiments first and save ' 'the pyIMD projects before running them individually. The projects can then ' 'be run sequentially using the batch mode (Batch processing tab).' '</li>' '<li>Select multiple data files holding the Ctrl button during selection after' 'clicking on <i>Select directory</i> or Ctrl+N.</li><' '</ul>' 'You can open this window any time from the Help menu.</ul>') self.quick_instructions.setReadOnly(1) self.quick_instructions.setFont(QFont('Arial', 9)) grid.addWidget(self.quick_instructions, 0, 0, 1, 0) grid.addWidget(ok, 1, 1) grid.addWidget(self.chk, 1, 0) self.setLayout(grid)
def close_application(self, event): """ Opens a message box to handle program exit properly asking the user if the project should be saved first. Args: event(`QCloseEvent`): A QCloseEvent Returns: status_code (`int`): 0 when process finished correctly, otherwise >0 """ self.settings.setValue('display_on_startup', self.qi.display_on_startup) msg_box = QMessageBox() msg_box.setWindowIcon( QtGui.QIcon( resource_path( os.path.join("ui", "icons", "pyIMD_logo_icon.ico")))) msg_box.setWindowTitle('pyIMD :: Quit Program') msg_box.setText( 'Do you want to save changes before quitting the program?') save_btn = QPushButton('Save') save_btn.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogSaveButton)) msg_box.addButton(save_btn, QMessageBox.YesRole) no_save_btn = QPushButton('Don\'t save') no_save_btn.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogNoButton)) msg_box.addButton(no_save_btn, QMessageBox.NoRole) abort_btn = QPushButton('Cancel') abort_btn.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogCancelButton)) msg_box.addButton(abort_btn, QMessageBox.RejectRole) ret = msg_box.exec_() if ret == 0: self.save_project() if not event: self.close() else: event.accept() elif ret == 1: if not event: self.close() else: event.accept() else: self.print_to_console("Program quit aborted") if not event: return else: event.ignore()
def __init__(self, parent=None): super(ConcatenateFiles, self).__init__(parent) self.setWindowTitle('pyIMD :: Concatenate data files') self.resize(150, 150) self.setWindowIcon( QtGui.QIcon( resource_path( os.path.join( os.path.join("ui", "icons", "pyIMD_logo_icon.ico"))))) self.directory = [] grid = QGridLayout() grid.setContentsMargins(5, 5, 5, 5) self.intro_label = QLabel( 'Concatenate measurement data saved in multiple files into a single one.' ) self.time_label = QLabel( 'Enter the acquisition time interval (in seconds):') self.time_interval_edit = QLineEdit(self) self.time_interval_edit.textChanged.connect( self.on_time_interval_changed) validator = QtGui.QDoubleValidator() self.time_interval_edit.setValidator(validator) self.directory_label = QLabel( 'Select directory containing all files to be concatenated:') self.directory_edit = QLineEdit(self) self.directory_edit.setStyleSheet("background-color: transparent; ") self.directory_edit.setReadOnly(1) self.directory_edit.textChanged.connect(self.on_directory_changed) self.select_dir_btn = QPushButton('Directory') self.select_dir_btn.clicked.connect(self.on_dir_selection) buttons = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) buttons.accepted.connect(self.on_accept) buttons.rejected.connect(self.reject) grid.addWidget(self.intro_label, 0, 0, 1, 2) grid.addWidget(self.directory_label, 1, 0) grid.addWidget(self.select_dir_btn, 1, 1) grid.addWidget(self.directory_edit, 2, 0, 1, 2) grid.addWidget(self.time_label, 3, 0) grid.addWidget(self.time_interval_edit, 3, 1) grid.addWidget(buttons, 5, 1) self.setLayout(grid)
def __init__(self): super(IMDWindow, self).__init__() uic.loadUi(resource_path(os.path.join('ui', 'main_window.ui')), self) self.setWindowTitle( 'pyIMD: Inertial mass determination [build: v%s %s]' % (__version__, __operating_system__)) self.setWindowIcon( QtGui.QIcon( resource_path( os.path.join( os.path.join("ui", "icons", "pyIMD_logo_icon.ico"))))) # Add AppUserModelID for windows systems if sys.platform == 'win32': app_id = u'ethz.csb.pyCAME.v%s' % __version__ ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( app_id) # Init QSettings for cross-platform temp settings file in ini format self.settings = QSettings(QSettings.IniFormat, QSettings.SystemScope, 'CSB', 'pyIMD') self.menuBar.setNativeMenuBar(False) self.settings_dialog = None self.about_window = None self.file_list = [] self.current_batch_project_file = [] self.last_selected_path = '' self.show() self.console_edit.setReadOnly(True) self.radio_btn_name_array = ['autoRadio', 'pllRadio', 'contSweepRadio'] self.opening_mode = 0 # intended to be used to distinguish if the ui is started as stand alone or not. self.task_done = False self.executor = ThreadPoolExecutor(max_workers=4) # Settings self.__settings = { "figure_format": FIGURE_FORMAT, "figure_width": FIGURE_WIDTH, "figure_height": FIGURE_HEIGHT, "figure_units": FIGURE_UNITS, "figure_resolution_dpi": FIGURE_RESOLUTION_DPI, "figure_name_pre_start_no_cell": FIGURE_NAME_PRE_START_NO_CELL, "figure_name_pre_start_with_cell": FIGURE_NAME_PRE_START_WITH_CELL, "figure_name_measured_data": FIGURE_NAME_MEASURED_DATA, "figure_plot_every_nth_point": FIGURE_PLOT_EVERY_NTH_POINT, "conversion_factor_hz_to_khz": CONVERSION_FACTOR_HZ_TO_KHZ, "conversion_factor_deg_to_rad": CONVERSION_FACTOR_DEG_TO_RAD, "spring_constant": SPRING_CONSTANT, "cantilever_length": CANTILEVER_LENGTH, "cell_position": CELL_POSITION, "initial_parameter_guess": INITIAL_PARAMETER_GUESS, "lower_parameter_bounds": LOWER_PARAMETER_BOUNDS, "upper_parameter_bounds": UPPER_PARAMETER_BOUNDS, "rolling_window_size": ROLLING_WINDOW_SIZE, "correct_for_frequency_offset": CORRECT_FOR_FREQUENCY_OFFSET, "frequency_offset_mode": FREQUENCY_OFFSET_MODE, "frequency_offset_n_measurements_used": FREQUENCY_OFFSET_N_MEASUREMENTS_USED, "frequency_offset": FREQUENCY_OFFSET, "read_text_data_from_line": READ_TEXT_DATA_FROM_LINE, "text_data_delimiter": repr(TEXT_DATA_DELIMITER).replace("'", "") } self.settings_dialog = SettingsDialog(self.__settings) self.settings_dialog.settings_has_changed_signal.connect( self.on_settings_changed) self.settings_dialog.set_values() self.setup_console_connection() self.selectDirBtn.clicked.connect(self.select_data_files) self.selectDirBtn.setShortcut("Ctrl+N") data_items = [ 'Measured data', 'Pre start no cell data', 'Pre start with cell data', 'Pre start frequency shift', 'Calculated cell mass' ] for i in range(0, len(data_items)): self.dataList.addItem(str(data_items[i])) self.dataList.itemSelectionChanged.connect( self.on_data_list_selection_changed) self.noCellDataBox.currentIndexChanged.connect( self.on_combo_box_changed) self.withCellDataBox.currentIndexChanged.connect( self.on_combo_box_changed) self.measuredDataBox.currentIndexChanged.connect( self.on_combo_box_changed) self.runCalculationBtn.clicked.connect(self.run_calculation) self.projectFilesBtn.clicked.connect(self.select_batch_files) self.runBatchBtn.clicked.connect(self.run_batch_calculation) self.actionView_Console.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogApplyButton)) self.actionView_Console.setShortcut("Ctrl+C") self.actionView_Console.setStatusTip('Show / hide console console') self.actionView_Console.triggered.connect(self.show_console) self.actionQuit.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogCloseButton)) self.actionQuit.setShortcut("Ctrl+Q") self.actionQuit.setStatusTip('Quit the application') self.actionQuit.triggered.connect(self.close_application) self.actionSave_project.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogSaveButton)) self.actionSave_project.setShortcut("Ctrl+S") self.actionSave_project.setStatusTip('Save a pyIMD project') self.actionSave_project.triggered.connect(self.save_project) self.actionOpen_project.setIcon(QApplication.style().standardIcon( QStyle.SP_DirOpenIcon)) self.actionOpen_project.setShortcut("Ctrl+O") self.actionOpen_project.setStatusTip('Open a pyIMD project') self.actionOpen_project.triggered.connect(self.open_project) self.actionSettings.setIcon(QApplication.style().standardIcon( QStyle.SP_FileDialogDetailedView)) self.actionSettings.setShortcut("Ctrl+P") self.actionSettings.setStatusTip( 'Configure pyIMD calculation settings') self.actionSettings.triggered.connect(self.show_settings_dialog) tools_menu = self.menuBar.addMenu('Tools') action_concat = tools_menu.addAction("Concatenate files") self.concatenation = ConcatenateFiles() action_concat.triggered.connect(self.on_concatenation) self.about_window = About() self.actionAbout.setShortcut("Ctrl+A") self.actionAbout.triggered.connect(self.on_about) self.qi = QuickInstructions() self.actionQuick_instructions.setStatusTip( 'Hints about how to use this program') self.actionQuick_instructions.triggered.connect( self.on_quick_instructions) self.change_log = ChangeLog() self.actionChange_log.setStatusTip('See what change recently') self.actionChange_log.triggered.connect(self.on_change_log) self.actionRead_documentation.setStatusTip('Show online documentation') self.actionRead_documentation.triggered.connect( self.on_read_documentation) sys.stderr = Stream(stream_signal=self.on_update_text) self.batchFileListWidget.setSelectionMode(QListWidget.MultiSelection) self.tabWidget.setTabEnabled(2, False) self.tabWidget.setCurrentIndex(0) self.graphicsView.plotItem.ctrlMenu = None self.imd_icon = QGraphicsSvgItem( resource_path( os.path.join(os.path.join("ui", "icons", "pyIMD_logo_vect.svg")))) self.imd_icon.scale(1, -1) self.graphicsView.addItem(self.imd_icon) self.graphicsView.hideAxis('bottom') self.graphicsView.hideAxis('left') self.logger = self.get_logger_object(__name__) self.logger.setLevel(logging.INFO) self.imd = InertialMassDetermination() try: if self.settings.value('display_on_startup') is None: self.settings.setValue('display_on_startup', 2) self.qi.show() elif int(self.settings.value('display_on_startup')) == 2: self.qi.show() except Exception as e: self.print_to_console( 'Could not load quick instruction window due to corrupt settings.ini file' + str(e))
event.accept() else: self.print_to_console("Program quit aborted") if not event: return else: event.ignore() def closeEvent(self, event): """ Application close event override of QMainWindow closeEvent Args: event (`QCloseEvent`): A QCloseEvent Returns: status_code (`int`): O when process finished correctly otherwise >0 """ self.close_application(event) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) main = IMDWindow() app_icon = QtGui.QIcon() app_icon.addFile( resource_path(os.path.join("icons", "pyIMD_logo_icon.ico")), QSize(256, 256)) app.setWindowIcon(app_icon) sys.exit(app.exec_())
def __init__(self, ): super(About, self).__init__() self.setWindowTitle('pyIMD :: About') self.setFixedSize(320, 450) self.setWindowFlags(Qt.WindowCloseButtonHint) about_icon = QIcon() about_icon.addPixmap(self.style().standardPixmap( QStyle.SP_FileDialogInfoView)) self.setWindowIcon(about_icon) self.le = QLabel() self.build = QLabel("[build: v%s %s bit]" % (__version__, __operating_system__)) self.author = QLabel( "pyIMD: Inertial mass determination. \nWritten by Andreas P. Cuny and Gotthold\nFläschner" ) self.license = QLabel("Licensed under the GPL v3 license.") self.copyright = QLabel( "\u00a9 Copyright Andreas P. Cuny \n2018-2019. All rights reserved. \ \nCSB Laboratory @ ETH Zurich\nMattenstrasse 26 \n4058 Basel Switzerland" ) self.dependencies = QTextBrowser() self.dependencies.setHtml( "The authors appreciate and use the following 3rd parties libraries: <br> \ <br>Python v3.5, under the <a href=https://docs.python.org/3/license.html>PSF License</a> \ <br>lxml, under the <a href=https://github.com/lxml/lxml/blob/master/LICENSES.txt>BSD 3-Clause License</a> \ <br>numpy v1.14.5, under the <a href=https://docs.scipy.org/doc/numpy-1.10.0/license.html>BSD 3-Clause License</a> \ <br>scipy v1.1.0, under the <a href=https://docs.scipy.org/doc/numpy-1.10.0/license.html>BSD 3-Clause License</a> \ <br>pandas v0.23.3, under the <a href=https://pandas.pydata.org/pandas-docs/stable/getting_started/overview.html>BSD 3-Clause License</a> \ <br>nptdms v0.12.0, under the <a href=https://github.com/adamreeve/npTDMS>GPL v3 License</a> \ <br>tqdm v4.23.4, under the <a href=https://github.com/tqdm/tqdm/blob/master/LICENCE>MIT License</a> \ <br>plotnine v0.3.0, under the <a href=https://github.com/has2k1/plotnine/blob/master/LICENSE>GPL v2 License</a> \ <br>PyQT5, under the <a href=https://www.riverbankcomputing.com/static/Docs/PyQt5/introduction.html#license>GPL v3 License</a> \ <br>xmltodict, under the <a href=https://github.com/martinblech/xmltodict/blob/master/LICENSE>MIT License</a> \ <br>matplotlib, under the <a href=https://matplotlib.org/devel/license.html>PSF License</a>\ <br>pyqtgraph, under the <a href=https://github.com/pyqtgraph/pyqtgraph/blob/develop/LICENSE.txt>MIT License</a>\ <br>xmlunittest, under the <a href=https://github.com/Exirel/python-xmlunittest/blob/master/LICENSE>MIT License</a>" ) self.dependencies.setReadOnly(True) self.dependencies.setOpenExternalLinks(True) self.le.setAlignment(Qt.AlignCenter) self.build.setAlignment(Qt.AlignCenter) font_b = QtGui.QFont() font_b.setPointSize(9) self.build.setFont(font_b) font = QtGui.QFont() font.setPointSize(11) self.author.setFont(font) self.copyright.setFont(font) self.dependencies.setFont(font_b) logo = QtGui.QPixmap( resource_path( os.path.join(os.path.join("ui", "icons", "pyIMD_logo.png")))) logo = logo.scaled(250, 250, Qt.KeepAspectRatio) self.le.setPixmap(logo) v_box = QVBoxLayout() v_box.addWidget(self.le) v_box.addWidget(self.build) v_box.addWidget(self.license) v_box.addStretch() v_box.addWidget(self.author) v_box.addStretch() v_box.addWidget(self.copyright) v_box.addWidget(self.dependencies) v_box.addStretch() self.setLayout(v_box) p = self.palette() p.setColor(self.backgroundRole(), Qt.white) self.setPalette(p)
def testWritePyimdProject(self): # Create the inertial mass determination object settings = Settings() # Save the project settings.write_pyimd_project(resource_path('TestConfig.xml')) with open( 'TestConfig.xml', 'rb', ) as myfile: data = myfile.read() # Everything starts with `assertXmlDocument` root = self.assertXmlDocument(data) # Check self.assertXmlNode(root, tag='PyIMDSettings') self.assertXpathValues(root, './GeneralSettings/figure_format/text()', 'png') self.assertXpathValues(root, './GeneralSettings/figure_width/text()', '56.44') self.assertXpathValues(root, './GeneralSettings/figure_height/text()', '45.16') self.assertXpathValues(root, './GeneralSettings/figure_units/text()', 'cm') self.assertXpathValues( root, './GeneralSettings/figure_resolution_dpi/text()', '72') self.assertXpathValues( root, './GeneralSettings/figure_name_pre_start_no_cell/text()', 'FitNoCellData') self.assertXpathValues( root, './GeneralSettings/figure_name_pre_start_with_cell/text()', 'FitWithCellData') self.assertXpathValues( root, './GeneralSettings/figure_name_measured_data/text()', 'CalculatedCellMass') self.assertXpathValues( root, './GeneralSettings/figure_plot_every_nth_point/text()', '1') self.assertXpathValues( root, './GeneralSettings/conversion_factor_hz_to_khz/text()', '1000.0') self.assertXpathValues( root, './GeneralSettings/conversion_factor_deg_to_rad/text()', '-57.3') self.assertXpathValues(root, './GeneralSettings/spring_constant/text()', '4.0') self.assertXpathValues(root, './GeneralSettings/cantilever_length/text()', '100') self.assertXpathValues(root, './GeneralSettings/cell_position/text()', '9.5') self.assertXpathValues( root, './GeneralSettings/initial_parameter_guess/text()', '[70.0, 2.0, 0.0, 0.0]') self.assertXpathValues( root, './GeneralSettings/lower_parameter_bounds/text()', '[10.0, 1.0, -3, -3]') self.assertXpathValues( root, './GeneralSettings/upper_parameter_bounds/text()', '[100.0, 5.0, 3, 3]') self.assertXpathValues(root, './GeneralSettings/rolling_window_size/text()', '1000') self.assertXpathValues(root, './GeneralSettings/frequency_offset/text()', '0') self.assertXpathValues( root, './GeneralSettings/read_text_data_from_line/text()', '23') self.assertXpathValues(root, './GeneralSettings/text_data_delimiter/text()', '\t') self.assertXpathValues( root, './ProjectSettings/selected_files/File/text()', ('20190110_ShowCase_PLL_A.txt', '20190110_ShowCase_PLL_B.txt', '20190110_ShowCase_PLL_LongTerm.txt')) self.assertXpathValues(root, './ProjectSettings/project_folder_path/text()', '') self.assertXpathValues( root, './ProjectSettings/pre_start_no_cell_path/text()', '') self.assertXpathValues( root, './ProjectSettings/pre_start_with_cell_path/text()', '') self.assertXpathValues(root, './ProjectSettings/measurements_path/text()', '') self.assertXpathValues(root, './ProjectSettings/calculation_mode/text()', 'PLL') self.assertXpathsUniqueValue(root, ('./leaf/@id', )) self.assertXpathValues(root, './leaf/@active', ('on', 'off'))
def __init__(self, settings_dictionary): """ Settings user interface (UI) constructor. Returns: QDialog (`obj`): Settings ui object """ super(SettingsDialog, self).__init__() uic.loadUi(resource_path(os.path.join('ui', 'setting_dialog.ui')), self) self.setWindowTitle('pyIMD :: Settings') self.settingsIcon = QIcon() self.settingsIcon.addPixmap( self.style().standardPixmap(QStyle.SP_FileDialogDetailedView), QIcon.Disabled, QIcon.Off) self.setWindowIcon(self.settingsIcon) self.frequency_offset_spin.setMinimum(1) self.frequency_offset_spin.setMaximum(10**9) self.settings_dictionary = settings_dictionary # Establish connections self.defaultBtn.clicked.connect(self.set_defaults) self.commitBtn.clicked.connect(self.commit_parameters) self.cancelBtn.clicked.connect(self.close_settings_dialog) self.do_offset_corr_chkbox.clicked.connect( self.on_toggle_frequency_offset) self.offsetGroupBox.setEnabled(False) self.frequency_offset_edit.setEnabled(False) self.frequency_offset_mode_auto.setChecked(True) self.frequency_offset_mode_auto.toggled.connect( self.on_frequency_offset_mode_auto) self.frequency_offset_mode_manual.toggled.connect( self.on_frequency_offset_mode_manual) double_validator = QDoubleValidator() self.figure_width_edit.setValidator(double_validator) self.figure_width_edit.textChanged.connect(self.check_state) self.figure_height_edit.setValidator(double_validator) self.figure_height_edit.textChanged.connect(self.check_state) self.figure_resolution_edit.setValidator(double_validator) self.figure_resolution_edit.textChanged.connect(self.check_state) self.conversion_factor_hz_edit.setValidator(double_validator) self.conversion_factor_hz_edit.textChanged.connect(self.check_state) self.conversion_factor_deg_edit.setValidator(double_validator) self.conversion_factor_deg_edit.textChanged.connect(self.check_state) self.spring_constant_edit.setValidator(double_validator) self.spring_constant_edit.textChanged.connect(self.check_state) self.cantilever_length_edit.setValidator(double_validator) self.cantilever_length_edit.textChanged.connect(self.check_state) self.cell_position_edit.setValidator(double_validator) self.cell_position_edit.textChanged.connect(self.check_state) self.read_text_data_from_line_edit.setValidator(double_validator) self.read_text_data_from_line_edit.textChanged.connect( self.check_state) self.figure_plot_every_nth_point_edit.setValidator(double_validator) self.figure_plot_every_nth_point_edit.textChanged.connect( self.check_state) self.rolling_window_size_edit.setValidator(double_validator) self.rolling_window_size_edit.textChanged.connect(self.check_state) self.frequency_offset_edit.setValidator(double_validator) self.frequency_offset_edit.textChanged.connect(self.check_state) reg_ex = QRegExp("[0-9-a-z-A-Z_]+") self.figure_name_wo_cell_edit.setValidator( QRegExpValidator(reg_ex, self.figure_name_wo_cell_edit)) self.figure_name_wo_cell_edit.textChanged.connect(self.check_state) self.figure_name_w_cell_edit.setValidator( QRegExpValidator(reg_ex, self.figure_name_w_cell_edit)) self.figure_name_w_cell_edit.textChanged.connect(self.check_state) self.figure_name_data_edit.setValidator( QRegExpValidator(reg_ex, self.figure_name_data_edit)) self.figure_name_data_edit.textChanged.connect(self.check_state) self.figure_unit_edit.setValidator( QRegExpValidator(QRegExp("[a-z-A-Z_]+"), self.figure_name_wo_cell_edit)) self.figure_unit_edit.textChanged.connect(self.check_state) self.figure_format_edit.setValidator( QRegExpValidator(QRegExp(".+[a-z-A-Z_]"), self.figure_name_wo_cell_edit)) self.figure_format_edit.textChanged.connect(self.check_state) self.text_data_delimiter_edit.setValidator( QRegExpValidator(QRegExp("(?:\\\{1}\w*)"), self.text_data_delimiter_edit)) self.text_data_delimiter_edit.textChanged.connect(self.check_state) self.intial_param_guess_edit.setValidator( QRegExpValidator(QRegExp("(?:\\[\d+.+,\s*)+\d+.+\\]{1}"), self.intial_param_guess_edit)) self.intial_param_guess_edit.textChanged.connect(self.check_state) self.lower_param_bound_edit.setValidator( QRegExpValidator(QRegExp("(?:\\[\d+.+,\s*)+\d+.+\\]{1}"), self.lower_param_bound_edit)) self.lower_param_bound_edit.textChanged.connect(self.check_state) self.upper_param_bound_edit.setValidator( QRegExpValidator(QRegExp("(?:\\[\d+.+,\s*)+\d+.+\\]{1}"), self.upper_param_bound_edit)) self.upper_param_bound_edit.textChanged.connect(self.check_state)